@boostdev/design-system-components 0.1.13 → 0.1.14
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 +1 -1
- package/README.md +1 -1
- package/dist/client.cjs +54 -50
- package/dist/client.css +495 -511
- package/dist/client.d.cts +5 -3
- package/dist/client.d.ts +5 -3
- package/dist/client.js +54 -50
- package/dist/index.cjs +54 -50
- package/dist/index.css +495 -511
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +54 -50
- package/package.json +4 -2
- package/src/components/interaction/Button/Button.mdx +73 -0
- package/src/components/interaction/Button/Button.module.css +12 -20
- package/src/components/interaction/Button/Button.stories.tsx +0 -1
- package/src/components/interaction/Command/Command.mdx +28 -0
- package/src/components/interaction/Command/Command.module.css +2 -1
- package/src/components/interaction/Command/Command.stories.tsx +3 -3
- package/src/components/interaction/Dialog/Dialog.mdx +57 -0
- package/src/components/interaction/Dialog/Dialog.module.css +1 -1
- package/src/components/interaction/Dialog/Dialog.stories.tsx +0 -1
- package/src/components/interaction/Drawer/Drawer.mdx +35 -0
- package/src/components/interaction/Drawer/Drawer.module.css +3 -9
- package/src/components/interaction/Drawer/Drawer.stories.tsx +3 -3
- package/src/components/interaction/Drawer/Drawer.tsx +5 -3
- package/src/components/interaction/DropdownMenu/DropdownMenu.mdx +40 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +5 -5
- package/src/components/interaction/Popover/Popover.mdx +34 -0
- package/src/components/interaction/Popover/Popover.stories.tsx +3 -3
- package/src/components/interaction/Rating/Rating.mdx +38 -0
- package/src/components/interaction/Rating/Rating.stories.tsx +0 -1
- package/src/components/interaction/Toast/Toast.mdx +78 -0
- package/src/components/interaction/Toast/Toast.module.css +21 -7
- package/src/components/interaction/Toast/Toast.stories.tsx +0 -1
- package/src/components/interaction/form/Checkbox/Checkbox.mdx +57 -0
- package/src/components/interaction/form/Checkbox/Checkbox.stories.tsx +0 -1
- package/src/components/interaction/form/Combobox/Combobox.mdx +37 -0
- package/src/components/interaction/form/Combobox/Combobox.stories.tsx +0 -1
- package/src/components/interaction/form/FileInput/FileInput.mdx +38 -0
- package/src/components/interaction/form/FileInput/FileInput.stories.tsx +0 -1
- package/src/components/interaction/form/FormInput/FormInput.mdx +54 -0
- package/src/components/interaction/form/FormInput/FormInput.stories.tsx +0 -1
- package/src/components/interaction/form/NumberInput/NumberInput.mdx +38 -0
- package/src/components/interaction/form/NumberInput/NumberInput.stories.tsx +0 -1
- package/src/components/interaction/form/Radio/Radio.mdx +60 -0
- package/src/components/interaction/form/Radio/Radio.module.css +24 -11
- package/src/components/interaction/form/Radio/Radio.stories.tsx +5 -5
- package/src/components/interaction/form/Radio/Radio.tsx +6 -2
- package/src/components/interaction/form/Select/Select.mdx +58 -0
- package/src/components/interaction/form/Select/Select.stories.tsx +0 -1
- package/src/components/interaction/form/Slider/Slider.mdx +58 -0
- package/src/components/interaction/form/Slider/Slider.stories.tsx +0 -1
- package/src/components/interaction/form/Switch/Switch.mdx +61 -0
- package/src/components/interaction/form/Switch/Switch.module.css +6 -10
- package/src/components/interaction/form/Switch/Switch.stories.tsx +0 -1
- package/src/components/interaction/form/Textarea/Textarea.mdx +41 -0
- package/src/components/interaction/form/Textarea/Textarea.stories.tsx +0 -1
- package/src/components/layout/ButtonGroup/ButtonGroup.mdx +47 -0
- package/src/components/layout/ButtonGroup/ButtonGroup.module.css +10 -10
- package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +1 -2
- package/src/components/layout/Card/Card.mdx +58 -0
- package/src/components/layout/Card/Card.stories.tsx +0 -1
- package/src/components/layout/IconWrapper/IconWrapper.mdx +39 -0
- package/src/components/layout/IconWrapper/IconWrapper.stories.tsx +0 -1
- package/src/components/layout/SectionHeader/SectionHeader.mdx +38 -0
- package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +0 -1
- package/src/components/ui/Accordion/Accordion.mdx +40 -0
- package/src/components/ui/Accordion/Accordion.stories.tsx +0 -1
- package/src/components/ui/Alert/Alert.mdx +63 -0
- package/src/components/ui/Alert/Alert.module.css +3 -7
- package/src/components/ui/Alert/Alert.stories.tsx +0 -1
- package/src/components/ui/Avatar/Avatar.mdx +53 -0
- package/src/components/ui/Avatar/Avatar.module.css +4 -8
- package/src/components/ui/Avatar/Avatar.stories.tsx +0 -1
- package/src/components/ui/Badge/Badge.mdx +62 -0
- package/src/components/ui/Badge/Badge.module.css +4 -10
- package/src/components/ui/Badge/Badge.stories.tsx +0 -1
- package/src/components/ui/Breadcrumb/Breadcrumb.mdx +36 -0
- package/src/components/ui/Breadcrumb/Breadcrumb.stories.tsx +0 -1
- package/src/components/ui/Calendar/Calendar.mdx +34 -0
- package/src/components/ui/Calendar/Calendar.stories.tsx +0 -1
- package/src/components/ui/Carousel/Carousel.mdx +31 -0
- package/src/components/ui/Carousel/Carousel.stories.tsx +0 -1
- package/src/components/ui/DescriptionList/DescriptionList.mdx +28 -0
- package/src/components/ui/DescriptionList/DescriptionList.stories.tsx +0 -1
- package/src/components/ui/Link/Link.mdx +44 -0
- package/src/components/ui/Link/Link.module.css +2 -5
- package/src/components/ui/Link/Link.stories.tsx +0 -1
- package/src/components/ui/Loading/Loading.mdx +52 -0
- package/src/components/ui/Loading/Loading.stories.tsx +0 -1
- package/src/components/ui/NotificationBanner/NotificationBanner.mdx +45 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.module.css +3 -7
- package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +2 -2
- package/src/components/ui/Pagination/Pagination.mdx +39 -0
- package/src/components/ui/Pagination/Pagination.module.css +3 -7
- package/src/components/ui/Pagination/Pagination.stories.tsx +0 -1
- package/src/components/ui/Progress/Progress.mdx +52 -0
- package/src/components/ui/Progress/Progress.module.css +1 -3
- package/src/components/ui/Progress/Progress.stories.tsx +0 -1
- package/src/components/ui/ProgressCircle/ProgressCircle.mdx +46 -0
- package/src/components/ui/ProgressCircle/ProgressCircle.stories.tsx +0 -1
- package/src/components/ui/Separator/Separator.mdx +29 -0
- package/src/components/ui/Separator/Separator.stories.tsx +0 -1
- package/src/components/ui/Skeleton/Skeleton.mdx +36 -0
- package/src/components/ui/Skeleton/Skeleton.stories.tsx +0 -1
- package/src/components/ui/SkipLink/SkipLink.mdx +34 -0
- package/src/components/ui/SkipLink/SkipLink.stories.tsx +0 -1
- package/src/components/ui/Table/Table.mdx +38 -0
- package/src/components/ui/Table/Table.stories.tsx +0 -1
- package/src/components/ui/Tabs/Tabs.mdx +40 -0
- package/src/components/ui/Tabs/Tabs.module.css +2 -5
- package/src/components/ui/Tabs/Tabs.stories.tsx +0 -1
- package/src/components/ui/Tooltip/Tooltip.mdx +43 -0
- package/src/components/ui/Tooltip/Tooltip.module.css +2 -5
- package/src/components/ui/Tooltip/Tooltip.stories.tsx +6 -6
- package/src/components/ui/Typography/Typography.mdx +41 -0
- package/src/components/ui/Typography/Typography.stories.tsx +0 -1
- package/src/static/logo.svg +8 -0
- package/src/stories/Introduction.css +17 -0
- package/src/stories/Introduction.mdx +129 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Toast.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Toast
|
|
7
|
+
|
|
8
|
+
Transient feedback notification that auto-dismisses after 5 seconds. Provided via a context provider and `useToast` hook.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Confirmation after a user action (save, delete, send)
|
|
12
|
+
- Non-blocking error or warning messages that do not require immediate action
|
|
13
|
+
|
|
14
|
+
## When not to use
|
|
15
|
+
- Critical errors that require user acknowledgement — use `Alert` or `Dialog`
|
|
16
|
+
- Status that needs to persist — use `Alert` or `NotificationBanner`
|
|
17
|
+
|
|
18
|
+
## Setup
|
|
19
|
+
|
|
20
|
+
Wrap your app (or layout root) once with `ToastProvider`:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { ToastProvider } from '@boostdev/components';
|
|
24
|
+
|
|
25
|
+
<ToastProvider>
|
|
26
|
+
<App />
|
|
27
|
+
</ToastProvider>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then call `showToast` anywhere inside:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { useToast } from '@boostdev/components';
|
|
34
|
+
|
|
35
|
+
const { showToast } = useToast();
|
|
36
|
+
|
|
37
|
+
showToast('Changes saved', 'success');
|
|
38
|
+
showToast('Something went wrong', 'error');
|
|
39
|
+
showToast('New message received', 'info');
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Examples
|
|
43
|
+
|
|
44
|
+
### Success
|
|
45
|
+
<Canvas of={Stories.Success} />
|
|
46
|
+
|
|
47
|
+
### Error
|
|
48
|
+
<Canvas of={Stories.Error} />
|
|
49
|
+
|
|
50
|
+
### Info
|
|
51
|
+
<Canvas of={Stories.Info} />
|
|
52
|
+
|
|
53
|
+
### All variants
|
|
54
|
+
<Canvas of={Stories.AllVariants} />
|
|
55
|
+
|
|
56
|
+
## Props
|
|
57
|
+
|
|
58
|
+
<ArgTypes of={Stories} />
|
|
59
|
+
|
|
60
|
+
## CSS variables
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
<table>
|
|
64
|
+
<thead>
|
|
65
|
+
<tr><th>Variable</th><th>Description</th></tr>
|
|
66
|
+
</thead>
|
|
67
|
+
<tbody>
|
|
68
|
+
<tr><td>`--toast_color`</td><td>Toast background</td></tr>
|
|
69
|
+
<tr><td>`--toast_on-color`</td><td>Toast text colour</td></tr>
|
|
70
|
+
</tbody>
|
|
71
|
+
</table>
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## Accessibility
|
|
75
|
+
|
|
76
|
+
- Toast container uses `role="status"` and `aria-live="polite"` — announcements are non-intrusive
|
|
77
|
+
- Auto-dismisses after 5 seconds; users can also manually dismiss
|
|
78
|
+
- Animation respects `prefers-reduced-motion`
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
padding: var(--space_m) var(--space_l);
|
|
14
14
|
background-color: var(--toast_color, var(--color_bg));
|
|
15
15
|
color: var(--toast_on-color, var(--color_on-bg));
|
|
16
|
-
border: 1px solid currentcolor;
|
|
17
16
|
border-radius: var(--border_radius--s);
|
|
18
17
|
box-shadow: var(--shadow_s);
|
|
19
18
|
display: flex;
|
|
@@ -23,6 +22,26 @@
|
|
|
23
22
|
animation: slideIn var(--animation_duration--fast) var(--animation_easing);
|
|
24
23
|
}
|
|
25
24
|
|
|
25
|
+
.toast.--variant_success {
|
|
26
|
+
--toast_color: var(--color_success);
|
|
27
|
+
--toast_on-color: var(--color_on-success);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.toast.--variant_warning {
|
|
31
|
+
--toast_color: var(--color_warning);
|
|
32
|
+
--toast_on-color: var(--color_on-warning);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.toast.--variant_info {
|
|
36
|
+
--toast_color: var(--color_bg--subtle);
|
|
37
|
+
--toast_on-color: var(--color_on-bg--subtle);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.toast.--variant_error {
|
|
41
|
+
--toast_color: var(--color_error);
|
|
42
|
+
--toast_on-color: var(--color_on-error);
|
|
43
|
+
}
|
|
44
|
+
|
|
26
45
|
.message {
|
|
27
46
|
flex: 1;
|
|
28
47
|
}
|
|
@@ -38,12 +57,7 @@
|
|
|
38
57
|
background: none;
|
|
39
58
|
border: none;
|
|
40
59
|
cursor: pointer;
|
|
41
|
-
color:
|
|
42
|
-
opacity: 0.6;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.closeButton:hover {
|
|
46
|
-
opacity: 1;
|
|
60
|
+
color: var(--toast_on-color);
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
.closeButton svg {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Checkbox.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Checkbox
|
|
7
|
+
|
|
8
|
+
Binary toggle for opt-in/opt-out choices. Renders a styled native `<input type="checkbox">` with label, hint, and error support.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Independent on/off choices (newsletter opt-in, terms acceptance)
|
|
12
|
+
- Multi-select within a group of options
|
|
13
|
+
|
|
14
|
+
## When not to use
|
|
15
|
+
- Mutually exclusive choices — use `Radio`
|
|
16
|
+
- A single toggle that takes immediate effect — use `Switch`
|
|
17
|
+
|
|
18
|
+
## Examples
|
|
19
|
+
|
|
20
|
+
### Default (unchecked)
|
|
21
|
+
<Canvas of={Stories.Default} />
|
|
22
|
+
|
|
23
|
+
### Checked
|
|
24
|
+
<Canvas of={Stories.Checked} />
|
|
25
|
+
|
|
26
|
+
### With hint
|
|
27
|
+
<Canvas of={Stories.WithHint} />
|
|
28
|
+
|
|
29
|
+
### With error
|
|
30
|
+
<Canvas of={Stories.WithError} />
|
|
31
|
+
|
|
32
|
+
### Disabled
|
|
33
|
+
<Canvas of={Stories.Disabled} />
|
|
34
|
+
|
|
35
|
+
## Props
|
|
36
|
+
|
|
37
|
+
<ArgTypes of={Stories} />
|
|
38
|
+
|
|
39
|
+
## CSS variables
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
<table>
|
|
43
|
+
<thead>
|
|
44
|
+
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
45
|
+
</thead>
|
|
46
|
+
<tbody>
|
|
47
|
+
<tr><td>`--checkbox_color-active`</td><td>`var(--color_active)`</td><td>Checked background</td></tr>
|
|
48
|
+
<tr><td>`--checkbox_color-on-active`</td><td>`var(--color_on-active)`</td><td>Checkmark colour</td></tr>
|
|
49
|
+
</tbody>
|
|
50
|
+
</table>
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Accessibility
|
|
54
|
+
|
|
55
|
+
- Native `<input type="checkbox">` — correct semantics without ARIA
|
|
56
|
+
- Label is always associated via `htmlFor`/`id`
|
|
57
|
+
- Error message is linked via `aria-describedby`
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Combobox.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Combobox
|
|
7
|
+
|
|
8
|
+
Searchable select with type-ahead filtering. Combines a text input with a dropdown option list.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Large option lists (10+) where filtering helps the user find their choice
|
|
12
|
+
- Data that may be loaded asynchronously
|
|
13
|
+
|
|
14
|
+
## When not to use
|
|
15
|
+
- Small, static option sets (< 10 items) — use `Select` for simplicity
|
|
16
|
+
|
|
17
|
+
## Examples
|
|
18
|
+
|
|
19
|
+
### Default
|
|
20
|
+
<Canvas of={Stories.Default} />
|
|
21
|
+
|
|
22
|
+
### With hint
|
|
23
|
+
<Canvas of={Stories.WithHint} />
|
|
24
|
+
|
|
25
|
+
### With error
|
|
26
|
+
<Canvas of={Stories.WithError} />
|
|
27
|
+
|
|
28
|
+
## Props
|
|
29
|
+
|
|
30
|
+
<ArgTypes of={Stories} />
|
|
31
|
+
|
|
32
|
+
## Accessibility
|
|
33
|
+
|
|
34
|
+
- Input uses `role="combobox"` with `aria-autocomplete="list"` and `aria-controls`
|
|
35
|
+
- Listbox uses `role="listbox"` with `role="option"` items
|
|
36
|
+
- `aria-activedescendant` tracks keyboard-highlighted option
|
|
37
|
+
- `Escape` closes the dropdown; `Enter` selects the active option
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './FileInput.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# FileInput
|
|
7
|
+
|
|
8
|
+
File upload input with styled trigger button, hint, and error support.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Any form that requires file attachments
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### Default
|
|
16
|
+
<Canvas of={Stories.Default} />
|
|
17
|
+
|
|
18
|
+
### With accept restriction
|
|
19
|
+
<Canvas of={Stories.WithAccept} />
|
|
20
|
+
|
|
21
|
+
### Multiple files
|
|
22
|
+
<Canvas of={Stories.Multiple} />
|
|
23
|
+
|
|
24
|
+
### With error
|
|
25
|
+
<Canvas of={Stories.WithError} />
|
|
26
|
+
|
|
27
|
+
### Disabled
|
|
28
|
+
<Canvas of={Stories.Disabled} />
|
|
29
|
+
|
|
30
|
+
## Props
|
|
31
|
+
|
|
32
|
+
<ArgTypes of={Stories} />
|
|
33
|
+
|
|
34
|
+
## Accessibility
|
|
35
|
+
|
|
36
|
+
- Native `<input type="file">` — opens the OS file picker
|
|
37
|
+
- Label linked via `id`/`htmlFor`
|
|
38
|
+
- `accept` restricts the file picker but does not validate — validate on the server too
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './FormInput.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# FormInput
|
|
7
|
+
|
|
8
|
+
Text input field with label, optional hint, and error state. The label and input are connected via `htmlFor`/`id` automatically.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Any single-line text, email, password, or number input in a form
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### Default
|
|
16
|
+
<Canvas of={Stories.Default} />
|
|
17
|
+
|
|
18
|
+
### With hint
|
|
19
|
+
<Canvas of={Stories.WithHint} />
|
|
20
|
+
|
|
21
|
+
### With error
|
|
22
|
+
<Canvas of={Stories.WithError} />
|
|
23
|
+
|
|
24
|
+
### Required
|
|
25
|
+
<Canvas of={Stories.Required} />
|
|
26
|
+
|
|
27
|
+
### Disabled
|
|
28
|
+
<Canvas of={Stories.Disabled} />
|
|
29
|
+
|
|
30
|
+
## Props
|
|
31
|
+
|
|
32
|
+
<ArgTypes of={Stories} />
|
|
33
|
+
|
|
34
|
+
## CSS variables
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
<table>
|
|
38
|
+
<thead>
|
|
39
|
+
<tr><th>Variable</th><th>Description</th></tr>
|
|
40
|
+
</thead>
|
|
41
|
+
<tbody>
|
|
42
|
+
<tr><td>`--input_border-color`</td><td>Border colour (set by state — focus, error)</td></tr>
|
|
43
|
+
<tr><td>`--input_color`</td><td>Background colour</td></tr>
|
|
44
|
+
<tr><td>`--input_on-color`</td><td>Text colour</td></tr>
|
|
45
|
+
</tbody>
|
|
46
|
+
</table>
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
## Accessibility
|
|
50
|
+
|
|
51
|
+
- `label` is always rendered and linked to the input via `id`/`htmlFor`
|
|
52
|
+
- Error message uses `aria-describedby` to associate with the input
|
|
53
|
+
- `required` sets both the HTML attribute and a visual indicator
|
|
54
|
+
- `disabled` removes the field from the tab order
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './NumberInput.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# NumberInput
|
|
7
|
+
|
|
8
|
+
Numeric text input with increment/decrement stepper buttons. Enforces `min`, `max`, and `step` constraints.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Quantity selectors, precise numeric values where a slider would be too imprecise
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### Default
|
|
16
|
+
<Canvas of={Stories.Default} />
|
|
17
|
+
|
|
18
|
+
### With range
|
|
19
|
+
<Canvas of={Stories.WithRange} />
|
|
20
|
+
|
|
21
|
+
### With step
|
|
22
|
+
<Canvas of={Stories.WithStep} />
|
|
23
|
+
|
|
24
|
+
### With error
|
|
25
|
+
<Canvas of={Stories.WithError} />
|
|
26
|
+
|
|
27
|
+
### Disabled
|
|
28
|
+
<Canvas of={Stories.Disabled} />
|
|
29
|
+
|
|
30
|
+
## Props
|
|
31
|
+
|
|
32
|
+
<ArgTypes of={Stories} />
|
|
33
|
+
|
|
34
|
+
## Accessibility
|
|
35
|
+
|
|
36
|
+
- Renders `<input type="number">` — browser-native spinner support
|
|
37
|
+
- Stepper buttons have `aria-label="Increment"` / `"Decrement"`
|
|
38
|
+
- Label linked via `id`/`htmlFor`
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Radio.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Radio
|
|
7
|
+
|
|
8
|
+
Single-select option within a group. Renders a styled native `<input type="radio">`. Group radios by a shared `name` attribute.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Mutually exclusive choices where all options are visible (2–6 options)
|
|
12
|
+
|
|
13
|
+
## When not to use
|
|
14
|
+
- More than 6 options — use `Select` or `Combobox`
|
|
15
|
+
- Independent on/off choices — use `Checkbox`
|
|
16
|
+
|
|
17
|
+
## Examples
|
|
18
|
+
|
|
19
|
+
### Default
|
|
20
|
+
<Canvas of={Stories.Default} />
|
|
21
|
+
|
|
22
|
+
### Selected
|
|
23
|
+
<Canvas of={Stories.Selected} />
|
|
24
|
+
|
|
25
|
+
### With description
|
|
26
|
+
<Canvas of={Stories.WithDescription} />
|
|
27
|
+
|
|
28
|
+
### Group
|
|
29
|
+
<Canvas of={Stories.Group} />
|
|
30
|
+
|
|
31
|
+
### With hint
|
|
32
|
+
<Canvas of={Stories.WithHint} />
|
|
33
|
+
|
|
34
|
+
### With error
|
|
35
|
+
<Canvas of={Stories.WithError} />
|
|
36
|
+
|
|
37
|
+
## Props
|
|
38
|
+
|
|
39
|
+
<ArgTypes of={Stories} />
|
|
40
|
+
|
|
41
|
+
## CSS variables
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
<table>
|
|
45
|
+
<thead>
|
|
46
|
+
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
47
|
+
</thead>
|
|
48
|
+
<tbody>
|
|
49
|
+
<tr><td>`--radio_color-active`</td><td>`var(--color_active)`</td><td>Selected fill and border colour</td></tr>
|
|
50
|
+
<tr><td>`--radio_color-on-active`</td><td>`var(--color_on-active)`</td><td>Inner dot colour</td></tr>
|
|
51
|
+
<tr><td>`--radio_description-color`</td><td>`var(--color_on-bg--subtle)`</td><td>Description text colour</td></tr>
|
|
52
|
+
</tbody>
|
|
53
|
+
</table>
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## Accessibility
|
|
57
|
+
|
|
58
|
+
- Native `<input type="radio">` — group semantics via shared `name`
|
|
59
|
+
- Wrap groups in `<fieldset>` with a `<legend>` for a group label
|
|
60
|
+
- Arrow keys move between radios in the same group
|
|
@@ -9,23 +9,36 @@
|
|
|
9
9
|
|
|
10
10
|
.inputWrapper {
|
|
11
11
|
display: flex;
|
|
12
|
-
align-items:
|
|
12
|
+
align-items: flex-start;
|
|
13
13
|
gap: var(--space_xs);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
.textWrapper {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: var(--space_xxxs);
|
|
20
|
+
padding-block-start: var(--space_xxxs);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.description {
|
|
24
|
+
font-size: var(--font_size--body--s);
|
|
25
|
+
color: var(--radio_description-color, var(--color_on-bg--subtle));
|
|
26
|
+
line-height: var(--font_line-height--body);
|
|
27
|
+
}
|
|
28
|
+
|
|
16
29
|
.radio {
|
|
17
30
|
flex-shrink: 0;
|
|
18
|
-
margin-block-start: 0.
|
|
31
|
+
margin-block-start: 0.2em;
|
|
19
32
|
width: var(--inputSize);
|
|
20
33
|
height: var(--inputSize);
|
|
21
34
|
color: var(--radio_color, var(--color_on-bg));
|
|
22
|
-
border:
|
|
35
|
+
border: 1.5px solid currentcolor;
|
|
23
36
|
border-radius: 50%;
|
|
24
37
|
appearance: none;
|
|
25
38
|
background-color: var(--radio_color_bg, var(--color_bg));
|
|
26
39
|
cursor: pointer;
|
|
27
40
|
position: relative;
|
|
28
|
-
transition: background-color 0.
|
|
41
|
+
transition: background-color 0.2s ease, border-color 0.2s ease;
|
|
29
42
|
}
|
|
30
43
|
|
|
31
44
|
.radio:checked {
|
|
@@ -36,13 +49,13 @@
|
|
|
36
49
|
.radio:checked::after {
|
|
37
50
|
content: '';
|
|
38
51
|
position: absolute;
|
|
39
|
-
left:
|
|
40
|
-
top:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
border-
|
|
45
|
-
|
|
52
|
+
left: 50%;
|
|
53
|
+
top: 50%;
|
|
54
|
+
transform: translate(-50%, -50%);
|
|
55
|
+
width: 0.4em;
|
|
56
|
+
height: 0.4em;
|
|
57
|
+
border-radius: 50%;
|
|
58
|
+
background-color: var(--radio_color-on-active, var(--color_on-active));
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
.radio:focus {
|
|
@@ -4,7 +4,6 @@ import { Radio } from './Radio';
|
|
|
4
4
|
const meta = {
|
|
5
5
|
title: 'Form/Radio',
|
|
6
6
|
component: Radio,
|
|
7
|
-
tags: ['autodocs'],
|
|
8
7
|
} satisfies Meta<typeof Radio>;
|
|
9
8
|
|
|
10
9
|
export default meta;
|
|
@@ -12,14 +11,15 @@ type Story = StoryObj<typeof meta>;
|
|
|
12
11
|
|
|
13
12
|
export const Default: Story = { args: { label: 'Option A', name: 'choice', value: 'a' } };
|
|
14
13
|
export const Selected: Story = { args: { label: 'Selected', name: 'choice2', value: 'b', defaultChecked: true } };
|
|
14
|
+
export const WithDescription: Story = { args: { label: 'Option A', name: 'choice5', value: 'a', description: 'Additional context about this option.' } };
|
|
15
15
|
export const WithError: Story = { args: { label: 'Required', name: 'choice3', value: 'c', error: 'Please select an option' } };
|
|
16
16
|
export const WithHint: Story = { args: { label: 'Option', name: 'choice4', value: 'd', hint: 'Choose carefully' } };
|
|
17
17
|
export const Group: Story = {
|
|
18
18
|
render: () => (
|
|
19
|
-
<fieldset>
|
|
20
|
-
<legend>Pick one</legend>
|
|
21
|
-
<Radio label="Option A" name="group" value="a" />
|
|
22
|
-
<Radio label="Option B" name="group" value="b" />
|
|
19
|
+
<fieldset style={{ border: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
|
|
20
|
+
<legend style={{ fontWeight: 'var(--font_weight--semibold)', marginBottom: '0.5rem' }}>Pick one</legend>
|
|
21
|
+
<Radio label="Option A" name="group" value="a" description="Short description for option A." />
|
|
22
|
+
<Radio label="Option B" name="group" value="b" description="Short description for option B." />
|
|
23
23
|
<Radio label="Option C" name="group" value="c" />
|
|
24
24
|
</fieldset>
|
|
25
25
|
),
|
|
@@ -8,12 +8,13 @@ import { cn } from '@boostdev/design-system-foundation';
|
|
|
8
8
|
interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
9
9
|
label: string;
|
|
10
10
|
name: string;
|
|
11
|
+
description?: string;
|
|
11
12
|
error?: string;
|
|
12
13
|
hint?: string;
|
|
13
14
|
className?: string;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export function Radio({ label, name, error, hint, className, ...props }: RadioProps) {
|
|
17
|
+
export function Radio({ label, name, description, error, hint, className, ...props }: RadioProps) {
|
|
17
18
|
const id = name + useId();
|
|
18
19
|
const hintId = id + 'hint';
|
|
19
20
|
const errorId = id + 'error';
|
|
@@ -31,7 +32,10 @@ export function Radio({ label, name, error, hint, className, ...props }: RadioPr
|
|
|
31
32
|
className={cn(css.radio, error && css.radioError)}
|
|
32
33
|
{...props}
|
|
33
34
|
/>
|
|
34
|
-
<
|
|
35
|
+
<div className={css.textWrapper}>
|
|
36
|
+
<Label id={id} label={label} />
|
|
37
|
+
{description && <span className={css.description}>{description}</span>}
|
|
38
|
+
</div>
|
|
35
39
|
</div>
|
|
36
40
|
<Message inputId={id} type="error" message={error} />
|
|
37
41
|
<Message inputId={id} type="hint" message={hint} />
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Select.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Select
|
|
7
|
+
|
|
8
|
+
Native dropdown for choosing one option from a list. Renders a styled `<select>` element.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- 5+ mutually exclusive options
|
|
12
|
+
- When space is constrained and a radio group would be too tall
|
|
13
|
+
|
|
14
|
+
## When not to use
|
|
15
|
+
- Fewer than 5 options — prefer `Radio` buttons for better scannability
|
|
16
|
+
- Searchable or async-loaded options — use `Combobox`
|
|
17
|
+
|
|
18
|
+
## Examples
|
|
19
|
+
|
|
20
|
+
### Default
|
|
21
|
+
<Canvas of={Stories.Default} />
|
|
22
|
+
|
|
23
|
+
### With placeholder
|
|
24
|
+
<Canvas of={Stories.WithPlaceholder} />
|
|
25
|
+
|
|
26
|
+
### With hint
|
|
27
|
+
<Canvas of={Stories.WithHint} />
|
|
28
|
+
|
|
29
|
+
### With error
|
|
30
|
+
<Canvas of={Stories.WithError} />
|
|
31
|
+
|
|
32
|
+
### Disabled
|
|
33
|
+
<Canvas of={Stories.Disabled} />
|
|
34
|
+
|
|
35
|
+
## Props
|
|
36
|
+
|
|
37
|
+
<ArgTypes of={Stories} />
|
|
38
|
+
|
|
39
|
+
## CSS variables
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
<table>
|
|
43
|
+
<thead>
|
|
44
|
+
<tr><th>Variable</th><th>Description</th></tr>
|
|
45
|
+
</thead>
|
|
46
|
+
<tbody>
|
|
47
|
+
<tr><td>`--select_border-color`</td><td>Border colour</td></tr>
|
|
48
|
+
<tr><td>`--select_color`</td><td>Background colour</td></tr>
|
|
49
|
+
<tr><td>`--select_on-color`</td><td>Text colour</td></tr>
|
|
50
|
+
</tbody>
|
|
51
|
+
</table>
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Accessibility
|
|
55
|
+
|
|
56
|
+
- Native `<select>` — keyboard, screen reader, and mobile support built-in
|
|
57
|
+
- Label always associated via `id`/`htmlFor`
|
|
58
|
+
- Error message linked via `aria-describedby`
|