@boostdev/design-system-components 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +25 -4
- package/dist/client.cjs +58 -54
- package/dist/client.css +503 -525
- package/dist/client.js +58 -54
- package/dist/index.cjs +58 -54
- package/dist/index.css +503 -525
- package/dist/index.js +58 -54
- package/dist/web-components/{chunk-6MH5UWUD.js → chunk-2FGATTGT.js} +5 -0
- package/dist/web-components/{chunk-5IPHEONG.js → chunk-6I2DBFQ7.js} +1 -0
- package/dist/web-components/{chunk-DI46Q2EA.js → chunk-OCODKRVZ.js} +57 -82
- package/dist/web-components/index.d.ts +571 -1
- package/dist/web-components/index.js +1501 -4
- package/dist/web-components/interaction/bds-collapsible.js +1 -1
- package/dist/web-components/interaction/bds-drawer.js +1 -1
- package/dist/web-components/interaction/form/bds-segmented-control.js +1 -1
- package/package.json +1 -1
- package/src/components/interaction/Drawer/Drawer.module.css +37 -62
- package/src/components/interaction/Drawer/Drawer.tsx +21 -17
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +8 -4
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +13 -6
- package/src/stories/Introduction.mdx +2 -1
- package/src/web-components/index.ts +12 -0
- package/src/web-components/interaction/BdsAccordion.mdx +80 -28
- package/src/web-components/interaction/BdsAccordion.stories.tsx +67 -58
- package/src/web-components/interaction/BdsCollapsible.mdx +93 -23
- package/src/web-components/interaction/BdsCollapsible.stories.tsx +29 -48
- package/src/web-components/interaction/BdsDialog.mdx +88 -27
- package/src/web-components/interaction/BdsDialog.stories.tsx +129 -47
- package/src/web-components/interaction/BdsDrawer.mdx +85 -27
- package/src/web-components/interaction/BdsDrawer.stories.tsx +161 -31
- package/src/web-components/interaction/BdsDropdownMenu.mdx +108 -0
- package/src/web-components/interaction/BdsDropdownMenu.stories.tsx +91 -0
- package/src/web-components/interaction/BdsSkipLink.mdx +72 -16
- package/src/web-components/interaction/BdsSkipLink.stories.tsx +47 -34
- package/src/web-components/interaction/BdsTabs.mdx +97 -27
- package/src/web-components/interaction/BdsTabs.stories.tsx +69 -55
- package/src/web-components/interaction/BdsTooltip.mdx +84 -18
- package/src/web-components/interaction/BdsTooltip.stories.tsx +49 -30
- package/src/web-components/interaction/bds-collapsible.ts +1 -0
- package/src/web-components/interaction/bds-drawer.ts +59 -82
- package/src/web-components/interaction/bds-dropdown-menu-item.ts +124 -0
- package/src/web-components/interaction/bds-dropdown-menu.spec.ts +102 -0
- package/src/web-components/interaction/bds-dropdown-menu.ts +200 -0
- package/src/web-components/interaction/form/BdsCheckbox.mdx +57 -20
- package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +35 -36
- package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +70 -0
- package/src/web-components/interaction/form/BdsCheckboxGroup.stories.tsx +81 -0
- package/src/web-components/interaction/form/BdsCombobox.mdx +52 -33
- package/src/web-components/interaction/form/BdsCombobox.stories.tsx +55 -53
- package/src/web-components/interaction/form/BdsFileInput.mdx +59 -24
- package/src/web-components/interaction/form/BdsFileInput.stories.tsx +38 -53
- package/src/web-components/interaction/form/BdsFormInput.mdx +98 -0
- package/src/web-components/interaction/form/BdsFormInput.stories.tsx +139 -0
- package/src/web-components/interaction/form/BdsNumberInput.mdx +51 -22
- package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +24 -46
- package/src/web-components/interaction/form/BdsRadio.mdx +53 -22
- package/src/web-components/interaction/form/BdsRadio.stories.tsx +19 -51
- package/src/web-components/interaction/form/BdsRadioGroup.mdx +73 -0
- package/src/web-components/interaction/form/BdsRadioGroup.stories.tsx +88 -0
- package/src/web-components/interaction/form/BdsSegmentedControl.mdx +76 -23
- package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +64 -54
- package/src/web-components/interaction/form/BdsSelect.mdx +59 -21
- package/src/web-components/interaction/form/BdsSelect.stories.tsx +47 -56
- package/src/web-components/interaction/form/BdsSlider.mdx +55 -27
- package/src/web-components/interaction/form/BdsSlider.stories.tsx +23 -44
- package/src/web-components/interaction/form/BdsSwitch.mdx +53 -20
- package/src/web-components/interaction/form/BdsSwitch.stories.tsx +22 -37
- package/src/web-components/interaction/form/BdsTextarea.mdx +54 -22
- package/src/web-components/interaction/form/BdsTextarea.stories.tsx +35 -44
- package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +55 -0
- package/src/web-components/interaction/form/bds-checkbox-group.ts +117 -0
- package/src/web-components/interaction/form/bds-form-input.spec.ts +83 -0
- package/src/web-components/interaction/form/bds-form-input.ts +268 -0
- package/src/web-components/interaction/form/bds-radio-group.spec.ts +62 -0
- package/src/web-components/interaction/form/bds-radio-group.ts +142 -0
- package/src/web-components/interaction/form/bds-segmented-control.ts +5 -0
- package/src/web-components/ui/BdsAvatar.mdx +29 -20
- package/src/web-components/ui/BdsAvatar.stories.tsx +14 -28
- package/src/web-components/ui/BdsBreadcrumb.mdx +22 -19
- package/src/web-components/ui/BdsBreadcrumb.stories.tsx +11 -21
- package/src/web-components/ui/BdsButtonGroup.mdx +69 -0
- package/src/web-components/ui/BdsButtonGroup.stories.tsx +66 -0
- package/src/web-components/ui/BdsCalendar.mdx +93 -0
- package/src/web-components/ui/BdsCalendar.stories.tsx +63 -0
- package/src/web-components/ui/BdsCard.mdx +30 -25
- package/src/web-components/ui/BdsCard.stories.tsx +19 -42
- package/src/web-components/ui/BdsCarousel.mdx +83 -0
- package/src/web-components/ui/BdsCarousel.stories.tsx +96 -0
- package/src/web-components/ui/BdsDescriptionList.mdx +13 -16
- package/src/web-components/ui/BdsDescriptionList.stories.tsx +34 -49
- package/src/web-components/ui/BdsIconWrapper.mdx +20 -20
- package/src/web-components/ui/BdsIconWrapper.stories.tsx +46 -41
- package/src/web-components/ui/BdsLink.mdx +21 -17
- package/src/web-components/ui/BdsLink.stories.tsx +19 -20
- package/src/web-components/ui/BdsLoading.mdx +9 -13
- package/src/web-components/ui/BdsLoading.stories.tsx +8 -10
- package/src/web-components/ui/BdsNotificationBanner.mdx +32 -37
- package/src/web-components/ui/BdsNotificationBanner.stories.tsx +38 -47
- package/src/web-components/ui/BdsPagination.mdx +74 -0
- package/src/web-components/ui/BdsPagination.stories.tsx +36 -0
- package/src/web-components/ui/BdsProgress.mdx +24 -17
- package/src/web-components/ui/BdsProgress.stories.tsx +55 -29
- package/src/web-components/ui/BdsProgressCircle.mdx +24 -18
- package/src/web-components/ui/BdsProgressCircle.stories.tsx +17 -29
- package/src/web-components/ui/BdsRating.mdx +22 -22
- package/src/web-components/ui/BdsRating.stories.tsx +19 -13
- package/src/web-components/ui/BdsSectionHeader.mdx +31 -28
- package/src/web-components/ui/BdsSectionHeader.stories.tsx +21 -23
- package/src/web-components/ui/BdsSeparator.mdx +22 -8
- package/src/web-components/ui/BdsSeparator.stories.tsx +27 -19
- package/src/web-components/ui/BdsSkeleton.mdx +20 -26
- package/src/web-components/ui/BdsSkeleton.stories.tsx +23 -23
- package/src/web-components/ui/BdsTable.mdx +81 -0
- package/src/web-components/ui/BdsTable.stories.tsx +83 -0
- package/src/web-components/ui/BdsTypography.mdx +20 -29
- package/src/web-components/ui/BdsTypography.stories.tsx +19 -18
- package/src/web-components/ui/bds-button-group.spec.ts +40 -0
- package/src/web-components/ui/bds-button-group.ts +78 -0
- package/src/web-components/ui/bds-calendar.spec.ts +91 -0
- package/src/web-components/ui/bds-calendar.ts +427 -0
- package/src/web-components/ui/bds-carousel.spec.ts +64 -0
- package/src/web-components/ui/bds-carousel.ts +296 -0
- package/src/web-components/ui/bds-pagination.spec.ts +67 -0
- package/src/web-components/ui/bds-pagination.ts +197 -0
- package/src/web-components/ui/bds-table.spec.ts +45 -0
- package/src/web-components/ui/bds-table.ts +96 -0
|
@@ -3,39 +3,70 @@ import * as Stories from './BdsRadio.stories';
|
|
|
3
3
|
|
|
4
4
|
<Meta of={Stories} />
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# <bds-radio>
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Form-associated radio button custom element. Use multiple `<bds-radio>` elements with the same `name` to form a mutually exclusive group. For a labelled group with error/hint support, use `<bds-radio-group>`.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
> **Status: experimental** — API may change before stable release.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import '@boostdev/components/web-components';
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## When to use
|
|
19
|
+
|
|
20
|
+
Use `<bds-radio>` for single-choice selection from a set of options. Always group radios that belong to the same choice under `<bds-radio-group>` for accessibility.
|
|
21
|
+
|
|
22
|
+
## Examples
|
|
23
|
+
|
|
24
|
+
### Default
|
|
25
|
+
<Canvas of={Stories.Default} />
|
|
26
|
+
|
|
27
|
+
### Checked
|
|
28
|
+
<Canvas of={Stories.Checked} />
|
|
29
|
+
|
|
30
|
+
### Disabled
|
|
31
|
+
<Canvas of={Stories.Disabled} />
|
|
32
|
+
|
|
33
|
+
## Props
|
|
34
|
+
|
|
35
|
+
<ArgTypes of={Stories} />
|
|
36
|
+
|
|
18
37
|
## Attributes
|
|
19
38
|
|
|
20
|
-
| Attribute | Type
|
|
21
|
-
|
|
22
|
-
| `name` |
|
|
23
|
-
| `value` |
|
|
24
|
-
| `checked` |
|
|
25
|
-
| `required` |
|
|
26
|
-
| `disabled` |
|
|
39
|
+
| Attribute | Type | Default | Description |
|
|
40
|
+
|------------|---------|---------|--------------------------------------------------|
|
|
41
|
+
| `name` | string | `""` | Form field name (groups radio buttons) |
|
|
42
|
+
| `value` | string | `""` | Value submitted when this option is selected |
|
|
43
|
+
| `checked` | boolean | `false` | Whether this option is selected |
|
|
44
|
+
| `required` | boolean | `false` | Marks the field as required |
|
|
45
|
+
| `disabled` | boolean | `false` | Disables this radio option |
|
|
27
46
|
|
|
28
47
|
## Slots
|
|
29
48
|
|
|
30
|
-
| Slot
|
|
31
|
-
|
|
32
|
-
| (default) | Label text
|
|
49
|
+
| Slot | Description |
|
|
50
|
+
|-------------|--------------------|
|
|
51
|
+
| `(default)` | Label text content |
|
|
33
52
|
|
|
34
|
-
##
|
|
53
|
+
## Events
|
|
35
54
|
|
|
36
|
-
|
|
37
|
-
|
|
55
|
+
| Event | Detail | Description |
|
|
56
|
+
|----------|--------|------------------------------------------------|
|
|
57
|
+
| `change` | — | Native change event forwarded from the inner `<input>` |
|
|
38
58
|
|
|
39
|
-
##
|
|
59
|
+
## Usage in plain HTML
|
|
40
60
|
|
|
41
|
-
|
|
61
|
+
```html
|
|
62
|
+
<bds-radio name="size" value="s">Small</bds-radio>
|
|
63
|
+
<bds-radio name="size" value="m" checked>Medium</bds-radio>
|
|
64
|
+
<bds-radio name="size" value="l">Large</bds-radio>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Accessibility
|
|
68
|
+
|
|
69
|
+
- The inner `<input type="radio">` is associated with a `<label>` via matching `id`/`for` attributes
|
|
70
|
+
- Radios with the same `name` form a mutually exclusive group for keyboard navigation
|
|
71
|
+
- Use `<bds-radio-group>` to wrap related radios in a `<fieldset>`/`<legend>` for proper screen reader announcements
|
|
72
|
+
- Participates in HTML forms via the Form-Associated Custom Elements API
|
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
-
import '../../index';
|
|
3
|
+
import '../../index'; // auto-registers all custom elements
|
|
4
4
|
|
|
5
5
|
function BdsRadio({
|
|
6
|
-
|
|
7
|
-
value
|
|
6
|
+
name,
|
|
7
|
+
value,
|
|
8
8
|
checked,
|
|
9
|
+
required,
|
|
9
10
|
disabled,
|
|
11
|
+
children,
|
|
10
12
|
}: {
|
|
11
|
-
|
|
13
|
+
name?: string;
|
|
12
14
|
value?: string;
|
|
13
15
|
checked?: boolean;
|
|
16
|
+
required?: boolean;
|
|
14
17
|
disabled?: boolean;
|
|
18
|
+
children?: React.ReactNode;
|
|
15
19
|
}) {
|
|
16
20
|
return React.createElement(
|
|
17
21
|
'bds-radio',
|
|
18
22
|
{
|
|
19
|
-
name
|
|
23
|
+
name,
|
|
20
24
|
value,
|
|
21
25
|
checked: checked || undefined,
|
|
26
|
+
required: required || undefined,
|
|
22
27
|
disabled: disabled || undefined,
|
|
23
28
|
},
|
|
24
|
-
|
|
29
|
+
children,
|
|
25
30
|
);
|
|
26
31
|
}
|
|
27
32
|
|
|
@@ -29,9 +34,10 @@ const meta = {
|
|
|
29
34
|
title: 'Web Components/Interaction/Form/Radio',
|
|
30
35
|
component: BdsRadio,
|
|
31
36
|
tags: ['!stable', 'experimental'],
|
|
32
|
-
parameters: { layout: '
|
|
37
|
+
parameters: { layout: 'centered' },
|
|
33
38
|
argTypes: {
|
|
34
39
|
checked: { control: 'boolean' },
|
|
40
|
+
required: { control: 'boolean' },
|
|
35
41
|
disabled: { control: 'boolean' },
|
|
36
42
|
},
|
|
37
43
|
} satisfies Meta<typeof BdsRadio>;
|
|
@@ -39,52 +45,14 @@ const meta = {
|
|
|
39
45
|
export default meta;
|
|
40
46
|
type Story = StoryObj<typeof meta>;
|
|
41
47
|
|
|
42
|
-
export const Default: Story = {
|
|
43
|
-
|
|
44
|
-
export const Disabled: Story = { args: { label: 'Disabled option', value: 'c', disabled: true } };
|
|
45
|
-
|
|
46
|
-
export const Group: Story = {
|
|
47
|
-
render: () => (
|
|
48
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
|
49
|
-
{React.createElement('bds-radio', { name: 'size', value: 's' }, 'Small')}
|
|
50
|
-
{React.createElement('bds-radio', { name: 'size', value: 'm', checked: true }, 'Medium')}
|
|
51
|
-
{React.createElement('bds-radio', { name: 'size', value: 'l' }, 'Large')}
|
|
52
|
-
{React.createElement('bds-radio', { name: 'size', value: 'xl', disabled: true }, 'Extra Large (unavailable)')}
|
|
53
|
-
</div>
|
|
54
|
-
),
|
|
48
|
+
export const Default: Story = {
|
|
49
|
+
args: { name: 'size', value: 'm', children: 'Medium' },
|
|
55
50
|
};
|
|
56
51
|
|
|
57
|
-
export const
|
|
58
|
-
|
|
59
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
|
60
|
-
{React.createElement('bds-radio', { name: 'v', value: 'a' }, 'Unselected')}
|
|
61
|
-
{React.createElement('bds-radio', { name: 'v', value: 'b', checked: true }, 'Selected')}
|
|
62
|
-
{React.createElement('bds-radio', { name: 'v', value: 'c', disabled: true }, 'Disabled')}
|
|
63
|
-
{React.createElement('bds-radio', { name: 'v', value: 'd', checked: true, disabled: true }, 'Disabled selected')}
|
|
64
|
-
</div>
|
|
65
|
-
),
|
|
52
|
+
export const Checked: Story = {
|
|
53
|
+
args: { name: 'size', value: 'm', checked: true, children: 'Medium (checked)' },
|
|
66
54
|
};
|
|
67
55
|
|
|
68
|
-
export const
|
|
69
|
-
|
|
70
|
-
const [value, setValue] = React.useState('m');
|
|
71
|
-
return (
|
|
72
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
|
73
|
-
<p style={{ margin: '0 0 8px', fontSize: '0.875rem' }}>Selected: {value}</p>
|
|
74
|
-
{(['s', 'm', 'l'] as const).map(v =>
|
|
75
|
-
React.createElement(
|
|
76
|
-
'bds-radio',
|
|
77
|
-
{
|
|
78
|
-
key: v,
|
|
79
|
-
name: 'ctrl',
|
|
80
|
-
value: v,
|
|
81
|
-
checked: value === v || undefined,
|
|
82
|
-
onChange: () => setValue(v),
|
|
83
|
-
},
|
|
84
|
-
v === 's' ? 'Small' : v === 'm' ? 'Medium' : 'Large',
|
|
85
|
-
),
|
|
86
|
-
)}
|
|
87
|
-
</div>
|
|
88
|
-
);
|
|
89
|
-
},
|
|
56
|
+
export const Disabled: Story = {
|
|
57
|
+
args: { name: 'size', value: 'm', disabled: true, children: 'Medium (disabled)' },
|
|
90
58
|
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './BdsRadioGroup.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# <bds-radio-group>
|
|
7
|
+
|
|
8
|
+
Groups related radio buttons with a shared label and name. Renders a `<fieldset>` with a `<legend>`, and propagates the `name` attribute to all slotted `<bds-radio>` children so they form a mutually exclusive set.
|
|
9
|
+
|
|
10
|
+
> **Status: experimental** — API may change before stable release.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import '@boostdev/components/web-components';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## When to use
|
|
19
|
+
|
|
20
|
+
Use `<bds-radio-group>` whenever you have a set of mutually exclusive options. The `<fieldset>`/`<legend>` structure ensures screen readers announce the group label alongside each radio option.
|
|
21
|
+
|
|
22
|
+
## Examples
|
|
23
|
+
|
|
24
|
+
### Default
|
|
25
|
+
<Canvas of={Stories.Default} />
|
|
26
|
+
|
|
27
|
+
### With error
|
|
28
|
+
<Canvas of={Stories.WithError} />
|
|
29
|
+
|
|
30
|
+
### With hint
|
|
31
|
+
<Canvas of={Stories.WithHint} />
|
|
32
|
+
|
|
33
|
+
### Disabled
|
|
34
|
+
<Canvas of={Stories.Disabled} />
|
|
35
|
+
|
|
36
|
+
## Props
|
|
37
|
+
|
|
38
|
+
<ArgTypes of={Stories} />
|
|
39
|
+
|
|
40
|
+
## Attributes
|
|
41
|
+
|
|
42
|
+
| Attribute | Type | Default | Description |
|
|
43
|
+
|------------|---------|---------|-------------------------------------------------------------------|
|
|
44
|
+
| `label` | string | `""` | Group label rendered as `<legend>` |
|
|
45
|
+
| `name` | string | `""` | Shared name propagated to all slotted `<bds-radio>` elements |
|
|
46
|
+
| `error` | string | — | Error message shown below items; takes priority over `hint` |
|
|
47
|
+
| `hint` | string | — | Helper text shown below items |
|
|
48
|
+
| `required` | boolean | `false` | Shows a required indicator on the legend |
|
|
49
|
+
| `disabled` | boolean | `false` | Disables the entire fieldset |
|
|
50
|
+
|
|
51
|
+
## Slots
|
|
52
|
+
|
|
53
|
+
| Slot | Description |
|
|
54
|
+
|-------------|---------------------------|
|
|
55
|
+
| `(default)` | `<bds-radio>` elements |
|
|
56
|
+
|
|
57
|
+
## Usage in plain HTML
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<bds-radio-group label="Preferred contact" name="contact" required>
|
|
61
|
+
<bds-radio value="email">Email</bds-radio>
|
|
62
|
+
<bds-radio value="phone">Phone</bds-radio>
|
|
63
|
+
<bds-radio value="post">Post</bds-radio>
|
|
64
|
+
</bds-radio-group>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Accessibility
|
|
68
|
+
|
|
69
|
+
- Renders a `<fieldset>` with a `<legend>`, which is announced as a group by screen readers
|
|
70
|
+
- The `name` attribute is automatically propagated to all slotted `<bds-radio>` elements
|
|
71
|
+
- `required` adds a visual asterisk to the legend
|
|
72
|
+
- `disabled` on the group disables all contained radios via the native `<fieldset disabled>` behaviour
|
|
73
|
+
- Error messages use `role="alert"` so they are announced immediately
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import '../../index'; // auto-registers all custom elements
|
|
4
|
+
|
|
5
|
+
function BdsRadioGroup({
|
|
6
|
+
label,
|
|
7
|
+
name,
|
|
8
|
+
error,
|
|
9
|
+
hint,
|
|
10
|
+
required,
|
|
11
|
+
disabled,
|
|
12
|
+
children,
|
|
13
|
+
}: {
|
|
14
|
+
label?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
hint?: string;
|
|
18
|
+
required?: boolean;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
children?: React.ReactNode;
|
|
21
|
+
}) {
|
|
22
|
+
return React.createElement(
|
|
23
|
+
'bds-radio-group',
|
|
24
|
+
{
|
|
25
|
+
label,
|
|
26
|
+
name,
|
|
27
|
+
error,
|
|
28
|
+
hint,
|
|
29
|
+
required: required || undefined,
|
|
30
|
+
disabled: disabled || undefined,
|
|
31
|
+
},
|
|
32
|
+
children,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const meta = {
|
|
37
|
+
title: 'Web Components/Interaction/Form/RadioGroup',
|
|
38
|
+
component: BdsRadioGroup,
|
|
39
|
+
tags: ['!stable', 'experimental'],
|
|
40
|
+
parameters: { layout: 'centered' },
|
|
41
|
+
argTypes: {
|
|
42
|
+
required: { control: 'boolean' },
|
|
43
|
+
disabled: { control: 'boolean' },
|
|
44
|
+
},
|
|
45
|
+
} satisfies Meta<typeof BdsRadioGroup>;
|
|
46
|
+
|
|
47
|
+
export default meta;
|
|
48
|
+
type Story = StoryObj<typeof meta>;
|
|
49
|
+
|
|
50
|
+
export const Default: Story = {
|
|
51
|
+
render: () => (
|
|
52
|
+
<BdsRadioGroup label="Preferred contact" name="contact">
|
|
53
|
+
{React.createElement('bds-radio', { value: 'email' }, 'Email')}
|
|
54
|
+
{React.createElement('bds-radio', { value: 'phone' }, 'Phone')}
|
|
55
|
+
{React.createElement('bds-radio', { value: 'post' }, 'Post')}
|
|
56
|
+
</BdsRadioGroup>
|
|
57
|
+
),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const WithError: Story = {
|
|
61
|
+
render: () => (
|
|
62
|
+
<BdsRadioGroup label="Preferred contact" name="contact" error="Please select a contact method.">
|
|
63
|
+
{React.createElement('bds-radio', { value: 'email' }, 'Email')}
|
|
64
|
+
{React.createElement('bds-radio', { value: 'phone' }, 'Phone')}
|
|
65
|
+
{React.createElement('bds-radio', { value: 'post' }, 'Post')}
|
|
66
|
+
</BdsRadioGroup>
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const WithHint: Story = {
|
|
71
|
+
render: () => (
|
|
72
|
+
<BdsRadioGroup label="Preferred contact" name="contact" hint="We will only contact you via the selected method.">
|
|
73
|
+
{React.createElement('bds-radio', { value: 'email' }, 'Email')}
|
|
74
|
+
{React.createElement('bds-radio', { value: 'phone' }, 'Phone')}
|
|
75
|
+
{React.createElement('bds-radio', { value: 'post' }, 'Post')}
|
|
76
|
+
</BdsRadioGroup>
|
|
77
|
+
),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const Disabled: Story = {
|
|
81
|
+
render: () => (
|
|
82
|
+
<BdsRadioGroup label="Preferred contact" name="contact" disabled>
|
|
83
|
+
{React.createElement('bds-radio', { value: 'email' }, 'Email')}
|
|
84
|
+
{React.createElement('bds-radio', { value: 'phone' }, 'Phone')}
|
|
85
|
+
{React.createElement('bds-radio', { value: 'post' }, 'Post')}
|
|
86
|
+
</BdsRadioGroup>
|
|
87
|
+
),
|
|
88
|
+
};
|
|
@@ -3,40 +3,93 @@ import * as Stories from './BdsSegmentedControl.stories';
|
|
|
3
3
|
|
|
4
4
|
<Meta of={Stories} />
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# <bds-segmented-control>
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Form-associated segmented control. Renders a group of radio buttons (or anchor links) styled as a unified segmented bar. Supports two visual variants: `outline` (sliding inset-border indicator) and `filled` (sliding filled thumb).
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
> **Status: experimental** — API may change before stable release.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
options='[{"value":"list","label":"List"},{"value":"grid","label":"Grid"}]'
|
|
17
|
-
></bds-segmented-control>
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import '@boostdev/components/web-components';
|
|
18
16
|
```
|
|
19
17
|
|
|
18
|
+
## When to use
|
|
19
|
+
|
|
20
|
+
Use `<bds-segmented-control>` for mutually exclusive view or mode toggles where all options are visible simultaneously — for example, switching between List, Grid, and Map views. When used with `href` options it renders `<a>` links for navigation.
|
|
21
|
+
|
|
22
|
+
## Examples
|
|
23
|
+
|
|
24
|
+
### Default (outline variant)
|
|
25
|
+
<Canvas of={Stories.Default} />
|
|
26
|
+
|
|
27
|
+
### Filled variant
|
|
28
|
+
<Canvas of={Stories.Filled} />
|
|
29
|
+
|
|
30
|
+
### With a disabled option
|
|
31
|
+
<Canvas of={Stories.WithDisabledOption} />
|
|
32
|
+
|
|
33
|
+
### As navigation links
|
|
34
|
+
<Canvas of={Stories.AsNavLinks} />
|
|
35
|
+
|
|
36
|
+
### Disabled
|
|
37
|
+
<Canvas of={Stories.Disabled} />
|
|
38
|
+
|
|
39
|
+
## Props
|
|
40
|
+
|
|
41
|
+
<ArgTypes of={Stories} />
|
|
42
|
+
|
|
20
43
|
## Attributes
|
|
21
44
|
|
|
22
|
-
| Attribute | Type
|
|
23
|
-
|
|
24
|
-
| `name` |
|
|
25
|
-
| `value` |
|
|
26
|
-
| `options` |
|
|
27
|
-
| `disabled` |
|
|
45
|
+
| Attribute | Type | Default | Description |
|
|
46
|
+
|------------|---------------------------|-------------|----------------------------------------------------------------|
|
|
47
|
+
| `name` | string | `""` | Form field name; also used as the internal radio group name |
|
|
48
|
+
| `value` | string | `""` | Currently selected value |
|
|
49
|
+
| `options` | string | `"[]"` | JSON array of `{ value, label, disabled?, href? }` objects |
|
|
50
|
+
| `disabled` | boolean | `false` | Disables all options |
|
|
51
|
+
| `variant` | `"outline" \| "filled"` | `"outline"` | Visual style — sliding inset-border or sliding filled thumb |
|
|
28
52
|
|
|
29
53
|
## Events
|
|
30
54
|
|
|
31
|
-
| Event | Description
|
|
32
|
-
|
|
33
|
-
| `change` | Fired when a new segment is selected |
|
|
55
|
+
| Event | Detail | Description |
|
|
56
|
+
|----------|--------|----------------------------------------------------------|
|
|
57
|
+
| `change` | — | Fired when a new segment is selected (radio mode only) |
|
|
34
58
|
|
|
35
|
-
##
|
|
59
|
+
## Usage in plain HTML
|
|
36
60
|
|
|
37
|
-
|
|
38
|
-
|
|
61
|
+
```html
|
|
62
|
+
<!-- Radio mode -->
|
|
63
|
+
<bds-segmented-control
|
|
64
|
+
name="view"
|
|
65
|
+
value="list"
|
|
66
|
+
options='[{"value":"list","label":"List"},{"value":"grid","label":"Grid"}]'
|
|
67
|
+
></bds-segmented-control>
|
|
68
|
+
|
|
69
|
+
<!-- Link mode -->
|
|
70
|
+
<bds-segmented-control
|
|
71
|
+
variant="outline"
|
|
72
|
+
value="home"
|
|
73
|
+
options='[{"value":"home","label":"Home","href":"/"},{"value":"about","label":"About","href":"/about"}]'
|
|
74
|
+
></bds-segmented-control>
|
|
75
|
+
```
|
|
39
76
|
|
|
40
|
-
##
|
|
77
|
+
## CSS Custom Properties
|
|
41
78
|
|
|
42
|
-
|
|
79
|
+
| Property | Default | Description |
|
|
80
|
+
|----------|---------|-------------|
|
|
81
|
+
| `--segmented_color_bg` | `var(--bds-color_bg--subtle)` | Track background colour (both variants) |
|
|
82
|
+
| `--segmented_item-bg` | `transparent` | Inactive item background (outline variant) |
|
|
83
|
+
| `--segmented_item-bg--active` | `var(--bds-color_bg)` | Active item background (outline variant) |
|
|
84
|
+
| `--segmented_thumb-color` | `var(--bds-color_interactive)` | Filled thumb colour (filled variant) |
|
|
85
|
+
| `--segmented_color` | `var(--bds-color_on-bg)` | Item text colour |
|
|
86
|
+
| `--segmented_color--active` | `var(--bds-color_on-interactive)` (filled) / `var(--bds-color_interactive)` (outline) | Active item text colour |
|
|
87
|
+
|
|
88
|
+
## Accessibility
|
|
89
|
+
|
|
90
|
+
- In radio mode, each option renders as a visually hidden `<input type="radio">` inside a `<label>`, so keyboard users can navigate with arrow keys
|
|
91
|
+
- In link mode, options render as `<a>` elements with `aria-current="page"` on the active item
|
|
92
|
+
- Disabled options have `aria-disabled="true"` and remove the `href` from link mode items
|
|
93
|
+
- `disabled` on the host element disables all options
|
|
94
|
+
- The sliding thumb and indicator use `aria-hidden="true"` so they are not announced by screen readers
|
|
95
|
+
- Participates in HTML forms via the Form-Associated Custom Elements API (radio mode only)
|
|
@@ -1,33 +1,44 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
-
import
|
|
4
|
-
import '../../index';
|
|
3
|
+
import '../../index'; // auto-registers all custom elements
|
|
5
4
|
|
|
6
|
-
const VIEW_OPTIONS
|
|
5
|
+
const VIEW_OPTIONS = JSON.stringify([
|
|
7
6
|
{ value: 'list', label: 'List' },
|
|
8
7
|
{ value: 'grid', label: 'Grid' },
|
|
9
|
-
{ value: '
|
|
10
|
-
];
|
|
8
|
+
{ value: 'map', label: 'Map' },
|
|
9
|
+
]);
|
|
11
10
|
|
|
12
|
-
const
|
|
13
|
-
{ value: '
|
|
14
|
-
{ value: '
|
|
15
|
-
{ value: '
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
const OPTIONS_WITH_DISABLED = JSON.stringify([
|
|
12
|
+
{ value: 'list', label: 'List' },
|
|
13
|
+
{ value: 'grid', label: 'Grid', disabled: true },
|
|
14
|
+
{ value: 'map', label: 'Map' },
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
const NAV_OPTIONS = JSON.stringify([
|
|
18
|
+
{ value: 'home', label: 'Home', href: '/' },
|
|
19
|
+
{ value: 'about', label: 'About', href: '/about' },
|
|
20
|
+
{ value: 'contact', label: 'Contact', href: '/contact' },
|
|
21
|
+
]);
|
|
18
22
|
|
|
19
23
|
function BdsSegmentedControl({
|
|
20
|
-
|
|
24
|
+
name,
|
|
25
|
+
value,
|
|
26
|
+
options,
|
|
21
27
|
disabled,
|
|
28
|
+
variant,
|
|
22
29
|
}: {
|
|
30
|
+
name?: string;
|
|
23
31
|
value?: string;
|
|
32
|
+
options?: string;
|
|
24
33
|
disabled?: boolean;
|
|
34
|
+
variant?: 'outline' | 'filled';
|
|
25
35
|
}) {
|
|
26
36
|
return React.createElement('bds-segmented-control', {
|
|
27
|
-
name
|
|
37
|
+
name,
|
|
28
38
|
value,
|
|
29
|
-
options
|
|
39
|
+
options,
|
|
30
40
|
disabled: disabled || undefined,
|
|
41
|
+
variant,
|
|
31
42
|
});
|
|
32
43
|
}
|
|
33
44
|
|
|
@@ -35,8 +46,9 @@ const meta = {
|
|
|
35
46
|
title: 'Web Components/Interaction/Form/SegmentedControl',
|
|
36
47
|
component: BdsSegmentedControl,
|
|
37
48
|
tags: ['!stable', 'experimental'],
|
|
38
|
-
parameters: { layout: '
|
|
49
|
+
parameters: { layout: 'centered' },
|
|
39
50
|
argTypes: {
|
|
51
|
+
variant: { control: 'select', options: ['outline', 'filled'] },
|
|
40
52
|
disabled: { control: 'boolean' },
|
|
41
53
|
},
|
|
42
54
|
} satisfies Meta<typeof BdsSegmentedControl>;
|
|
@@ -44,49 +56,47 @@ const meta = {
|
|
|
44
56
|
export default meta;
|
|
45
57
|
type Story = StoryObj<typeof meta>;
|
|
46
58
|
|
|
47
|
-
export const Default: Story = {
|
|
48
|
-
|
|
59
|
+
export const Default: Story = {
|
|
60
|
+
args: {
|
|
61
|
+
name: 'view',
|
|
62
|
+
value: 'list',
|
|
63
|
+
options: VIEW_OPTIONS,
|
|
64
|
+
variant: 'outline',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const Filled: Story = {
|
|
69
|
+
args: {
|
|
70
|
+
name: 'view',
|
|
71
|
+
value: 'list',
|
|
72
|
+
options: VIEW_OPTIONS,
|
|
73
|
+
variant: 'filled',
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
49
77
|
export const WithDisabledOption: Story = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
78
|
+
args: {
|
|
79
|
+
name: 'view',
|
|
80
|
+
value: 'list',
|
|
81
|
+
options: OPTIONS_WITH_DISABLED,
|
|
82
|
+
variant: 'outline',
|
|
83
|
+
},
|
|
56
84
|
};
|
|
57
85
|
|
|
58
|
-
export const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
</div>
|
|
65
|
-
<div>
|
|
66
|
-
<p style={{ margin: '0 0 8px', fontSize: '0.75rem', color: '#888' }}>Time period (year disabled)</p>
|
|
67
|
-
{React.createElement('bds-segmented-control', { name: 'period1', value: 'week', options: JSON.stringify(PERIOD_OPTIONS) })}
|
|
68
|
-
</div>
|
|
69
|
-
<div>
|
|
70
|
-
<p style={{ margin: '0 0 8px', fontSize: '0.75rem', color: '#888' }}>Disabled</p>
|
|
71
|
-
{React.createElement('bds-segmented-control', { name: 'view2', value: 'list', options: JSON.stringify(VIEW_OPTIONS), disabled: true })}
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
),
|
|
86
|
+
export const AsNavLinks: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
value: 'home',
|
|
89
|
+
options: NAV_OPTIONS,
|
|
90
|
+
variant: 'outline',
|
|
91
|
+
},
|
|
75
92
|
};
|
|
76
93
|
|
|
77
|
-
export const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
name: 'view-ctrl',
|
|
85
|
-
value: view,
|
|
86
|
-
options: JSON.stringify(VIEW_OPTIONS),
|
|
87
|
-
onChange: (e: Event) => setView((e.target as HTMLInputElement).value),
|
|
88
|
-
})}
|
|
89
|
-
</div>
|
|
90
|
-
);
|
|
94
|
+
export const Disabled: Story = {
|
|
95
|
+
args: {
|
|
96
|
+
name: 'view',
|
|
97
|
+
value: 'list',
|
|
98
|
+
options: VIEW_OPTIONS,
|
|
99
|
+
disabled: true,
|
|
100
|
+
variant: 'outline',
|
|
91
101
|
},
|
|
92
102
|
};
|