@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,73 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Button.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Button
|
|
7
|
+
|
|
8
|
+
Primary interactive element for triggering actions. Renders as `<button>` or `<a>` depending on whether `href` is provided.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- The main action on a page or dialog (`primary`)
|
|
12
|
+
- Secondary or destructive actions (`secondary`)
|
|
13
|
+
- Navigation that should look like an action (`href` → renders as `<a>`)
|
|
14
|
+
|
|
15
|
+
## When not to use
|
|
16
|
+
- Navigation to another page when it reads naturally as a link — use `Link` instead
|
|
17
|
+
- When the action is purely icon-based without a label — ensure `aria-label` is set
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### All variants & sizes
|
|
22
|
+
<Canvas of={Stories.AllVariants} />
|
|
23
|
+
|
|
24
|
+
### Primary
|
|
25
|
+
<Canvas of={Stories.Primary} />
|
|
26
|
+
|
|
27
|
+
### Secondary
|
|
28
|
+
<Canvas of={Stories.Secondary} />
|
|
29
|
+
|
|
30
|
+
### Sizes
|
|
31
|
+
<Canvas of={Stories.Small} />
|
|
32
|
+
<Canvas of={Stories.Medium} />
|
|
33
|
+
<Canvas of={Stories.Large} />
|
|
34
|
+
|
|
35
|
+
### As a link
|
|
36
|
+
<Canvas of={Stories.AsLink} />
|
|
37
|
+
|
|
38
|
+
### With icons
|
|
39
|
+
<Canvas of={Stories.WithIcons} />
|
|
40
|
+
|
|
41
|
+
### Pulse (CTA emphasis)
|
|
42
|
+
<Canvas of={Stories.WithPulse} />
|
|
43
|
+
|
|
44
|
+
### Disabled
|
|
45
|
+
<Canvas of={Stories.Disabled} />
|
|
46
|
+
|
|
47
|
+
## Props
|
|
48
|
+
|
|
49
|
+
<ArgTypes of={Stories} />
|
|
50
|
+
|
|
51
|
+
## CSS variables
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
<table>
|
|
55
|
+
<thead>
|
|
56
|
+
<tr><th>Variable</th><th>Description</th></tr>
|
|
57
|
+
</thead>
|
|
58
|
+
<tbody>
|
|
59
|
+
<tr><td>`--button_bg`</td><td>Background colour</td></tr>
|
|
60
|
+
<tr><td>`--button_color`</td><td>Text colour</td></tr>
|
|
61
|
+
<tr><td>`--button_border-color`</td><td>Border colour</td></tr>
|
|
62
|
+
<tr><td>`--button_bg--hover`</td><td>Background on hover</td></tr>
|
|
63
|
+
<tr><td>`--button_color--hover`</td><td>Text colour on hover</td></tr>
|
|
64
|
+
</tbody>
|
|
65
|
+
</table>
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## Accessibility
|
|
69
|
+
|
|
70
|
+
- Always provide children or `aria-label` for icon-only buttons
|
|
71
|
+
- Disabled state sets `aria-disabled="true"` and removes the element from the tab order
|
|
72
|
+
- When rendered as `<a>`, `href` must point to a valid destination
|
|
73
|
+
- `hasPulse` animation respects `prefers-reduced-motion`
|
|
@@ -1,55 +1,45 @@
|
|
|
1
1
|
@keyframes pulse {
|
|
2
2
|
0% {
|
|
3
3
|
transform: scale(1);
|
|
4
|
-
box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color) r g b / 50%);
|
|
4
|
+
box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color, var(--color_cta)) r g b / 50%);
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
70% {
|
|
8
8
|
transform: scale(1.01);
|
|
9
|
-
box-shadow: 0 0 0 var(--space_m) rgb(from var(--button_pulse-color) r g b / 0%);
|
|
9
|
+
box-shadow: 0 0 0 var(--space_m) rgb(from var(--button_pulse-color, var(--color_cta)) r g b / 0%);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
100% {
|
|
13
13
|
transform: scale(1);
|
|
14
|
-
box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color) r g b / 0%);
|
|
14
|
+
box-shadow: 0 0 0 0 rgb(from var(--button_pulse-color, var(--color_cta)) r g b / 0%);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
@layer component {
|
|
19
19
|
.button,
|
|
20
20
|
.button[href] {
|
|
21
|
-
/* Component tokens — override from a parent context or inline style */
|
|
22
|
-
--button_height: 3em;
|
|
23
|
-
--button_font-size: inherit;
|
|
24
|
-
--button_bg: var(--color_cta);
|
|
25
|
-
--button_text: var(--color_on-cta);
|
|
26
|
-
--button_border-color: var(--color_cta);
|
|
27
|
-
--button_pulse-color: var(--button_bg);
|
|
28
|
-
|
|
29
21
|
all: unset;
|
|
30
22
|
position: relative;
|
|
31
23
|
font: inherit;
|
|
32
24
|
padding-inline: var(--space_l);
|
|
33
25
|
display: inline-flex;
|
|
34
26
|
gap: var(--space_xs);
|
|
35
|
-
border-radius: var(--border_radius--s);
|
|
27
|
+
border-radius: var(--button_border_radius--s, var(--border_radius--s));
|
|
36
28
|
align-items: center;
|
|
37
29
|
cursor: pointer;
|
|
38
30
|
scroll-padding-block-end: var(--space_l);
|
|
39
31
|
justify-content: center;
|
|
40
|
-
height: var(--button_height);
|
|
41
|
-
font-size: var(--button_font-size);
|
|
32
|
+
height: var(--button_height, 3em);
|
|
33
|
+
font-size: var(--button_font-size, inherit);
|
|
42
34
|
white-space: nowrap;
|
|
43
35
|
line-height: 1;
|
|
44
|
-
background-color: var(--button_bg);
|
|
45
|
-
color: var(--button_text);
|
|
46
|
-
border: 2px solid var(--button_border-color);
|
|
36
|
+
background-color: var(--button_bg, var(--color_cta));
|
|
37
|
+
color: var(--button_text, var(--color_on-cta));
|
|
38
|
+
border: 2px solid var(--button_border-color, var(--color_cta));
|
|
47
39
|
transition: var(--animation_transition);
|
|
48
40
|
}
|
|
49
41
|
|
|
50
42
|
.button.--primary {
|
|
51
|
-
--button_bg: var(--color_cta);
|
|
52
|
-
--button_text: var(--color_on-cta);
|
|
53
43
|
--button_border-color: transparent;
|
|
54
44
|
}
|
|
55
45
|
|
|
@@ -62,6 +52,7 @@
|
|
|
62
52
|
.button.--size_small {
|
|
63
53
|
--button_height: 2.25em;
|
|
64
54
|
--button_font-size: var(--font_size--body--s);
|
|
55
|
+
--button_border_radius--s: var(--border_radius--xs);
|
|
65
56
|
|
|
66
57
|
padding-inline: var(--space_m);
|
|
67
58
|
}
|
|
@@ -73,10 +64,11 @@
|
|
|
73
64
|
}
|
|
74
65
|
|
|
75
66
|
.button.--size_large {
|
|
76
|
-
--button_height: 3.75em;
|
|
77
67
|
--button_font-size: var(--font_size--heading-3);
|
|
78
68
|
|
|
79
69
|
padding-inline: var(--space_xl);
|
|
70
|
+
--button_border_radius--s: var(--border_radius--m);
|
|
71
|
+
|
|
80
72
|
}
|
|
81
73
|
|
|
82
74
|
.button.--hasPulse {
|
|
@@ -4,7 +4,6 @@ import { Button } from './Button';
|
|
|
4
4
|
const meta = {
|
|
5
5
|
title: 'Interaction/Button',
|
|
6
6
|
component: Button,
|
|
7
|
-
tags: ['autodocs'],
|
|
8
7
|
argTypes: {
|
|
9
8
|
variant: { control: 'select', options: ['primary', 'secondary'] },
|
|
10
9
|
size: { control: 'select', options: ['small', 'medium', 'large'] },
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Command.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Command
|
|
7
|
+
|
|
8
|
+
Keyboard-first command palette for searching and executing actions. Commonly triggered by `⌘K`.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Applications with many actions that power users access via keyboard
|
|
12
|
+
- Global search combining navigation, actions, and settings
|
|
13
|
+
|
|
14
|
+
## Examples
|
|
15
|
+
|
|
16
|
+
### Default
|
|
17
|
+
<Canvas of={Stories.Default} />
|
|
18
|
+
|
|
19
|
+
## Props
|
|
20
|
+
|
|
21
|
+
<ArgTypes of={Stories} />
|
|
22
|
+
|
|
23
|
+
## Accessibility
|
|
24
|
+
|
|
25
|
+
- Input has `role="combobox"` with `aria-controls` pointing to the results list
|
|
26
|
+
- Results list uses `role="listbox"` with `role="option"` items
|
|
27
|
+
- `aria-activedescendant` tracks the highlighted option
|
|
28
|
+
- Keyboard: `↑`/`↓` to navigate, `Enter` to execute, `Escape` to dismiss
|
|
@@ -2,11 +2,11 @@ import { useState } from 'react';
|
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
3
|
import { Command } from './Command';
|
|
4
4
|
import type { CommandItem } from './Command';
|
|
5
|
+
import { Button } from '../Button/Button';
|
|
5
6
|
|
|
6
7
|
const meta = {
|
|
7
8
|
title: 'Interaction/Command',
|
|
8
9
|
component: Command,
|
|
9
|
-
tags: ['autodocs'],
|
|
10
10
|
} satisfies Meta<typeof Command>;
|
|
11
11
|
|
|
12
12
|
export default meta;
|
|
@@ -25,9 +25,9 @@ export const Default: Story = {
|
|
|
25
25
|
const [open, setOpen] = useState(false);
|
|
26
26
|
return (
|
|
27
27
|
<>
|
|
28
|
-
<
|
|
28
|
+
<Button onClick={() => setOpen(true)}>
|
|
29
29
|
Open Command Palette <kbd>⌘K</kbd>
|
|
30
|
-
</
|
|
30
|
+
</Button>
|
|
31
31
|
<Command isOpen={open} items={items} onClose={() => setOpen(false)} />
|
|
32
32
|
</>
|
|
33
33
|
);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Dialog.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Dialog
|
|
7
|
+
|
|
8
|
+
Modal overlay using the native `<dialog>` element. Traps focus inside the dialog while open and renders a dimmed, blurred backdrop.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Confirmations, alerts, and short focused tasks that need the user's attention before continuing
|
|
12
|
+
- Forms that are secondary to the main page flow
|
|
13
|
+
|
|
14
|
+
## When not to use
|
|
15
|
+
- Complex workflows with many steps — consider a dedicated page or a `Drawer`
|
|
16
|
+
- Non-critical information — use a `Popover` or inline content instead
|
|
17
|
+
|
|
18
|
+
## Examples
|
|
19
|
+
|
|
20
|
+
### Default (open)
|
|
21
|
+
<Canvas of={Stories.Default} />
|
|
22
|
+
|
|
23
|
+
### Interactive (controlled)
|
|
24
|
+
<Canvas of={Stories.Interactive} />
|
|
25
|
+
|
|
26
|
+
## Props
|
|
27
|
+
|
|
28
|
+
<ArgTypes of={Stories} />
|
|
29
|
+
|
|
30
|
+
## CSS variables
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
<table>
|
|
34
|
+
<thead>
|
|
35
|
+
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
36
|
+
</thead>
|
|
37
|
+
<tbody>
|
|
38
|
+
<tr><td>`--dialog_color`</td><td>`var(--color_grey--subtle)`</td><td>Dialog background</td></tr>
|
|
39
|
+
<tr><td>`--dialog_on-color`</td><td>`var(--color_on-grey--subtle)`</td><td>Dialog text colour</td></tr>
|
|
40
|
+
<tr><td>`--dialog_backdrop-color`</td><td>`rgb(0 0 0 / 50%)`</td><td>Backdrop overlay colour</td></tr>
|
|
41
|
+
</tbody>
|
|
42
|
+
</table>
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Behaviour
|
|
46
|
+
|
|
47
|
+
- Opens via `showModal()` — provides native focus trapping and `Escape` key to close
|
|
48
|
+
- Backdrop click calls `onClose`
|
|
49
|
+
- Body scroll is locked while open (`overflow: hidden`)
|
|
50
|
+
- `onClose` is also called on `Escape` via the native `cancel` event
|
|
51
|
+
|
|
52
|
+
## Accessibility
|
|
53
|
+
|
|
54
|
+
- Native `<dialog>` provides `role="dialog"` and `aria-modal="true"` automatically
|
|
55
|
+
- Focus is moved to the first focusable element inside on open
|
|
56
|
+
- `Escape` key closes the dialog
|
|
57
|
+
- Add `aria-labelledby` pointing to the dialog title for screen readers
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Drawer.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Drawer
|
|
7
|
+
|
|
8
|
+
Slide-in panel from the left or right edge of the viewport. Use for navigation, filters, or secondary task flows that should not interrupt the main content.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Mobile navigation panels
|
|
12
|
+
- Filter sidebars in data-heavy views
|
|
13
|
+
- Multi-step flows that are secondary to the main page
|
|
14
|
+
|
|
15
|
+
## When not to use
|
|
16
|
+
- Simple confirmations or short prompts — use `Dialog`
|
|
17
|
+
- Content that always needs to be visible — use a sidebar layout instead
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### Left
|
|
22
|
+
<Canvas of={Stories.Left} />
|
|
23
|
+
|
|
24
|
+
### Right
|
|
25
|
+
<Canvas of={Stories.Right} />
|
|
26
|
+
|
|
27
|
+
## Props
|
|
28
|
+
|
|
29
|
+
<ArgTypes of={Stories} />
|
|
30
|
+
|
|
31
|
+
## Accessibility
|
|
32
|
+
|
|
33
|
+
- Uses `<dialog>` internally — provides native focus trapping and `Escape` to close
|
|
34
|
+
- `aria-label` on the dialog describes the drawer's purpose
|
|
35
|
+
- Body scroll locked while open
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
.drawer::backdrop {
|
|
17
|
-
background: rgb(0 0 0 / 50%);
|
|
17
|
+
background-color: var(--drawer_backdrop-color, rgb(0 0 0 / 50%));
|
|
18
|
+
backdrop-filter: blur(3px);
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
.panel {
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
color: var(--drawer_on-color, var(--color_on-bg));
|
|
29
30
|
box-shadow: var(--shadow_xl);
|
|
30
31
|
overflow: hidden;
|
|
31
|
-
transition:
|
|
32
|
+
transition: var(--animation_transition);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
.--side_right .panel {
|
|
@@ -48,13 +49,6 @@
|
|
|
48
49
|
flex-shrink: 0;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
.title {
|
|
52
|
-
margin: 0;
|
|
53
|
-
font-size: var(--font_size--heading--s);
|
|
54
|
-
font-weight: var(--font_weight--semibold);
|
|
55
|
-
line-height: var(--font_line-height--heading);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
52
|
.closeButton {
|
|
59
53
|
all: unset;
|
|
60
54
|
display: flex;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
3
|
import { Drawer } from './Drawer';
|
|
4
|
+
import { Button } from '../Button/Button';
|
|
4
5
|
|
|
5
6
|
const meta = {
|
|
6
7
|
title: 'Interaction/Drawer',
|
|
7
8
|
component: Drawer,
|
|
8
|
-
tags: ['autodocs'],
|
|
9
9
|
} satisfies Meta<typeof Drawer>;
|
|
10
10
|
|
|
11
11
|
export default meta;
|
|
@@ -16,7 +16,7 @@ export const Right: Story = {
|
|
|
16
16
|
const [open, setOpen] = useState(false);
|
|
17
17
|
return (
|
|
18
18
|
<>
|
|
19
|
-
<
|
|
19
|
+
<Button onClick={() => setOpen(true)}>Open right drawer</Button>
|
|
20
20
|
<Drawer isOpen={open} onClose={() => setOpen(false)} title="Settings" side="right">
|
|
21
21
|
<p>Drawer content goes here. You can put any React content inside.</p>
|
|
22
22
|
</Drawer>
|
|
@@ -30,7 +30,7 @@ export const Left: Story = {
|
|
|
30
30
|
const [open, setOpen] = useState(false);
|
|
31
31
|
return (
|
|
32
32
|
<>
|
|
33
|
-
<
|
|
33
|
+
<Button onClick={() => setOpen(true)}>Open left drawer</Button>
|
|
34
34
|
<Drawer isOpen={open} onClose={() => setOpen(false)} title="Navigation" side="left">
|
|
35
35
|
<nav>
|
|
36
36
|
<ul>
|
|
@@ -5,10 +5,11 @@ import { cn } from '@boostdev/design-system-foundation';
|
|
|
5
5
|
interface DrawerProps {
|
|
6
6
|
isOpen: boolean;
|
|
7
7
|
onClose: () => void;
|
|
8
|
-
title:
|
|
8
|
+
title: ReactNode;
|
|
9
9
|
children: ReactNode;
|
|
10
10
|
side?: 'left' | 'right';
|
|
11
11
|
className?: string;
|
|
12
|
+
arialLabel?: string;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export function Drawer({
|
|
@@ -17,6 +18,7 @@ export function Drawer({
|
|
|
17
18
|
title,
|
|
18
19
|
children,
|
|
19
20
|
side = 'right',
|
|
21
|
+
arialLabel = '',
|
|
20
22
|
className,
|
|
21
23
|
}: Readonly<DrawerProps>) {
|
|
22
24
|
const dialogRef = useRef<HTMLDialogElement>(null);
|
|
@@ -46,13 +48,13 @@ export function Drawer({
|
|
|
46
48
|
<dialog
|
|
47
49
|
ref={dialogRef}
|
|
48
50
|
className={cn(css.drawer, css[`--side_${side}`], className)}
|
|
49
|
-
aria-label={
|
|
51
|
+
aria-label={arialLabel}
|
|
50
52
|
onClick={handleClick}
|
|
51
53
|
onCancel={handleCancel}
|
|
52
54
|
>
|
|
53
55
|
<div className={css.panel}>
|
|
54
56
|
<div className={css.header}>
|
|
55
|
-
|
|
57
|
+
{!!title && title}
|
|
56
58
|
<button
|
|
57
59
|
type="button"
|
|
58
60
|
className={css.closeButton}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './DropdownMenu.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# DropdownMenu
|
|
7
|
+
|
|
8
|
+
Floating menu with a list of selectable actions, triggered by a button. Supports disabled items and separators.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Overflow actions in a toolbar or table row
|
|
12
|
+
- Context menus on cards or list items
|
|
13
|
+
|
|
14
|
+
## When not to use
|
|
15
|
+
- Navigation — use a `<nav>` with links
|
|
16
|
+
- More than ~8 items — consider a `Command` palette or a `Select`
|
|
17
|
+
|
|
18
|
+
## Examples
|
|
19
|
+
|
|
20
|
+
### Default
|
|
21
|
+
<Canvas of={Stories.Default} />
|
|
22
|
+
|
|
23
|
+
### With separator
|
|
24
|
+
<Canvas of={Stories.WithSeparator} />
|
|
25
|
+
|
|
26
|
+
### With disabled item
|
|
27
|
+
<Canvas of={Stories.WithDisabledItem} />
|
|
28
|
+
|
|
29
|
+
### Aligned end
|
|
30
|
+
<Canvas of={Stories.AlignedEnd} />
|
|
31
|
+
|
|
32
|
+
## Props
|
|
33
|
+
|
|
34
|
+
<ArgTypes of={Stories} />
|
|
35
|
+
|
|
36
|
+
## Accessibility
|
|
37
|
+
|
|
38
|
+
- Implements ARIA menu pattern: `role="menu"`, `role="menuitem"`
|
|
39
|
+
- Arrow keys navigate items; `Enter`/`Space` activate; `Escape` closes
|
|
40
|
+
- Disabled items have `aria-disabled="true"` and are skipped during keyboard navigation
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { DropdownMenu } from './DropdownMenu';
|
|
3
|
+
import {Button} from "../Button";
|
|
3
4
|
|
|
4
5
|
const meta = {
|
|
5
6
|
title: 'Interaction/DropdownMenu',
|
|
6
7
|
component: DropdownMenu,
|
|
7
|
-
tags: ['autodocs'],
|
|
8
8
|
decorators: [
|
|
9
9
|
Story => (
|
|
10
10
|
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'center', padding: '2rem' }}>
|
|
@@ -26,14 +26,14 @@ export const Default: Story = {
|
|
|
26
26
|
],
|
|
27
27
|
},
|
|
28
28
|
render: args => (
|
|
29
|
-
<DropdownMenu {...args} trigger={<
|
|
29
|
+
<DropdownMenu {...args} trigger={<Button variant="secondary">Actions ▾</Button>} />
|
|
30
30
|
),
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
export const WithDisabledItem: Story = {
|
|
34
34
|
render: () => (
|
|
35
35
|
<DropdownMenu
|
|
36
|
-
trigger={<
|
|
36
|
+
trigger={<Button variant="secondary">Options ▾</Button>}
|
|
37
37
|
items={[
|
|
38
38
|
{ id: 'view', label: 'View' },
|
|
39
39
|
{ id: 'edit', label: 'Edit' },
|
|
@@ -46,7 +46,7 @@ export const WithDisabledItem: Story = {
|
|
|
46
46
|
export const WithSeparator: Story = {
|
|
47
47
|
render: () => (
|
|
48
48
|
<DropdownMenu
|
|
49
|
-
trigger={<
|
|
49
|
+
trigger={<Button variant="secondary">File ▾</Button>}
|
|
50
50
|
items={[
|
|
51
51
|
{ id: 'new', label: 'New' },
|
|
52
52
|
{ id: 'open', label: 'Open' },
|
|
@@ -60,7 +60,7 @@ export const WithSeparator: Story = {
|
|
|
60
60
|
export const AlignedEnd: Story = {
|
|
61
61
|
render: () => (
|
|
62
62
|
<DropdownMenu
|
|
63
|
-
trigger={<
|
|
63
|
+
trigger={<Button variant="secondary">⋮</Button>}
|
|
64
64
|
items={[{ id: 'copy', label: 'Copy link' }, { id: 'report', label: 'Report' }]}
|
|
65
65
|
placement="bottom-end"
|
|
66
66
|
/>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Popover.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Popover
|
|
7
|
+
|
|
8
|
+
Floating panel anchored to a trigger element. Unlike `Tooltip`, popovers can contain interactive content (forms, links, buttons).
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Filter panels, date pickers, or mini-forms attached to a trigger
|
|
12
|
+
- Rich contextual details that require user interaction
|
|
13
|
+
|
|
14
|
+
## When not to use
|
|
15
|
+
- Non-interactive supplementary text — use `Tooltip` instead
|
|
16
|
+
- Full-screen overlays — use `Dialog`
|
|
17
|
+
|
|
18
|
+
## Examples
|
|
19
|
+
|
|
20
|
+
### Default
|
|
21
|
+
<Canvas of={Stories.Default} />
|
|
22
|
+
|
|
23
|
+
### With form
|
|
24
|
+
<Canvas of={Stories.WithForm} />
|
|
25
|
+
|
|
26
|
+
## Props
|
|
27
|
+
|
|
28
|
+
<ArgTypes of={Stories} />
|
|
29
|
+
|
|
30
|
+
## Accessibility
|
|
31
|
+
|
|
32
|
+
- Trigger element gets `aria-haspopup="true"` and `aria-expanded` toggled on open/close
|
|
33
|
+
- Popover panel is linked via `aria-controls`
|
|
34
|
+
- Focus moves into the panel on open; `Escape` closes and returns focus to trigger
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { Popover } from './Popover';
|
|
3
|
+
import { Button } from '../Button/Button';
|
|
3
4
|
|
|
4
5
|
const meta = {
|
|
5
6
|
title: 'Interaction/Popover',
|
|
6
7
|
component: Popover,
|
|
7
|
-
tags: ['autodocs'],
|
|
8
8
|
argTypes: {
|
|
9
9
|
placement: { control: 'radio', options: ['top', 'bottom', 'left', 'right'] },
|
|
10
10
|
},
|
|
@@ -24,7 +24,7 @@ export const Default: Story = {
|
|
|
24
24
|
args: { placement: 'bottom', content: <p style={{ margin: 0 }}>This is a popover with rich content.</p> },
|
|
25
25
|
render: args => (
|
|
26
26
|
<Popover {...args}>
|
|
27
|
-
<
|
|
27
|
+
<Button>Open popover</Button>
|
|
28
28
|
</Popover>
|
|
29
29
|
),
|
|
30
30
|
};
|
|
@@ -41,7 +41,7 @@ export const WithForm: Story = {
|
|
|
41
41
|
</div>
|
|
42
42
|
}
|
|
43
43
|
>
|
|
44
|
-
<
|
|
44
|
+
<Button variant="secondary">Filters</Button>
|
|
45
45
|
</Popover>
|
|
46
46
|
),
|
|
47
47
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Rating.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Rating
|
|
7
|
+
|
|
8
|
+
Read-only star rating display. Use to show an existing score; for user input, combine with a form input or interactive stars.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- Displaying product or review scores
|
|
12
|
+
- Showing ratings in cards or lists
|
|
13
|
+
|
|
14
|
+
## Examples
|
|
15
|
+
|
|
16
|
+
### Three stars (default)
|
|
17
|
+
<Canvas of={Stories.ThreeStars} />
|
|
18
|
+
|
|
19
|
+
### Full stars
|
|
20
|
+
<Canvas of={Stories.FullStars} />
|
|
21
|
+
|
|
22
|
+
### Empty
|
|
23
|
+
<Canvas of={Stories.Empty} />
|
|
24
|
+
|
|
25
|
+
### Custom max (out of 10)
|
|
26
|
+
<Canvas of={Stories.CustomMax} />
|
|
27
|
+
|
|
28
|
+
### All ratings
|
|
29
|
+
<Canvas of={Stories.AllRatings} />
|
|
30
|
+
|
|
31
|
+
## Props
|
|
32
|
+
|
|
33
|
+
<ArgTypes of={Stories} />
|
|
34
|
+
|
|
35
|
+
## Accessibility
|
|
36
|
+
|
|
37
|
+
- Renders as a `<span>` with `role="img"` and `aria-label="X out of Y stars"`
|
|
38
|
+
- Stars are `aria-hidden` — the label carries the full meaning
|