diff --git a/.changeset/brown-grapes-fry.md b/.changeset/brown-grapes-fry.md new file mode 100644 index 0000000000..c2fd1fbe2a --- /dev/null +++ b/.changeset/brown-grapes-fry.md @@ -0,0 +1,7 @@ +--- +'@backstage/ui': patch +--- + +Added indeterminate state support to the Checkbox component for handling partial selection scenarios like table header checkboxes. + +Affected components: Checkbox diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index ca9d2bcff1..fa50111cde 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -460,6 +460,7 @@ export const CheckboxDefinition: { }; readonly dataAttributes: { readonly selected: readonly [true, false]; + readonly indeterminate: readonly [true, false]; }; }; diff --git a/packages/ui/src/components/Checkbox/Checkbox.module.css b/packages/ui/src/components/Checkbox/Checkbox.module.css index 23a88d2b80..a4647c935a 100644 --- a/packages/ui/src/components/Checkbox/Checkbox.module.css +++ b/packages/ui/src/components/Checkbox/Checkbox.module.css @@ -62,7 +62,14 @@ color: var(--bui-fg-solid); } - .bui-Checkbox[data-hovered]:not([data-selected]) & { + .bui-Checkbox[data-indeterminate] & { + background-color: var(--bui-bg-surface-1); + box-shadow: inset 0 0 0 1px var(--bui-border); + color: var(--bui-fg-primary); + } + + .bui-Checkbox[data-hovered]:not([data-selected]):not([data-indeterminate]) + & { box-shadow: inset 0 0 0 1px var(--bui-border-hover); } diff --git a/packages/ui/src/components/Checkbox/Checkbox.stories.tsx b/packages/ui/src/components/Checkbox/Checkbox.stories.tsx index c5318cc6fb..e8f7859785 100644 --- a/packages/ui/src/components/Checkbox/Checkbox.stories.tsx +++ b/packages/ui/src/components/Checkbox/Checkbox.stories.tsx @@ -28,16 +28,27 @@ export const Default = meta.story({ }, }); +export const Indeterminate = meta.story({ + args: { + children: 'Select all', + isIndeterminate: true, + }, +}); + export const AllVariants = meta.story({ ...Default.input, render: () => ( Unchecked Checked + Indeterminate Disabled Checked & Disabled + + Indeterminate & Disabled + ), }); diff --git a/packages/ui/src/components/Checkbox/Checkbox.tsx b/packages/ui/src/components/Checkbox/Checkbox.tsx index 97a603daf2..6030fbc06b 100644 --- a/packages/ui/src/components/Checkbox/Checkbox.tsx +++ b/packages/ui/src/components/Checkbox/Checkbox.tsx @@ -21,7 +21,7 @@ import { useStyles } from '../../hooks/useStyles'; import { CheckboxDefinition } from './definition'; import clsx from 'clsx'; import styles from './Checkbox.module.css'; -import { RiCheckLine } from '@remixicon/react'; +import { RiCheckLine, RiSubtractLine } from '@remixicon/react'; /** @public */ export const Checkbox = forwardRef( @@ -35,12 +35,23 @@ export const Checkbox = forwardRef( className={clsx(classNames.root, styles[classNames.root], className)} {...rest} > -
- -
- {children} + {({ isIndeterminate }) => ( + <> +
+ {isIndeterminate ? ( + + ) : ( + + )} +
+ {children} + + )} ); }, diff --git a/packages/ui/src/components/Checkbox/definition.ts b/packages/ui/src/components/Checkbox/definition.ts index 022f2a0af1..c7b5b16276 100644 --- a/packages/ui/src/components/Checkbox/definition.ts +++ b/packages/ui/src/components/Checkbox/definition.ts @@ -27,5 +27,6 @@ export const CheckboxDefinition = { }, dataAttributes: { selected: [true, false] as const, + indeterminate: [true, false] as const, }, } as const satisfies ComponentDefinition;