Add CheckboxGroup story and initial implementation (#33051)
* Add CheckboxGroup story and initial implementation Signed-off-by: Deepthi Ajith <deepthi.ajith@infosys.com> * Add CheckboxGroup implementation and docs coverage Signed-off-by: Deepthi Ajith <deepthi.ajith@infosys.com> * update api-reports Signed-off-by: Deepthi Ajith <deepthi.ajith@infosys.com> * fix: add more story variations, docs page, and fix JSDoc comments Signed-off-by: Deepthi Ajith <deepthi.ajith@infosys.com> * fix: address review feedback for CheckboxGroup component Signed-off-by: Deepthi Ajith <deepthi.ajith@infosys.com> --------- Signed-off-by: Deepthi Ajith <deepthi.ajith@infosys.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Add an initial CheckboxGroup component implementation and docs coverage.
|
||||
@@ -0,0 +1,81 @@
|
||||
'use client';
|
||||
|
||||
import { CheckboxGroup } from '../../../../../packages/ui/src/components/CheckboxGroup/CheckboxGroup';
|
||||
import { Checkbox } from '../../../../../packages/ui/src/components/Checkbox/Checkbox';
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<CheckboxGroup
|
||||
label="Choose platforms for notifications"
|
||||
defaultValue={['github']}
|
||||
>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export const Horizontal = () => (
|
||||
<CheckboxGroup
|
||||
label="Choose platforms for notifications"
|
||||
defaultValue={['github']}
|
||||
orientation="horizontal"
|
||||
>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
);
|
||||
|
||||
export const Disabled = () => (
|
||||
<CheckboxGroup
|
||||
label="Choose platforms for notifications"
|
||||
defaultValue={['github']}
|
||||
isDisabled
|
||||
>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
);
|
||||
|
||||
export const DisabledSingle = () => (
|
||||
<CheckboxGroup
|
||||
label="Choose platforms for notifications"
|
||||
defaultValue={['github']}
|
||||
>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack" isDisabled>
|
||||
Slack
|
||||
</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
);
|
||||
|
||||
export const Validation = () => (
|
||||
<CheckboxGroup
|
||||
label="Choose platforms for notifications"
|
||||
defaultValue={['github', 'slack']}
|
||||
validationBehavior="aria"
|
||||
validate={(value: string[]) =>
|
||||
value.includes('slack') ? 'Slack is not available in your region.' : null
|
||||
}
|
||||
>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
);
|
||||
|
||||
export const ReadOnly = () => (
|
||||
<CheckboxGroup
|
||||
label="Choose platforms for notifications"
|
||||
defaultValue={['github']}
|
||||
isReadOnly
|
||||
>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
);
|
||||
@@ -0,0 +1,110 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { ReactAriaLink } from '@/components/ReactAriaLink';
|
||||
import { checkboxGroupPropDefs } from './props-definition';
|
||||
import {
|
||||
checkboxGroupUsageSnippet,
|
||||
defaultSnippet,
|
||||
horizontalSnippet,
|
||||
disabledSnippet,
|
||||
disabledSingleSnippet,
|
||||
validationSnippet,
|
||||
readOnlySnippet,
|
||||
} from './snippets';
|
||||
import {
|
||||
Default,
|
||||
Horizontal,
|
||||
Disabled,
|
||||
DisabledSingle,
|
||||
Validation,
|
||||
ReadOnly,
|
||||
} from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { CheckboxGroupDefinition } from '../../../utils/definitions';
|
||||
|
||||
export const reactAriaUrls = {
|
||||
checkboxGroup: 'https://react-aria.adobe.com/CheckboxGroup',
|
||||
};
|
||||
|
||||
<PageTitle
|
||||
title="CheckboxGroup"
|
||||
description="A group of checkboxes for selecting multiple options from a list."
|
||||
/>
|
||||
|
||||
<Snippet align="center" py={4} preview={<Default />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeBlock code={checkboxGroupUsageSnippet} />
|
||||
|
||||
## API reference
|
||||
|
||||
### CheckboxGroup
|
||||
|
||||
<PropsTable data={checkboxGroupPropDefs} />
|
||||
|
||||
<ReactAriaLink component="CheckboxGroup" href={reactAriaUrls.checkboxGroup} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Horizontal
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
layout="side-by-side"
|
||||
preview={<Horizontal />}
|
||||
code={horizontalSnippet}
|
||||
/>
|
||||
|
||||
### Disabled
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
layout="side-by-side"
|
||||
preview={<Disabled />}
|
||||
code={disabledSnippet}
|
||||
/>
|
||||
|
||||
### Disabled single checkbox
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
layout="side-by-side"
|
||||
preview={<DisabledSingle />}
|
||||
code={disabledSingleSnippet}
|
||||
/>
|
||||
|
||||
### Validation
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
layout="side-by-side"
|
||||
preview={<Validation />}
|
||||
code={validationSnippet}
|
||||
/>
|
||||
|
||||
### Read only
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
layout="side-by-side"
|
||||
preview={<ReadOnly />}
|
||||
code={readOnlySnippet}
|
||||
/>
|
||||
|
||||
<Theming definition={CheckboxGroupDefinition} />
|
||||
|
||||
<ChangelogComponent component="checkbox-group" />
|
||||
@@ -0,0 +1,82 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
childrenPropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
import { Chip } from '@/components/Chip';
|
||||
|
||||
export const checkboxGroupPropDefs: Record<string, PropDef> = {
|
||||
label: {
|
||||
type: 'string',
|
||||
description: 'The visible label for the checkbox group.',
|
||||
},
|
||||
'aria-label': {
|
||||
type: 'string',
|
||||
description:
|
||||
'Accessible label when a visible label is not provided. Either label, aria-label, or aria-labelledby is required.',
|
||||
},
|
||||
'aria-labelledby': {
|
||||
type: 'string',
|
||||
description:
|
||||
'ID of an element that labels the checkbox group. Either label, aria-label, or aria-labelledby is required.',
|
||||
},
|
||||
secondaryLabel: {
|
||||
type: 'string',
|
||||
description: (
|
||||
<>
|
||||
Secondary label text. Defaults to <Chip>Required</Chip> when isRequired
|
||||
is true.
|
||||
</>
|
||||
),
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: 'Helper text displayed below the label.',
|
||||
},
|
||||
orientation: {
|
||||
type: 'enum',
|
||||
values: ['horizontal', 'vertical'],
|
||||
default: 'vertical',
|
||||
description: 'The axis the checkboxes should align with.',
|
||||
},
|
||||
value: {
|
||||
type: 'enum',
|
||||
values: ['string[]'],
|
||||
description: 'The selected values (controlled).',
|
||||
},
|
||||
defaultValue: {
|
||||
type: 'enum',
|
||||
values: ['string[]'],
|
||||
description: 'The initial selected values (uncontrolled).',
|
||||
},
|
||||
onChange: {
|
||||
type: 'enum',
|
||||
values: ['(value: string[]) => void'],
|
||||
description: 'Handler called when the selected values change.',
|
||||
},
|
||||
isDisabled: {
|
||||
type: 'boolean',
|
||||
description: 'Whether all checkboxes in the group are disabled.',
|
||||
},
|
||||
isReadOnly: {
|
||||
type: 'boolean',
|
||||
description: 'Whether all checkboxes in the group are read-only.',
|
||||
},
|
||||
isRequired: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Whether at least one selection is required for form submission.',
|
||||
},
|
||||
isInvalid: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the checkbox group is in an invalid state.',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'The name used for form submission.',
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
export const checkboxGroupUsageSnippet = `import { CheckboxGroup, Checkbox } from '@backstage/ui';
|
||||
|
||||
<CheckboxGroup label="Choose platforms for notifications" defaultValue={['github']}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>`;
|
||||
|
||||
export const defaultSnippet = `<CheckboxGroup label="Choose platforms for notifications" defaultValue={['github']}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>`;
|
||||
|
||||
export const horizontalSnippet = `<CheckboxGroup
|
||||
label="Choose platforms for notifications"
|
||||
defaultValue={['github']}
|
||||
orientation="horizontal"
|
||||
>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>`;
|
||||
|
||||
export const disabledSnippet = `<CheckboxGroup
|
||||
label="Choose platforms for notifications"
|
||||
defaultValue={['github']}
|
||||
isDisabled
|
||||
>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>`;
|
||||
|
||||
export const disabledSingleSnippet = `<CheckboxGroup
|
||||
label="Choose platforms for notifications"
|
||||
defaultValue={['github']}
|
||||
>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack" isDisabled>Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>`;
|
||||
|
||||
export const validationSnippet = `<CheckboxGroup
|
||||
label="Choose platforms for notifications"
|
||||
defaultValue={['github', 'slack']}
|
||||
validationBehavior="aria"
|
||||
validate={value =>
|
||||
value.includes('slack') ? 'Slack is not available in your region.' : null
|
||||
}
|
||||
>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>`;
|
||||
|
||||
export const readOnlySnippet = `<CheckboxGroup
|
||||
label="Choose platforms for notifications"
|
||||
defaultValue={['github']}
|
||||
isReadOnly
|
||||
>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>`;
|
||||
@@ -45,6 +45,10 @@ export const components: Page[] = [
|
||||
title: 'Checkbox',
|
||||
slug: 'checkbox',
|
||||
},
|
||||
{
|
||||
title: 'CheckboxGroup',
|
||||
slug: 'checkbox-group',
|
||||
},
|
||||
{
|
||||
title: 'Container',
|
||||
slug: 'container',
|
||||
|
||||
@@ -17,6 +17,7 @@ export type Component =
|
||||
| 'cell-profile'
|
||||
| 'cell-text'
|
||||
| 'checkbox'
|
||||
| 'checkbox-group'
|
||||
| 'collapsible'
|
||||
| 'column'
|
||||
| 'container'
|
||||
|
||||
+44
-15
@@ -5,6 +5,7 @@
|
||||
```ts
|
||||
import type { ButtonProps as ButtonProps_2 } from 'react-aria-components';
|
||||
import { CellProps as CellProps_2 } from 'react-aria-components';
|
||||
import type { CheckboxGroupProps as CheckboxGroupProps_2 } from 'react-aria-components';
|
||||
import type { CheckboxProps as CheckboxProps_2 } from 'react-aria-components';
|
||||
import { ColumnProps as ColumnProps_2 } from 'react-aria-components';
|
||||
import type { ColumnSize } from '@react-types/table';
|
||||
@@ -374,7 +375,7 @@ export interface BgProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export type Border = 'none' | 'base' | 'error' | 'warning' | 'selected';
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -879,6 +880,47 @@ export const CheckboxDefinition: {
|
||||
};
|
||||
};
|
||||
|
||||
// @public
|
||||
export const CheckboxGroup: ForwardRefExoticComponent<
|
||||
CheckboxGroupProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
|
||||
// @public
|
||||
export const CheckboxGroupDefinition: {
|
||||
readonly styles: {
|
||||
readonly [key: string]: string;
|
||||
};
|
||||
readonly classNames: {
|
||||
readonly root: 'bui-CheckboxGroup';
|
||||
readonly content: 'bui-CheckboxGroupContent';
|
||||
};
|
||||
readonly propDefs: {
|
||||
readonly className: {};
|
||||
readonly children: {};
|
||||
readonly label: {};
|
||||
readonly secondaryLabel: {};
|
||||
readonly description: {};
|
||||
readonly isRequired: {};
|
||||
readonly orientation: {};
|
||||
};
|
||||
};
|
||||
|
||||
// @public
|
||||
export type CheckboxGroupOwnProps = {
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
label?: FieldLabelProps['label'];
|
||||
secondaryLabel?: FieldLabelProps['secondaryLabel'];
|
||||
description?: FieldLabelProps['description'];
|
||||
isRequired?: CheckboxGroupProps_2['isRequired'];
|
||||
orientation?: 'horizontal' | 'vertical';
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface CheckboxGroupProps
|
||||
extends Omit<CheckboxGroupProps_2, keyof CheckboxGroupOwnProps>,
|
||||
CheckboxGroupOwnProps {}
|
||||
|
||||
// @public (undocumented)
|
||||
export type CheckboxOwnProps = {
|
||||
children?: React.ReactNode;
|
||||
@@ -3284,33 +3326,20 @@ export interface UseTableResult<T extends TableItem, TFilter = unknown> {
|
||||
>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export interface UtilityProps extends SpaceProps {
|
||||
// (undocumented)
|
||||
alignItems?: Responsive<AlignItems>;
|
||||
// (undocumented)
|
||||
border?: Responsive<Border>;
|
||||
// (undocumented)
|
||||
borderRadius?: Responsive<BorderRadius>;
|
||||
// (undocumented)
|
||||
colEnd?: Responsive<Columns | 'auto'>;
|
||||
// (undocumented)
|
||||
colSpan?: Responsive<Columns | 'full'>;
|
||||
// (undocumented)
|
||||
colStart?: Responsive<Columns | 'auto'>;
|
||||
// (undocumented)
|
||||
columns?: Responsive<Columns>;
|
||||
// (undocumented)
|
||||
display?: Responsive<Display>;
|
||||
// (undocumented)
|
||||
flexDirection?: Responsive<FlexDirection>;
|
||||
// (undocumented)
|
||||
flexWrap?: Responsive<FlexWrap>;
|
||||
// (undocumented)
|
||||
gap?: Responsive<Space>;
|
||||
// (undocumented)
|
||||
justifyContent?: Responsive<JustifyContent>;
|
||||
// (undocumented)
|
||||
rowSpan?: Responsive<Columns | 'full'>;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
@layer tokens, base, components, utilities;
|
||||
|
||||
@layer components {
|
||||
.bui-CheckboxGroup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.bui-CheckboxGroup[data-orientation='horizontal'] .bui-CheckboxGroupContent {
|
||||
flex-direction: row;
|
||||
gap: var(--bui-space-4);
|
||||
}
|
||||
|
||||
.bui-CheckboxGroupContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--bui-space-2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import preview from '../../../../../.storybook/preview';
|
||||
import { CheckboxGroup } from './CheckboxGroup';
|
||||
import { Checkbox } from '../Checkbox/Checkbox';
|
||||
import { Text } from '../Text';
|
||||
|
||||
const meta = preview.meta({
|
||||
title: 'Backstage UI/CheckboxGroup',
|
||||
component: CheckboxGroup,
|
||||
});
|
||||
|
||||
export const Default = meta.story({
|
||||
args: {
|
||||
label: 'Choose platforms for notifications',
|
||||
defaultValue: ['github'],
|
||||
},
|
||||
render: args => (
|
||||
<CheckboxGroup {...args}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const Controlled = meta.story({
|
||||
args: {
|
||||
label: 'Choose platforms for notifications',
|
||||
},
|
||||
render: args => {
|
||||
const [values, setValues] = useState<string[]>(['email']);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CheckboxGroup {...args} value={values} onChange={setValues}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
<Text>Selected: {values.join(', ') || 'none'}</Text>
|
||||
</>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const Horizontal = meta.story({
|
||||
args: {
|
||||
...Default.input.args,
|
||||
orientation: 'horizontal',
|
||||
},
|
||||
render: args => (
|
||||
<CheckboxGroup {...args}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const Disabled = meta.story({
|
||||
args: {
|
||||
...Default.input.args,
|
||||
isDisabled: true,
|
||||
},
|
||||
render: args => (
|
||||
<CheckboxGroup {...args}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const DisabledSingle = meta.story({
|
||||
args: {
|
||||
...Default.input.args,
|
||||
},
|
||||
render: args => (
|
||||
<CheckboxGroup {...args}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack" isDisabled>
|
||||
Slack
|
||||
</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const DisabledAndSelected = meta.story({
|
||||
args: {
|
||||
...Default.input.args,
|
||||
defaultValue: ['slack'],
|
||||
},
|
||||
render: args => (
|
||||
<CheckboxGroup {...args}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack" isDisabled>
|
||||
Slack
|
||||
</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const Invalid = meta.story({
|
||||
args: {
|
||||
...Default.input.args,
|
||||
isInvalid: true,
|
||||
},
|
||||
render: args => (
|
||||
<CheckboxGroup {...args}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const ReadOnly = meta.story({
|
||||
args: {
|
||||
...Default.input.args,
|
||||
isReadOnly: true,
|
||||
defaultValue: ['github'],
|
||||
},
|
||||
render: args => (
|
||||
<CheckboxGroup {...args}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const WithDescription = meta.story({
|
||||
args: {
|
||||
...Default.input.args,
|
||||
description: 'Select all channels where you want to receive notifications.',
|
||||
},
|
||||
render: args => (
|
||||
<CheckboxGroup {...args}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const Required = meta.story({
|
||||
args: {
|
||||
...Default.input.args,
|
||||
isRequired: true,
|
||||
},
|
||||
render: args => (
|
||||
<CheckboxGroup {...args}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
),
|
||||
});
|
||||
|
||||
export const Validation = meta.story({
|
||||
args: {
|
||||
...Default.input.args,
|
||||
defaultValue: ['github', 'slack'],
|
||||
validationBehavior: 'aria',
|
||||
validate: (value: string[]) =>
|
||||
value.includes('slack') ? 'Slack is not available in your region.' : null,
|
||||
},
|
||||
render: args => (
|
||||
<CheckboxGroup {...args}>
|
||||
<Checkbox value="github">GitHub</Checkbox>
|
||||
<Checkbox value="slack">Slack</Checkbox>
|
||||
<Checkbox value="email">Email</Checkbox>
|
||||
</CheckboxGroup>
|
||||
),
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef, useEffect } from 'react';
|
||||
import { CheckboxGroup as RACheckboxGroup } from 'react-aria-components';
|
||||
import type { CheckboxGroupProps } from './types';
|
||||
import { useDefinition } from '../../hooks/useDefinition';
|
||||
import { CheckboxGroupDefinition } from './definition';
|
||||
import { FieldLabel } from '../FieldLabel';
|
||||
import { FieldError } from '../FieldError';
|
||||
|
||||
/**
|
||||
* A group of checkboxes for selecting multiple options from a list.
|
||||
* @public
|
||||
*/
|
||||
export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
||||
(props, ref) => {
|
||||
const { ownProps, restProps } = useDefinition(
|
||||
CheckboxGroupDefinition,
|
||||
props,
|
||||
);
|
||||
const {
|
||||
classes,
|
||||
label,
|
||||
secondaryLabel,
|
||||
description,
|
||||
isRequired,
|
||||
orientation,
|
||||
children,
|
||||
} = ownProps;
|
||||
|
||||
const ariaLabel = restProps['aria-label'];
|
||||
const ariaLabelledBy = restProps['aria-labelledby'];
|
||||
|
||||
useEffect(() => {
|
||||
if (!label && !ariaLabel && !ariaLabelledBy) {
|
||||
console.warn(
|
||||
'CheckboxGroup requires either a visible label, aria-label, or aria-labelledby for accessibility',
|
||||
);
|
||||
}
|
||||
}, [label, ariaLabel, ariaLabelledBy]);
|
||||
|
||||
const secondaryLabelText =
|
||||
secondaryLabel || (isRequired ? 'Required' : null);
|
||||
|
||||
return (
|
||||
<RACheckboxGroup
|
||||
ref={ref}
|
||||
className={classes.root}
|
||||
isRequired={isRequired}
|
||||
data-orientation={orientation}
|
||||
{...restProps}
|
||||
>
|
||||
<FieldLabel
|
||||
label={label}
|
||||
secondaryLabel={secondaryLabelText}
|
||||
description={description}
|
||||
/>
|
||||
<div className={classes.content}>{children}</div>
|
||||
<FieldError />
|
||||
</RACheckboxGroup>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
CheckboxGroup.displayName = 'CheckboxGroup';
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineComponent } from '../../hooks/useDefinition';
|
||||
import type { CheckboxGroupOwnProps } from './types';
|
||||
import styles from './CheckboxGroup.module.css';
|
||||
|
||||
/**
|
||||
* Component definition for CheckboxGroup
|
||||
* @public
|
||||
*/
|
||||
export const CheckboxGroupDefinition = defineComponent<CheckboxGroupOwnProps>()(
|
||||
{
|
||||
styles,
|
||||
classNames: {
|
||||
root: 'bui-CheckboxGroup',
|
||||
content: 'bui-CheckboxGroupContent',
|
||||
},
|
||||
propDefs: {
|
||||
className: {},
|
||||
children: {},
|
||||
label: {},
|
||||
secondaryLabel: {},
|
||||
description: {},
|
||||
isRequired: {},
|
||||
orientation: {},
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { CheckboxGroup } from './CheckboxGroup';
|
||||
export { CheckboxGroupDefinition } from './definition';
|
||||
export type { CheckboxGroupOwnProps, CheckboxGroupProps } from './types';
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { CheckboxGroupProps as RACheckboxGroupProps } from 'react-aria-components';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { FieldLabelProps } from '../FieldLabel/types';
|
||||
|
||||
/**
|
||||
* Own props for the CheckboxGroup component.
|
||||
* @public
|
||||
*/
|
||||
export type CheckboxGroupOwnProps = {
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
label?: FieldLabelProps['label'];
|
||||
secondaryLabel?: FieldLabelProps['secondaryLabel'];
|
||||
description?: FieldLabelProps['description'];
|
||||
isRequired?: RACheckboxGroupProps['isRequired'];
|
||||
orientation?: 'horizontal' | 'vertical';
|
||||
};
|
||||
|
||||
/**
|
||||
* Props for the CheckboxGroup component.
|
||||
* @public
|
||||
*/
|
||||
export interface CheckboxGroupProps
|
||||
extends Omit<RACheckboxGroupProps, keyof CheckboxGroupOwnProps>,
|
||||
CheckboxGroupOwnProps {}
|
||||
@@ -34,6 +34,7 @@ export { ButtonIconDefinition } from './components/ButtonIcon/definition';
|
||||
export { ButtonLinkDefinition } from './components/ButtonLink/definition';
|
||||
export { CardDefinition } from './components/Card/definition';
|
||||
export { CheckboxDefinition } from './components/Checkbox/definition';
|
||||
export { CheckboxGroupDefinition } from './components/CheckboxGroup/definition';
|
||||
export { ContainerDefinition } from './components/Container/definition';
|
||||
export { DialogDefinition } from './components/Dialog/definition';
|
||||
export { FieldErrorDefinition } from './components/FieldError/definition';
|
||||
|
||||
@@ -41,6 +41,7 @@ export * from './components/Header';
|
||||
export * from './components/ButtonIcon';
|
||||
export * from './components/ButtonLink';
|
||||
export * from './components/Checkbox';
|
||||
export * from './components/CheckboxGroup';
|
||||
export * from './components/RadioGroup';
|
||||
export * from './components/Slider';
|
||||
export * from './components/Table';
|
||||
|
||||
@@ -72,7 +72,11 @@ export type BorderRadius =
|
||||
| 'xl'
|
||||
| '2xl';
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* Border variants available for UI utility props.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type Border = 'none' | 'base' | 'error' | 'warning' | 'selected';
|
||||
|
||||
/** @public */
|
||||
@@ -136,20 +140,37 @@ export type TextColorStatus = 'danger' | 'warning' | 'success' | 'info';
|
||||
/** @public */
|
||||
export type TextWeights = 'regular' | 'bold';
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* Shared utility props supported by layout-oriented UI components.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface UtilityProps extends SpaceProps {
|
||||
/** Aligns children on the cross axis in flex and grid layouts. */
|
||||
alignItems?: Responsive<AlignItems>;
|
||||
/** Applies a semantic border variant. */
|
||||
border?: Responsive<Border>;
|
||||
/** Applies a semantic border radius token. */
|
||||
borderRadius?: Responsive<BorderRadius>;
|
||||
/** Sets the ending grid column line. */
|
||||
colEnd?: Responsive<Columns | 'auto'>;
|
||||
/** Sets the number of grid columns to span. */
|
||||
colSpan?: Responsive<Columns | 'full'>;
|
||||
/** Sets the starting grid column line. */
|
||||
colStart?: Responsive<Columns | 'auto'>;
|
||||
/** Sets the number of columns for grid containers. */
|
||||
columns?: Responsive<Columns>;
|
||||
/** Controls the CSS display value. */
|
||||
display?: Responsive<Display>;
|
||||
/** Controls the direction of flex items. */
|
||||
flexDirection?: Responsive<FlexDirection>;
|
||||
/** Controls how flex items wrap. */
|
||||
flexWrap?: Responsive<FlexWrap>;
|
||||
/** Sets spacing between children in flex and grid layouts. */
|
||||
gap?: Responsive<Space>;
|
||||
/** Aligns children on the main axis in flex and grid layouts. */
|
||||
justifyContent?: Responsive<JustifyContent>;
|
||||
/** Sets the number of grid rows to span. */
|
||||
rowSpan?: Responsive<Columns | 'full'>;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user