fleet/frontend/components/forms/fields/Checkbox/Checkbox.tsx

187 lines
5 KiB
TypeScript
Raw Normal View History

import React, { ReactNode, KeyboardEvent, useEffect, useRef } from "react";
import classnames from "classnames";
import { noop, pick } from "lodash";
import FormField from "components/forms/FormField";
import { IFormFieldProps } from "components/forms/FormField/FormField";
import TooltipWrapper from "components/TooltipWrapper";
import Icon from "components/Icon";
const baseClass = "fleet-checkbox";
export interface ICheckboxProps {
children?: ReactNode;
className?: string;
/** readOnly displays a non-editable field */
readOnly?: boolean;
/** disabled displays a greyed out non-editable field */
disabled?: boolean;
name?: string;
onChange?: any; // TODO: meant to be an event; figure out type for this
onBlur?: (event: React.FocusEvent<HTMLDivElement>) => void;
value?: boolean | null;
wrapperClassName?: string;
indeterminate?: boolean;
parseTarget?: boolean;
/** to display over the checkbox label */
labelTooltipContent?: React.ReactNode;
/** to display over the checkbox icon */
iconTooltipContent?: React.ReactNode;
isLeftLabel?: boolean;
UI – refactor forms and form fields (#16159) ## Addresses #15325 - Define shared global styles for forms (`form` and `.form`s) and `.form-field`s - Sweep through the app, updating each form from being locally styled to first prioritizing the global styles and only defining local styles where needed for custom behavior - Remove lots of unnecessary nesting of elements - Other small bug fixes and improvements ### Samples, before (L) | after (R): **Save query modal** ![Screenshot 2024-01-17 at 11 49 14 AM](https://github.com/fleetdm/fleet/assets/61553566/14f209fb-31db-41ef-8e63-e0d8994698c1) **Edit query form** ![Screenshot 2024-01-17 at 11 50 35 AM](https://github.com/fleetdm/fleet/assets/61553566/b07e70ea-3095-4e4f-be73-95a3c499839b) **Add hosts modal** ![Screenshot 2024-01-17 at 11 51 48 AM](https://github.com/fleetdm/fleet/assets/61553566/4ef1f410-a823-41d1-b2f6-ea8eb5231f93) ## QA Plan: @xpkoala here's the same list from the issue, freshly de-checked for you to use if it's helpful: * Please check error states of each field #### Specified by issue: ##### In "Save query" modal: - [ ] Reduce space between checkboxes and their help text to 8px/0.5rem for the following fields: - [ ] Observers can run - [ ] Discard data - [ ] Update the following checkbox labels to have normal font weight (not bold): - [ ] Discard data ##### On "Edit query" page: - [ ] Update the following checkbox labels to have normal font weight (not bold): - [ ] Observers can run - [ ] Discard data ##### In "Add hosts" modal, for copy text fields: - [ ] match typical form form field styles - [ ] Adjust the positioning of the copy icon to keep it from being too far down ##### Further locations to check - [ ] ChangeEmailForm.jsx - [ ] ChangePasswordForm.jsx - [ ] ConfirmInviteForm.jsx - [ ] ConfirmSSOInviteForm.jsx - [ ] EnrollSecretModal.tsx - [ ] ForgotPasswordForm.jsx - [ ] LoginForm.tsx - [ ] EditPackForm.tsx - [ ] (New)PackForm.tsx - [ ] AdminDetails.jsx - [ ] ConfirmationPage.tsx - [ ] FleetDetails.jsx - [ ] OrgDetails.jsx - [ ] ResetPasswordForm.tsx - [ ] UserSettingsForm.jsx - [ ] EditTeamModal.tsx - [ ] IdpSection.tsx - [ ] DeleteIntegrationModal.tsx - [ ] IntegrationForm.tsx - [ ] EndUserMigrationSection.tsx - [ ] RequestCSRModal.tsx - [ ] Advanced.tsx - [ ] Agents.tsx - [ ] FleetDesktop.tsx - [ ] HostStatusWebhook.tsx front - [ ] Info.tsx - [ ] Smtp.tsx - [ ] Sso.tsx - [ ] Statistics.tsx - [ ] WebAddress.tsx - [ ] CreateTeamModal.tsx - [ ] DeleteTeamModal.tsx - [ ] EditTeamModal.tsx - [ ] AgentOptionsPage.tsx - updated the layout of this page to align with the rest of the forms in the UI – can easily revert if it's not what we want - [ ] AddMemberModal.tsx - [ ] RemoveMemberModal.tsx - [ ] UserForm.tsx - Used by both `EditUserModal` and `CreateUserModal` - A few different conditions that cause different rendering behavior - [ ] DeleteHostModal.tsx - [ ] TransferHostModal.tsx - [ ] LabelForm.tsx - [ ] MacOSTargetForm.tsx - [ ] WindowsTargetForm.tsx - [ ] BootstrapPackageListltem.ts - [ ] EndUserAuthForm.tsx - [ ] PackQueryEditorModal.tsx - [ ] PolicyForm.tsx - [ ] SaveNewPolicyModal.tsx - [ ] ConfirmSaveChangesModal.tsx - [ ] Query automations modal - [ ] Policy automations modal - addresses #16010 - [ ] SoftwareAutomationsModal ## Checklist for submitter - [x] Changes file added for user-visible changes in `changes/` - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling <jacob@fleetdm.com> Co-authored-by: RachelElysia <71795832+RachelElysia@users.noreply.github.com>
2024-01-18 15:48:44 +00:00
helpText?: React.ReactNode;
/** Use in table action only
* Do not use on forms as enter key reserved for submit */
enableEnterToCheck?: boolean;
}
const Checkbox = (props: ICheckboxProps) => {
const {
children,
className,
readOnly = false,
disabled = false,
name,
onChange = noop,
onBlur = noop,
value = false,
wrapperClassName,
indeterminate = false,
parseTarget,
labelTooltipContent,
iconTooltipContent,
isLeftLabel,
helpText,
enableEnterToCheck = false,
} = props;
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.indeterminate = indeterminate;
}
}, [indeterminate]);
const handleChange = (
event: React.MouseEvent | React.KeyboardEvent
): void => {
event.preventDefault();
if (readOnly || disabled) return;
// If indeterminate, set to true; otherwise, toggle the current value
const newValue = indeterminate || !value;
if (parseTarget) {
onChange({ name, value: newValue });
} else {
onChange(newValue);
}
// Update the hidden input
if (inputRef.current) {
inputRef.current.checked = newValue;
}
};
/** Manual implementation of spacebar toggling checkboxes (default behavior)
* since we're using a custom div instead of a native checkbox
* Enter key intended to toggle table checkboxes only */
const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {
if (event.key === " " || (enableEnterToCheck && event.key === "Enter")) {
handleChange(event);
}
};
const checkBoxClass = classnames(
{ inverse: isLeftLabel },
className,
baseClass
);
const checkBoxLabelClass = classnames(checkBoxClass, {
[`${baseClass}__label--read-only`]: readOnly || disabled,
[`${baseClass}__label--disabled`]: disabled,
});
const formFieldProps = {
UI – refactor forms and form fields (#16159) ## Addresses #15325 - Define shared global styles for forms (`form` and `.form`s) and `.form-field`s - Sweep through the app, updating each form from being locally styled to first prioritizing the global styles and only defining local styles where needed for custom behavior - Remove lots of unnecessary nesting of elements - Other small bug fixes and improvements ### Samples, before (L) | after (R): **Save query modal** ![Screenshot 2024-01-17 at 11 49 14 AM](https://github.com/fleetdm/fleet/assets/61553566/14f209fb-31db-41ef-8e63-e0d8994698c1) **Edit query form** ![Screenshot 2024-01-17 at 11 50 35 AM](https://github.com/fleetdm/fleet/assets/61553566/b07e70ea-3095-4e4f-be73-95a3c499839b) **Add hosts modal** ![Screenshot 2024-01-17 at 11 51 48 AM](https://github.com/fleetdm/fleet/assets/61553566/4ef1f410-a823-41d1-b2f6-ea8eb5231f93) ## QA Plan: @xpkoala here's the same list from the issue, freshly de-checked for you to use if it's helpful: * Please check error states of each field #### Specified by issue: ##### In "Save query" modal: - [ ] Reduce space between checkboxes and their help text to 8px/0.5rem for the following fields: - [ ] Observers can run - [ ] Discard data - [ ] Update the following checkbox labels to have normal font weight (not bold): - [ ] Discard data ##### On "Edit query" page: - [ ] Update the following checkbox labels to have normal font weight (not bold): - [ ] Observers can run - [ ] Discard data ##### In "Add hosts" modal, for copy text fields: - [ ] match typical form form field styles - [ ] Adjust the positioning of the copy icon to keep it from being too far down ##### Further locations to check - [ ] ChangeEmailForm.jsx - [ ] ChangePasswordForm.jsx - [ ] ConfirmInviteForm.jsx - [ ] ConfirmSSOInviteForm.jsx - [ ] EnrollSecretModal.tsx - [ ] ForgotPasswordForm.jsx - [ ] LoginForm.tsx - [ ] EditPackForm.tsx - [ ] (New)PackForm.tsx - [ ] AdminDetails.jsx - [ ] ConfirmationPage.tsx - [ ] FleetDetails.jsx - [ ] OrgDetails.jsx - [ ] ResetPasswordForm.tsx - [ ] UserSettingsForm.jsx - [ ] EditTeamModal.tsx - [ ] IdpSection.tsx - [ ] DeleteIntegrationModal.tsx - [ ] IntegrationForm.tsx - [ ] EndUserMigrationSection.tsx - [ ] RequestCSRModal.tsx - [ ] Advanced.tsx - [ ] Agents.tsx - [ ] FleetDesktop.tsx - [ ] HostStatusWebhook.tsx front - [ ] Info.tsx - [ ] Smtp.tsx - [ ] Sso.tsx - [ ] Statistics.tsx - [ ] WebAddress.tsx - [ ] CreateTeamModal.tsx - [ ] DeleteTeamModal.tsx - [ ] EditTeamModal.tsx - [ ] AgentOptionsPage.tsx - updated the layout of this page to align with the rest of the forms in the UI – can easily revert if it's not what we want - [ ] AddMemberModal.tsx - [ ] RemoveMemberModal.tsx - [ ] UserForm.tsx - Used by both `EditUserModal` and `CreateUserModal` - A few different conditions that cause different rendering behavior - [ ] DeleteHostModal.tsx - [ ] TransferHostModal.tsx - [ ] LabelForm.tsx - [ ] MacOSTargetForm.tsx - [ ] WindowsTargetForm.tsx - [ ] BootstrapPackageListltem.ts - [ ] EndUserAuthForm.tsx - [ ] PackQueryEditorModal.tsx - [ ] PolicyForm.tsx - [ ] SaveNewPolicyModal.tsx - [ ] ConfirmSaveChangesModal.tsx - [ ] Query automations modal - [ ] Policy automations modal - addresses #16010 - [ ] SoftwareAutomationsModal ## Checklist for submitter - [x] Changes file added for user-visible changes in `changes/` - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling <jacob@fleetdm.com> Co-authored-by: RachelElysia <71795832+RachelElysia@users.noreply.github.com>
2024-01-18 15:48:44 +00:00
...pick(props, ["helpText", "label", "error", "name"]),
className: wrapperClassName,
type: "checkbox",
} as IFormFieldProps;
const getIconName = () => {
if (indeterminate) return "checkbox-indeterminate";
if (value) return "checkbox";
return "checkbox-unchecked";
};
const renderIcon = () => {
const icon = (
<Icon
name={getIconName()}
className={`${baseClass}__icon ${baseClass}__icon--${getIconName()}`}
/>
);
if (iconTooltipContent) {
return (
<TooltipWrapper
tipContent={iconTooltipContent}
clickable={false}
underline={false}
showArrow
position="right"
tipOffset={8}
>
{icon}
</TooltipWrapper>
);
}
return icon;
};
return (
<FormField {...formFieldProps}>
<label htmlFor={name}>
<input
type="checkbox"
ref={inputRef}
name={name}
Add pagination to manage automations (#26414) For #23243 # Checklist for submitter <!-- Note that API documentation changes are now addressed by the product design team. --> - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/Committing-Changes.md#changes-files) for more information. - [x] Added/updated automated tests - [ ] A detailed QA plan exists on the associated ticket (if it isn't there, work with the product group's QA engineer to add it) - [x] Manual QA for all new/changed functionality ## Details This PR updates the policy Manage Automations modals to support pagination. Previously, these modals received a list of policies from the main Manage Policies page, which is itself paginated, so that a user could only add automations to whatever policies were currently listed on the Manage Automations page. This PR does some refactoring via the creation of a new PaginatedList component which: * accepts a `fetchPage` property it can call to get a page of data, * renders the data in a list with checkboxes and optional custom markup (e.g. dropdowns) * keeps track of changed ("dirty") items in the list, even across page changes * allows parent components to access the list of dirty items via a React `ref` For this specific use case, there's also a new `PoliciesPaginatedList` which implements the `fetchPage` for getting a page of policies, and adds Save and Cancel buttons. Each of the updated modals uses `PoliciesPaginatedList` to replace its current code for rendering policies in a list, and delegates much of the logic around change tracking to the new components.
2025-02-27 18:43:38 +00:00
checked={!!value}
onChange={noop} // Empty onChange to avoid React warning
disabled={disabled || readOnly}
style={{ display: "none" }} // Hide the input
id={name}
/>
<div
role="checkbox"
aria-label={name}
aria-checked={indeterminate ? "mixed" : value || undefined}
aria-readonly={readOnly}
aria-disabled={disabled}
tabIndex={disabled ? -1 : 0}
className={checkBoxLabelClass}
onClick={handleChange}
onKeyDown={handleKeyDown}
onBlur={onBlur}
>
{renderIcon()}
{labelTooltipContent ? (
<span className={`${baseClass}__label-tooltip tooltip`}>
<TooltipWrapper
tipContent={labelTooltipContent}
clickable={false} // Not block form behind tooltip
>
{children}
</TooltipWrapper>
</span>
) : (
<span className={`${baseClass}__label`}>{children}</span>
)}
</div>
</label>
</FormField>
);
};
export default Checkbox;