@boostdev/design-system-components 1.0.3 → 1.1.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 +51 -10
- package/dist/client.cjs +350 -174
- package/dist/client.css +751 -647
- package/dist/client.d.cts +62 -97
- package/dist/client.d.ts +62 -97
- package/dist/client.js +377 -211
- package/dist/index.cjs +350 -174
- package/dist/index.css +751 -647
- package/dist/index.d.cts +62 -97
- package/dist/index.d.ts +62 -97
- package/dist/index.js +377 -211
- package/dist/native/index.cjs +991 -0
- package/dist/native/index.d.cts +208 -0
- package/dist/native/index.d.ts +208 -0
- package/dist/native/index.js +968 -0
- package/dist/web-components.d.ts +304 -0
- package/dist/web-components.js +1978 -0
- package/package.json +27 -5
- package/src/components/interaction/Button/Button.module.css +11 -8
- package/src/components/interaction/Button/Button.native.mdx +72 -0
- package/src/components/interaction/Button/Button.native.spec.tsx +35 -0
- package/src/components/interaction/Button/Button.native.stories.tsx +42 -0
- package/src/components/interaction/Button/Button.native.tsx +95 -0
- package/src/components/interaction/Button/Button.stories.tsx +1 -1
- package/src/components/interaction/Button/Button.tsx +2 -2
- package/src/components/interaction/Command/Command.mdx +16 -0
- package/src/components/interaction/Command/Command.module.css +7 -7
- package/src/components/interaction/Command/Command.spec.tsx +37 -0
- package/src/components/interaction/Command/Command.stories.tsx +1 -1
- package/src/components/interaction/Command/Command.tsx +39 -4
- package/src/components/interaction/Dialog/Dialog.module.css +10 -13
- package/src/components/interaction/Dialog/Dialog.spec.tsx +53 -1
- package/src/components/interaction/Dialog/Dialog.stories.tsx +18 -1
- package/src/components/interaction/Dialog/Dialog.tsx +52 -18
- package/src/components/interaction/Drawer/Drawer.mdx +14 -0
- package/src/components/interaction/Drawer/Drawer.module.css +11 -11
- package/src/components/interaction/Drawer/Drawer.spec.tsx +48 -1
- package/src/components/interaction/Drawer/Drawer.stories.tsx +1 -1
- package/src/components/interaction/Drawer/Drawer.tsx +40 -6
- package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +6 -6
- package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +1 -1
- package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +2 -2
- package/src/components/interaction/Popover/Popover.mdx +17 -0
- package/src/components/interaction/Popover/Popover.module.css +132 -16
- package/src/components/interaction/Popover/Popover.spec.tsx +119 -20
- package/src/components/interaction/Popover/Popover.stories.tsx +1 -1
- package/src/components/interaction/Popover/Popover.tsx +69 -29
- package/src/components/interaction/Rating/Rating.stories.tsx +1 -1
- package/src/components/interaction/Rating/Rating.tsx +2 -2
- package/src/components/interaction/Toast/Toast.module.css +13 -8
- package/src/components/interaction/Toast/Toast.spec.tsx +21 -0
- package/src/components/interaction/Toast/Toast.stories.tsx +1 -1
- package/src/components/interaction/Toast/Toast.tsx +22 -2
- package/src/components/interaction/form/Checkbox/Checkbox.mdx +2 -2
- package/src/components/interaction/form/Checkbox/Checkbox.module.css +9 -9
- package/src/components/interaction/form/Checkbox/Checkbox.native.mdx +74 -0
- package/src/components/interaction/form/Checkbox/Checkbox.native.spec.tsx +42 -0
- package/src/components/interaction/form/Checkbox/Checkbox.native.stories.tsx +36 -0
- package/src/components/interaction/form/Checkbox/Checkbox.native.tsx +80 -0
- package/src/components/interaction/form/Checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/interaction/form/Checkbox/Checkbox.tsx +2 -2
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +44 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.stories.tsx +1 -1
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +2 -2
- package/src/components/interaction/form/Combobox/Combobox.mdx +21 -0
- package/src/components/interaction/form/Combobox/Combobox.module.css +8 -9
- package/src/components/interaction/form/Combobox/Combobox.stories.tsx +1 -1
- package/src/components/interaction/form/Combobox/Combobox.tsx +2 -2
- package/src/components/interaction/form/FileInput/FileInput.mdx +14 -0
- package/src/components/interaction/form/FileInput/FileInput.module.css +5 -5
- package/src/components/interaction/form/FileInput/FileInput.stories.tsx +1 -1
- package/src/components/interaction/form/FileInput/FileInput.tsx +2 -2
- package/src/components/interaction/form/FormInput/FormInput.stories.tsx +1 -1
- package/src/components/interaction/form/FormInput/FormInput.tsx +2 -2
- package/src/components/interaction/form/NumberInput/NumberInput.mdx +16 -0
- package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -1
- package/src/components/interaction/form/NumberInput/NumberInput.stories.tsx +1 -1
- package/src/components/interaction/form/NumberInput/NumberInput.tsx +2 -2
- package/src/components/interaction/form/Radio/Radio.mdx +3 -3
- package/src/components/interaction/form/Radio/Radio.module.css +9 -9
- package/src/components/interaction/form/Radio/Radio.native.stories.tsx +46 -0
- package/src/components/interaction/form/Radio/Radio.native.tsx +79 -0
- package/src/components/interaction/form/Radio/Radio.stories.tsx +1 -1
- package/src/components/interaction/form/Radio/Radio.tsx +2 -2
- package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +45 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.stories.tsx +1 -1
- package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +2 -2
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +5 -6
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.stories.tsx +1 -1
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +2 -2
- package/src/components/interaction/form/Select/Select.module.css +3 -3
- package/src/components/interaction/form/Select/Select.stories.tsx +1 -1
- package/src/components/interaction/form/Select/Select.tsx +2 -2
- package/src/components/interaction/form/Slider/Slider.mdx +1 -1
- package/src/components/interaction/form/Slider/Slider.module.css +10 -10
- package/src/components/interaction/form/Slider/Slider.stories.tsx +1 -1
- package/src/components/interaction/form/Slider/Slider.tsx +2 -2
- package/src/components/interaction/form/Switch/Switch.mdx +4 -4
- package/src/components/interaction/form/Switch/Switch.module.css +11 -11
- package/src/components/interaction/form/Switch/Switch.native.spec.tsx +60 -0
- package/src/components/interaction/form/Switch/Switch.native.stories.tsx +35 -0
- package/src/components/interaction/form/Switch/Switch.native.tsx +59 -0
- package/src/components/interaction/form/Switch/Switch.stories.tsx +1 -1
- package/src/components/interaction/form/Switch/Switch.tsx +2 -2
- package/src/components/interaction/form/Textarea/Textarea.mdx +17 -0
- package/src/components/interaction/form/Textarea/Textarea.module.css +1 -1
- package/src/components/interaction/form/Textarea/Textarea.stories.tsx +1 -1
- package/src/components/interaction/form/Textarea/Textarea.tsx +2 -2
- package/src/components/interaction/form/atoms/InputContainer.tsx +2 -1
- package/src/components/interaction/form/atoms/Label.native.stories.tsx +18 -0
- package/src/components/interaction/form/atoms/Label.native.tsx +29 -0
- package/src/components/interaction/form/atoms/Label.tsx +5 -3
- package/src/components/interaction/form/atoms/Message.native.spec.tsx +30 -0
- package/src/components/interaction/form/atoms/Message.native.stories.tsx +22 -0
- package/src/components/interaction/form/atoms/Message.native.tsx +34 -0
- package/src/components/interaction/form/atoms/Message.tsx +5 -3
- package/src/components/layout/ButtonGroup/ButtonGroup.native.stories.tsx +50 -0
- package/src/components/layout/ButtonGroup/ButtonGroup.native.tsx +34 -0
- package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +1 -1
- package/src/components/layout/ButtonGroup/ButtonGroup.tsx +2 -2
- package/src/components/layout/Card/Card.native.stories.tsx +53 -0
- package/src/components/layout/Card/Card.native.tsx +89 -0
- package/src/components/layout/Card/Card.stories.tsx +1 -1
- package/src/components/layout/Card/Card.tsx +2 -2
- package/src/components/layout/IconWrapper/IconWrapper.mdx +2 -2
- package/src/components/layout/IconWrapper/IconWrapper.module.css +2 -2
- package/src/components/layout/IconWrapper/IconWrapper.native.spec.tsx +39 -0
- package/src/components/layout/IconWrapper/IconWrapper.native.stories.tsx +41 -0
- package/src/components/layout/IconWrapper/IconWrapper.native.tsx +20 -0
- package/src/components/layout/IconWrapper/IconWrapper.stories.tsx +1 -1
- package/src/components/layout/IconWrapper/IconWrapper.tsx +2 -2
- package/src/components/layout/SectionHeader/SectionHeader.mdx +13 -0
- package/src/components/layout/SectionHeader/SectionHeader.native.stories.tsx +38 -0
- package/src/components/layout/SectionHeader/SectionHeader.native.tsx +79 -0
- package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +1 -1
- package/src/components/layout/SectionHeader/SectionHeader.tsx +2 -2
- package/src/components/ui/Accordion/Accordion.mdx +14 -0
- package/src/components/ui/Accordion/Accordion.module.css +5 -5
- package/src/components/ui/Accordion/Accordion.stories.tsx +1 -1
- package/src/components/ui/Accordion/Accordion.tsx +2 -2
- package/src/components/ui/Alert/Alert.native.mdx +62 -0
- package/src/components/ui/Alert/Alert.native.stories.tsx +43 -0
- package/src/components/ui/Alert/Alert.native.tsx +94 -0
- package/src/components/ui/Alert/Alert.stories.tsx +1 -1
- package/src/components/ui/Alert/Alert.tsx +2 -2
- package/src/components/ui/Avatar/Avatar.native.mdx +50 -0
- package/src/components/ui/Avatar/Avatar.native.spec.tsx +47 -0
- package/src/components/ui/Avatar/Avatar.native.stories.tsx +34 -0
- package/src/components/ui/Avatar/Avatar.native.tsx +85 -0
- package/src/components/ui/Avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/Avatar/Avatar.tsx +2 -2
- package/src/components/ui/Badge/Badge.mdx +2 -2
- package/src/components/ui/Badge/Badge.native.mdx +54 -0
- package/src/components/ui/Badge/Badge.native.stories.tsx +36 -0
- package/src/components/ui/Badge/Badge.native.tsx +50 -0
- package/src/components/ui/Badge/Badge.stories.tsx +1 -1
- package/src/components/ui/Badge/Badge.tsx +2 -2
- package/src/components/ui/Breadcrumb/Breadcrumb.stories.tsx +1 -1
- package/src/components/ui/Breadcrumb/Breadcrumb.tsx +2 -2
- package/src/components/ui/Calendar/Calendar.mdx +16 -0
- package/src/components/ui/Calendar/Calendar.module.css +7 -7
- package/src/components/ui/Calendar/Calendar.stories.tsx +1 -1
- package/src/components/ui/Calendar/Calendar.tsx +2 -2
- package/src/components/ui/Carousel/Carousel.module.css +5 -5
- package/src/components/ui/Carousel/Carousel.stories.tsx +1 -1
- package/src/components/ui/Carousel/Carousel.tsx +2 -2
- package/src/components/ui/Collapsible/Collapsible.module.css +4 -4
- package/src/components/ui/Collapsible/Collapsible.stories.tsx +1 -1
- package/src/components/ui/Collapsible/Collapsible.tsx +2 -2
- package/src/components/ui/DescriptionList/DescriptionList.stories.tsx +1 -1
- package/src/components/ui/DescriptionList/DescriptionList.tsx +2 -2
- package/src/components/ui/Link/Link.mdx +14 -0
- package/src/components/ui/Link/Link.module.css +2 -2
- package/src/components/ui/Link/Link.stories.tsx +1 -1
- package/src/components/ui/Link/Link.tsx +2 -2
- package/src/components/ui/Loading/Loading.module.css +7 -7
- package/src/components/ui/Loading/Loading.native.spec.tsx +24 -0
- package/src/components/ui/Loading/Loading.native.stories.tsx +33 -0
- package/src/components/ui/Loading/Loading.native.tsx +29 -0
- package/src/components/ui/Loading/Loading.stories.tsx +1 -1
- package/src/components/ui/Loading/Loading.tsx +2 -2
- package/src/components/ui/NotificationBanner/NotificationBanner.module.css +3 -3
- package/src/components/ui/NotificationBanner/NotificationBanner.native.stories.tsx +39 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.native.tsx +76 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +1 -1
- package/src/components/ui/NotificationBanner/NotificationBanner.tsx +2 -2
- package/src/components/ui/Pagination/Pagination.module.css +7 -7
- package/src/components/ui/Pagination/Pagination.stories.tsx +1 -1
- package/src/components/ui/Pagination/Pagination.tsx +2 -2
- package/src/components/ui/Progress/Progress.mdx +1 -1
- package/src/components/ui/Progress/Progress.module.css +6 -6
- package/src/components/ui/Progress/Progress.native.stories.tsx +34 -0
- package/src/components/ui/Progress/Progress.native.tsx +84 -0
- package/src/components/ui/Progress/Progress.stories.tsx +1 -1
- package/src/components/ui/Progress/Progress.tsx +2 -2
- package/src/components/ui/ProgressCircle/ProgressCircle.module.css +1 -1
- package/src/components/ui/ProgressCircle/ProgressCircle.stories.tsx +1 -1
- package/src/components/ui/ProgressCircle/ProgressCircle.tsx +2 -2
- package/src/components/ui/Separator/Separator.mdx +14 -0
- package/src/components/ui/Separator/Separator.module.css +3 -3
- package/src/components/ui/Separator/Separator.native.stories.tsx +42 -0
- package/src/components/ui/Separator/Separator.native.tsx +32 -0
- package/src/components/ui/Separator/Separator.stories.tsx +1 -1
- package/src/components/ui/Separator/Separator.tsx +2 -2
- package/src/components/ui/Skeleton/Skeleton.module.css +1 -1
- package/src/components/ui/Skeleton/Skeleton.native.stories.tsx +33 -0
- package/src/components/ui/Skeleton/Skeleton.native.tsx +41 -0
- package/src/components/ui/Skeleton/Skeleton.stories.tsx +1 -1
- package/src/components/ui/Skeleton/Skeleton.tsx +2 -3
- package/src/components/ui/SkipLink/SkipLink.stories.tsx +1 -1
- package/src/components/ui/SkipLink/SkipLink.tsx +5 -3
- package/src/components/ui/Table/Table.mdx +14 -0
- package/src/components/ui/Table/Table.module.css +9 -9
- package/src/components/ui/Table/Table.stories.tsx +1 -1
- package/src/components/ui/Table/Table.tsx +2 -2
- package/src/components/ui/Tabs/Tabs.module.css +3 -3
- package/src/components/ui/Tabs/Tabs.stories.tsx +1 -1
- package/src/components/ui/Tabs/Tabs.tsx +2 -2
- package/src/components/ui/Tooltip/Tooltip.mdx +14 -0
- package/src/components/ui/Tooltip/Tooltip.module.css +8 -8
- package/src/components/ui/Tooltip/Tooltip.stories.tsx +1 -1
- package/src/components/ui/Tooltip/Tooltip.tsx +2 -2
- package/src/components/ui/Typography/Typography.mdx +13 -0
- package/src/components/ui/Typography/Typography.native.mdx +56 -0
- package/src/components/ui/Typography/Typography.native.stories.tsx +38 -0
- package/src/components/ui/Typography/Typography.native.tsx +65 -0
- package/src/components/ui/Typography/Typography.stories.tsx +1 -1
- package/src/components/ui/Typography/Typography.tsx +2 -2
- package/src/css/bdc.css +8 -0
- package/src/index.ts +1 -0
- package/src/native/ThemeContext.tsx +28 -0
- package/src/native/tokens.ts +13 -0
- package/src/native.ts +39 -0
- package/src/polyfill-invoker-commands.ts +68 -0
- package/src/react-augment.d.ts +32 -0
- package/src/stories/DesignSystem/DarkMode.mdx +130 -0
- package/src/stories/ReactNative.mdx +121 -0
- package/src/types.ts +2 -0
- package/src/typings.d.ts +3 -0
- package/src/web-components/globals.ts +61 -0
- package/src/web-components/index.ts +12 -0
- package/src/web-components/interaction/BdsButton.mdx +106 -0
- package/src/web-components/interaction/BdsButton.stories.tsx +60 -0
- package/src/web-components/interaction/BdsPopover.mdx +120 -0
- package/src/web-components/interaction/BdsPopover.stories.tsx +70 -0
- package/src/web-components/interaction/BdsToastProvider.mdx +94 -0
- package/src/web-components/interaction/BdsToastProvider.stories.tsx +73 -0
- package/src/web-components/interaction/bds-button.spec.ts +95 -0
- package/src/web-components/interaction/bds-button.ts +293 -0
- package/src/web-components/interaction/bds-popover.spec.ts +174 -0
- package/src/web-components/interaction/bds-popover.ts +229 -0
- package/src/web-components/interaction/bds-toast-provider.spec.ts +122 -0
- package/src/web-components/interaction/bds-toast-provider.ts +211 -0
- package/src/web-components/test/helpers.ts +22 -0
- package/src/web-components/ui/BdsAlert.mdx +90 -0
- package/src/web-components/ui/BdsAlert.stories.tsx +60 -0
- package/src/web-components/ui/BdsBadge.mdx +74 -0
- package/src/web-components/ui/BdsBadge.stories.tsx +37 -0
- package/src/web-components/ui/bds-alert.spec.ts +109 -0
- package/src/web-components/ui/bds-alert.ts +209 -0
- package/src/web-components/ui/bds-badge.spec.ts +51 -0
- package/src/web-components/ui/bds-badge.ts +88 -0
|
@@ -12,29 +12,26 @@
|
|
|
12
12
|
background-color: var(--dialog_color ,var(--bds-color_grey--subtle));
|
|
13
13
|
color: var(--dialog_on-color, var(--bds-color_on-grey--subtle));
|
|
14
14
|
padding: var(--bds-space_l);
|
|
15
|
-
max-
|
|
16
|
-
max-
|
|
15
|
+
max-inline-size: 90svw;
|
|
16
|
+
max-block-size: 90svh;
|
|
17
17
|
overflow: visible;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
.dialogContent {
|
|
21
21
|
display: grid;
|
|
22
22
|
gap: var(--bds-grid_gap);
|
|
23
|
-
overflow-
|
|
24
|
-
min-
|
|
23
|
+
overflow-block: auto;
|
|
24
|
+
min-block-size: 0;
|
|
25
25
|
flex: 1;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
.
|
|
28
|
+
.closeButton {
|
|
29
|
+
all: unset;
|
|
29
30
|
position: absolute;
|
|
30
31
|
inset-block-start: -1em;
|
|
31
32
|
inset-inline-end: -1em;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.closeButton {
|
|
35
|
-
all: unset;
|
|
36
|
-
width: 2em;
|
|
37
|
-
height: 2em;
|
|
33
|
+
inline-size: 2em;
|
|
34
|
+
block-size: 2em;
|
|
38
35
|
display: flex;
|
|
39
36
|
align-items: center;
|
|
40
37
|
justify-content: center;
|
|
@@ -50,8 +47,8 @@
|
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
.closeButton svg {
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
inline-size: 1.25em;
|
|
51
|
+
block-size: 1.25em;
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
.dialog::backdrop {
|
|
@@ -29,7 +29,26 @@ describe('Dialog', () => {
|
|
|
29
29
|
expect(screen.getByRole('button', { name: /close dialog/i })).toBeInTheDocument();
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
it('
|
|
32
|
+
it('close button is type="button"', () => {
|
|
33
|
+
render(<Dialog isOpen><span>Content</span></Dialog>);
|
|
34
|
+
expect(screen.getByRole('button', { name: /close dialog/i })).toHaveAttribute('type', 'button');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('close button always uses commandfor/command', () => {
|
|
38
|
+
render(<Dialog isOpen><span>Content</span></Dialog>);
|
|
39
|
+
const closeBtn = screen.getByRole('button', { name: /close dialog/i });
|
|
40
|
+
expect(closeBtn).toHaveAttribute('commandfor');
|
|
41
|
+
expect(closeBtn).toHaveAttribute('command', 'close');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('close button commandfor matches dialog id', () => {
|
|
45
|
+
render(<Dialog id="my-dialog" isOpen><span>Content</span></Dialog>);
|
|
46
|
+
const closeBtn = screen.getByRole('button', { name: /close dialog/i });
|
|
47
|
+
expect(closeBtn).toHaveAttribute('commandfor', 'my-dialog');
|
|
48
|
+
expect(document.querySelector('dialog')).toHaveAttribute('id', 'my-dialog');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('close button click via polyfill calls onClose', async () => {
|
|
33
52
|
const user = userEvent.setup();
|
|
34
53
|
const onClose = vi.fn();
|
|
35
54
|
render(<Dialog isOpen onClose={onClose}><span>Content</span></Dialog>);
|
|
@@ -42,6 +61,12 @@ describe('Dialog', () => {
|
|
|
42
61
|
expect(HTMLDialogElement.prototype.showModal).not.toHaveBeenCalled();
|
|
43
62
|
});
|
|
44
63
|
|
|
64
|
+
it('does not call showModal again when dialog is already open', () => {
|
|
65
|
+
const { rerender } = render(<Dialog isOpen><span>Content</span></Dialog>);
|
|
66
|
+
rerender(<Dialog isOpen><span>Content</span></Dialog>);
|
|
67
|
+
expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalledTimes(1);
|
|
68
|
+
});
|
|
69
|
+
|
|
45
70
|
it('locks body scroll when open', () => {
|
|
46
71
|
render(<Dialog isOpen><span>Content</span></Dialog>);
|
|
47
72
|
expect(document.body.style.overflow).toBe('hidden');
|
|
@@ -77,4 +102,31 @@ describe('Dialog', () => {
|
|
|
77
102
|
|
|
78
103
|
document.body.removeChild(trigger);
|
|
79
104
|
});
|
|
105
|
+
|
|
106
|
+
describe('command event (Invoker Commands API)', () => {
|
|
107
|
+
function makeCommandEvent(command: string) {
|
|
108
|
+
const event = new Event('command');
|
|
109
|
+
Object.defineProperty(event, 'command', { value: command, configurable: true });
|
|
110
|
+
return event;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
it('locks scroll and calls onOpen on show-modal command', () => {
|
|
114
|
+
const onOpen = vi.fn();
|
|
115
|
+
render(<Dialog onOpen={onOpen}><span>Content</span></Dialog>);
|
|
116
|
+
const dialog = document.querySelector('dialog')!;
|
|
117
|
+
dialog.dispatchEvent(makeCommandEvent('show-modal'));
|
|
118
|
+
expect(document.body.style.overflow).toBe('hidden');
|
|
119
|
+
expect(onOpen).toHaveBeenCalled();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('restores scroll and calls onClose on close command', () => {
|
|
123
|
+
const onClose = vi.fn();
|
|
124
|
+
render(<Dialog isOpen onClose={onClose}><span>Content</span></Dialog>);
|
|
125
|
+
document.body.style.overflow = 'hidden';
|
|
126
|
+
const dialog = document.querySelector('dialog')!;
|
|
127
|
+
dialog.dispatchEvent(makeCommandEvent('close'));
|
|
128
|
+
expect(document.body.style.overflow).toBe('');
|
|
129
|
+
expect(onClose).toHaveBeenCalled();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
80
132
|
});
|
|
@@ -4,7 +4,7 @@ import { Dialog } from './Dialog';
|
|
|
4
4
|
import { Button } from '../Button/Button';
|
|
5
5
|
|
|
6
6
|
const meta = {
|
|
7
|
-
title: 'Interaction/Dialog',
|
|
7
|
+
title: 'React/Interaction/Dialog',
|
|
8
8
|
component: Dialog,
|
|
9
9
|
} satisfies Meta<typeof Dialog>;
|
|
10
10
|
|
|
@@ -33,3 +33,20 @@ export const Interactive: Story = {
|
|
|
33
33
|
);
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
|
+
|
|
37
|
+
// Open and close using the Invoker Commands API — no JS event handlers needed.
|
|
38
|
+
// The browser wires up the buttons to the dialog natively via commandfor/command.
|
|
39
|
+
export const InvokerCommands: Story = {
|
|
40
|
+
render: () => (
|
|
41
|
+
<>
|
|
42
|
+
<Button type="button" commandfor="invoker-dialog" command="show-modal">
|
|
43
|
+
Open Dialog (no JS)
|
|
44
|
+
</Button>
|
|
45
|
+
<Dialog id="invoker-dialog">
|
|
46
|
+
<h2>Invoker Dialog</h2>
|
|
47
|
+
<p>This dialog was opened with a declarative HTML invoker command — no onClick needed.</p>
|
|
48
|
+
<p>The close button (×) inside also uses an invoker command.</p>
|
|
49
|
+
</Dialog>
|
|
50
|
+
</>
|
|
51
|
+
),
|
|
52
|
+
};
|
|
@@ -1,28 +1,38 @@
|
|
|
1
|
-
import { ReactNode, useEffect, useRef } from 'react';
|
|
1
|
+
import { ReactNode, useEffect, useId, useRef } from 'react';
|
|
2
|
+
import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
|
|
3
|
+
|
|
4
|
+
// Install once at module load — no-op if native support is present.
|
|
5
|
+
installInvokerCommandsPolyfill();
|
|
2
6
|
|
|
3
7
|
const FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
4
8
|
import css from './Dialog.module.css';
|
|
5
9
|
import { cn } from '@boostdev/design-system-foundation';
|
|
10
|
+
import type { WithClassName } from '../../../types';
|
|
6
11
|
|
|
7
|
-
interface DialogProps {
|
|
12
|
+
interface DialogProps extends WithClassName {
|
|
8
13
|
children: ReactNode;
|
|
9
|
-
|
|
14
|
+
id?: string;
|
|
10
15
|
isOpen?: boolean;
|
|
16
|
+
onOpen?: () => void;
|
|
11
17
|
onClose?: () => void;
|
|
12
18
|
}
|
|
13
19
|
|
|
14
|
-
export function Dialog({ children, isOpen = false, className, onClose }: DialogProps) {
|
|
20
|
+
export function Dialog({ children, id: idProp, isOpen = false, className, onOpen, onClose }: DialogProps) {
|
|
21
|
+
const generatedId = useId();
|
|
22
|
+
const id = idProp ?? generatedId;
|
|
23
|
+
|
|
15
24
|
const dialogRef = useRef<HTMLDialogElement | null>(null);
|
|
16
25
|
const triggerRef = useRef<Element | null>(null);
|
|
17
26
|
|
|
27
|
+
// Controlled mode: open/close in response to the isOpen prop.
|
|
18
28
|
useEffect(() => {
|
|
19
29
|
const dialog = dialogRef.current;
|
|
20
30
|
if (!dialog) return;
|
|
21
|
-
if (isOpen) {
|
|
31
|
+
if (isOpen && !dialog.open) {
|
|
22
32
|
triggerRef.current = document.activeElement;
|
|
23
33
|
dialog.showModal();
|
|
24
34
|
document.body.style.overflow = 'hidden';
|
|
25
|
-
} else if (dialog.open) {
|
|
35
|
+
} else if (!isOpen && dialog.open) {
|
|
26
36
|
dialog.close();
|
|
27
37
|
document.body.style.overflow = '';
|
|
28
38
|
(triggerRef.current as HTMLElement | null)?.focus();
|
|
@@ -32,6 +42,30 @@ export function Dialog({ children, isOpen = false, className, onClose }: DialogP
|
|
|
32
42
|
};
|
|
33
43
|
}, [isOpen]);
|
|
34
44
|
|
|
45
|
+
// Invoker Commands API: sync state when the dialog is opened or closed via
|
|
46
|
+
// a <button commandfor="id" command="show-modal|close"> without JS.
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const dialog = dialogRef.current;
|
|
49
|
+
if (!dialog) return;
|
|
50
|
+
const handleCommand = (e: CommandEvent) => {
|
|
51
|
+
if (e.command === 'show-modal') {
|
|
52
|
+
triggerRef.current = document.activeElement;
|
|
53
|
+
document.body.style.overflow = 'hidden';
|
|
54
|
+
onOpen?.();
|
|
55
|
+
} else if (e.command === 'close') {
|
|
56
|
+
document.body.style.overflow = '';
|
|
57
|
+
// Defer focus restoration: the dialog is still open when the command
|
|
58
|
+
// event fires (the action runs after), so focusing outside would be
|
|
59
|
+
// blocked by the focus-trap. setTimeout defers until after close.
|
|
60
|
+
const trigger = triggerRef.current;
|
|
61
|
+
setTimeout(() => (trigger as HTMLElement | null)?.focus(), 0);
|
|
62
|
+
onClose?.();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
dialog.addEventListener('command', handleCommand);
|
|
66
|
+
return () => dialog.removeEventListener('command', handleCommand);
|
|
67
|
+
}, [onOpen, onClose]);
|
|
68
|
+
|
|
35
69
|
const handleBackdropClick = (e: React.MouseEvent<HTMLDialogElement>) => {
|
|
36
70
|
if (e.target === dialogRef.current) onClose?.();
|
|
37
71
|
};
|
|
@@ -59,24 +93,24 @@ export function Dialog({ children, isOpen = false, className, onClose }: DialogP
|
|
|
59
93
|
return (
|
|
60
94
|
<dialog
|
|
61
95
|
ref={dialogRef}
|
|
96
|
+
id={id}
|
|
62
97
|
className={cn(className, css.dialog)}
|
|
63
98
|
aria-modal="true"
|
|
64
99
|
onClick={handleBackdropClick}
|
|
65
100
|
onCancel={handleCancel}
|
|
66
101
|
onKeyDown={handleKeyDown}
|
|
67
102
|
>
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
</form>
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
className={css.closeButton}
|
|
106
|
+
commandfor={id}
|
|
107
|
+
command="close"
|
|
108
|
+
aria-label="Close dialog"
|
|
109
|
+
>
|
|
110
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
111
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M18 6L6 18M6 6l12 12" />
|
|
112
|
+
</svg>
|
|
113
|
+
</button>
|
|
80
114
|
<div className={css.dialogContent}>
|
|
81
115
|
{children}
|
|
82
116
|
</div>
|
|
@@ -28,6 +28,20 @@ Slide-in panel from the left or right edge of the viewport. Use for navigation,
|
|
|
28
28
|
|
|
29
29
|
<ArgTypes of={Stories} />
|
|
30
30
|
|
|
31
|
+
## CSS variables
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
<table>
|
|
35
|
+
<thead>
|
|
36
|
+
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
37
|
+
</thead>
|
|
38
|
+
<tbody>
|
|
39
|
+
<tr><td>`--drawer_color`</td><td>`var(--bds-color_bg)`</td><td>Panel background colour</td></tr>
|
|
40
|
+
<tr><td>`--drawer_on-color`</td><td>`var(--bds-color_on-bg)`</td><td>Text colour</td></tr>
|
|
41
|
+
</tbody>
|
|
42
|
+
</table>
|
|
43
|
+
|
|
44
|
+
|
|
31
45
|
## Accessibility
|
|
32
46
|
|
|
33
47
|
- Uses `<dialog>` internally — provides native focus trapping and `Escape` to close
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
inset: 0;
|
|
5
5
|
margin: 0;
|
|
6
6
|
padding: 0;
|
|
7
|
-
|
|
8
|
-
max-
|
|
9
|
-
|
|
10
|
-
max-
|
|
7
|
+
inline-size: 100%;
|
|
8
|
+
max-inline-size: 100%;
|
|
9
|
+
block-size: 100%;
|
|
10
|
+
max-block-size: 100%;
|
|
11
11
|
border: none;
|
|
12
12
|
background: transparent;
|
|
13
13
|
overflow: hidden;
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
inset-block: 0;
|
|
24
24
|
display: flex;
|
|
25
25
|
flex-direction: column;
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
inline-size: min(28rem, 90vw);
|
|
27
|
+
block-size: 100%;
|
|
28
28
|
background-color: var(--drawer_color, var(--bds-color_bg));
|
|
29
29
|
color: var(--drawer_on-color, var(--bds-color_on-bg));
|
|
30
30
|
box-shadow: var(--bds-shadow_xl);
|
|
@@ -54,16 +54,16 @@
|
|
|
54
54
|
display: flex;
|
|
55
55
|
align-items: center;
|
|
56
56
|
justify-content: center;
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
inline-size: 2rem;
|
|
58
|
+
block-size: 2rem;
|
|
59
59
|
border-radius: 50%;
|
|
60
60
|
cursor: pointer;
|
|
61
61
|
transition: var(--bds-animation_transition);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
.closeButton svg {
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
inline-size: 1.25rem;
|
|
66
|
+
block-size: 1.25rem;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
.closeButton:focus-visible {
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
|
|
80
80
|
.body {
|
|
81
81
|
flex: 1;
|
|
82
|
-
overflow-
|
|
82
|
+
overflow-block: auto;
|
|
83
83
|
padding: var(--bds-space_l);
|
|
84
84
|
display: flex;
|
|
85
85
|
flex-direction: column;
|
|
@@ -24,7 +24,21 @@ describe('Drawer', () => {
|
|
|
24
24
|
expect(screen.getByText('Navigation items')).toBeInTheDocument();
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
it('
|
|
27
|
+
it('close button uses commandfor/command', () => {
|
|
28
|
+
render(<Drawer isOpen title="Menu" onClose={() => {}}>Content</Drawer>);
|
|
29
|
+
const closeBtn = screen.getByRole('button', { name: 'Close drawer' });
|
|
30
|
+
expect(closeBtn).toHaveAttribute('commandfor');
|
|
31
|
+
expect(closeBtn).toHaveAttribute('command', 'close');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('close button commandfor matches dialog id', () => {
|
|
35
|
+
render(<Drawer id="my-drawer" isOpen title="Menu" onClose={() => {}}>Content</Drawer>);
|
|
36
|
+
const closeBtn = screen.getByRole('button', { name: 'Close drawer' });
|
|
37
|
+
expect(closeBtn).toHaveAttribute('commandfor', 'my-drawer');
|
|
38
|
+
expect(document.querySelector('dialog')).toHaveAttribute('id', 'my-drawer');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('close button click via polyfill calls onClose', async () => {
|
|
28
42
|
const onClose = vi.fn();
|
|
29
43
|
render(<Drawer isOpen title="Menu" onClose={onClose}>Content</Drawer>);
|
|
30
44
|
await userEvent.click(screen.getByRole('button', { name: 'Close drawer' }));
|
|
@@ -42,6 +56,12 @@ describe('Drawer', () => {
|
|
|
42
56
|
expect(HTMLDialogElement.prototype.close).toHaveBeenCalled();
|
|
43
57
|
});
|
|
44
58
|
|
|
59
|
+
it('does not call showModal again when already open', () => {
|
|
60
|
+
const { rerender } = render(<Drawer isOpen title="Test" onClose={() => {}}>Body</Drawer>);
|
|
61
|
+
rerender(<Drawer isOpen title="Test" onClose={() => {}}>Body</Drawer>);
|
|
62
|
+
expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalledTimes(1);
|
|
63
|
+
});
|
|
64
|
+
|
|
45
65
|
it('locks body scroll when open', () => {
|
|
46
66
|
render(<Drawer isOpen title="Settings" onClose={() => {}}>Content</Drawer>);
|
|
47
67
|
expect(document.body.style.overflow).toBe('hidden');
|
|
@@ -88,4 +108,31 @@ describe('Drawer', () => {
|
|
|
88
108
|
|
|
89
109
|
document.body.removeChild(trigger);
|
|
90
110
|
});
|
|
111
|
+
|
|
112
|
+
describe('command event (Invoker Commands API)', () => {
|
|
113
|
+
function makeCommandEvent(command: string) {
|
|
114
|
+
const event = new Event('command');
|
|
115
|
+
Object.defineProperty(event, 'command', { value: command, configurable: true });
|
|
116
|
+
return event;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
it('locks scroll and calls onOpen on show-modal command', () => {
|
|
120
|
+
const onOpen = vi.fn();
|
|
121
|
+
render(<Drawer title="Menu" isOpen={false} onOpen={onOpen} onClose={() => {}}>Content</Drawer>);
|
|
122
|
+
const dialog = document.querySelector('dialog')!;
|
|
123
|
+
dialog.dispatchEvent(makeCommandEvent('show-modal'));
|
|
124
|
+
expect(document.body.style.overflow).toBe('hidden');
|
|
125
|
+
expect(onOpen).toHaveBeenCalled();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('restores scroll and calls onClose on close command', () => {
|
|
129
|
+
const onClose = vi.fn();
|
|
130
|
+
render(<Drawer title="Menu" isOpen onClose={onClose}>Content</Drawer>);
|
|
131
|
+
document.body.style.overflow = 'hidden';
|
|
132
|
+
const dialog = document.querySelector('dialog')!;
|
|
133
|
+
dialog.dispatchEvent(makeCommandEvent('close'));
|
|
134
|
+
expect(document.body.style.overflow).toBe('');
|
|
135
|
+
expect(onClose).toHaveBeenCalled();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
91
138
|
});
|
|
@@ -1,19 +1,28 @@
|
|
|
1
|
-
import { ReactNode, useEffect, useRef } from 'react';
|
|
1
|
+
import { ReactNode, useEffect, useId, useRef } from 'react';
|
|
2
|
+
import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
|
|
3
|
+
|
|
4
|
+
// Install once at module load — no-op if native support is present.
|
|
5
|
+
installInvokerCommandsPolyfill();
|
|
6
|
+
|
|
2
7
|
import css from './Drawer.module.css';
|
|
3
8
|
import { cn } from '@boostdev/design-system-foundation';
|
|
9
|
+
import type { WithClassName } from '../../../types';
|
|
4
10
|
|
|
5
|
-
interface DrawerProps {
|
|
11
|
+
interface DrawerProps extends WithClassName {
|
|
12
|
+
id?: string;
|
|
6
13
|
isOpen: boolean;
|
|
14
|
+
onOpen?: () => void;
|
|
7
15
|
onClose: () => void;
|
|
8
16
|
title: ReactNode;
|
|
9
17
|
children: ReactNode;
|
|
10
18
|
side?: 'left' | 'right';
|
|
11
|
-
className?: string;
|
|
12
19
|
ariaLabel?: string;
|
|
13
20
|
}
|
|
14
21
|
|
|
15
22
|
export function Drawer({
|
|
23
|
+
id: idProp,
|
|
16
24
|
isOpen,
|
|
25
|
+
onOpen,
|
|
17
26
|
onClose,
|
|
18
27
|
title,
|
|
19
28
|
children,
|
|
@@ -21,17 +30,20 @@ export function Drawer({
|
|
|
21
30
|
ariaLabel,
|
|
22
31
|
className,
|
|
23
32
|
}: Readonly<DrawerProps>) {
|
|
33
|
+
const generatedId = useId();
|
|
34
|
+
const id = idProp ?? generatedId;
|
|
35
|
+
|
|
24
36
|
const dialogRef = useRef<HTMLDialogElement>(null);
|
|
25
37
|
const triggerRef = useRef<Element | null>(null);
|
|
26
38
|
|
|
27
39
|
useEffect(() => {
|
|
28
40
|
const dialog = dialogRef.current;
|
|
29
41
|
if (!dialog) return;
|
|
30
|
-
if (isOpen) {
|
|
42
|
+
if (isOpen && !dialog.open) {
|
|
31
43
|
triggerRef.current = document.activeElement;
|
|
32
44
|
dialog.showModal();
|
|
33
45
|
document.body.style.overflow = 'hidden';
|
|
34
|
-
} else if (dialog.open) {
|
|
46
|
+
} else if (!isOpen && dialog.open) {
|
|
35
47
|
dialog.close();
|
|
36
48
|
document.body.style.overflow = '';
|
|
37
49
|
(triggerRef.current as HTMLElement | null)?.focus();
|
|
@@ -41,6 +53,26 @@ export function Drawer({
|
|
|
41
53
|
};
|
|
42
54
|
}, [isOpen]);
|
|
43
55
|
|
|
56
|
+
// Invoker Commands API: sync state when opened/closed via commandfor/command.
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const dialog = dialogRef.current;
|
|
59
|
+
if (!dialog) return;
|
|
60
|
+
const handleCommand = (e: CommandEvent) => {
|
|
61
|
+
if (e.command === 'show-modal') {
|
|
62
|
+
triggerRef.current = document.activeElement;
|
|
63
|
+
document.body.style.overflow = 'hidden';
|
|
64
|
+
onOpen?.();
|
|
65
|
+
} else if (e.command === 'close') {
|
|
66
|
+
document.body.style.overflow = '';
|
|
67
|
+
const trigger = triggerRef.current;
|
|
68
|
+
setTimeout(() => (trigger as HTMLElement | null)?.focus(), 0);
|
|
69
|
+
onClose();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
dialog.addEventListener('command', handleCommand);
|
|
73
|
+
return () => dialog.removeEventListener('command', handleCommand);
|
|
74
|
+
}, [onOpen, onClose]);
|
|
75
|
+
|
|
44
76
|
// Close on backdrop click
|
|
45
77
|
const handleClick = (e: React.MouseEvent<HTMLDialogElement>) => {
|
|
46
78
|
if (e.target === dialogRef.current) onClose();
|
|
@@ -55,6 +87,7 @@ export function Drawer({
|
|
|
55
87
|
return (
|
|
56
88
|
<dialog
|
|
57
89
|
ref={dialogRef}
|
|
90
|
+
id={id}
|
|
58
91
|
className={cn(css.drawer, css[`--side_${side}`], className)}
|
|
59
92
|
aria-label={ariaLabel}
|
|
60
93
|
aria-modal="true"
|
|
@@ -67,7 +100,8 @@ export function Drawer({
|
|
|
67
100
|
<button
|
|
68
101
|
type="button"
|
|
69
102
|
className={css.closeButton}
|
|
70
|
-
|
|
103
|
+
commandfor={id}
|
|
104
|
+
command="close"
|
|
71
105
|
aria-label="Close drawer"
|
|
72
106
|
>
|
|
73
107
|
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
.menu {
|
|
8
8
|
position: absolute;
|
|
9
|
-
|
|
9
|
+
inset-block-start: calc(100% + var(--bds-space_xxs));
|
|
10
10
|
z-index: var(--bds-z-index_dropdown);
|
|
11
|
-
min-
|
|
11
|
+
min-inline-size: 10rem;
|
|
12
12
|
padding: var(--bds-space_xxs) 0;
|
|
13
13
|
border: var(--dropdown_border, none);
|
|
14
14
|
--bdc_color: currentcolor;
|
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
margin: 0;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
.--placement_bottom-start {
|
|
28
|
-
.--placement_bottom-end {
|
|
27
|
+
.--placement_bottom-start { inset-inline-start: 0; }
|
|
28
|
+
.--placement_bottom-end { inset-inline-end: 0; }
|
|
29
29
|
|
|
30
30
|
.separator {
|
|
31
31
|
border: none;
|
|
32
|
-
border-
|
|
32
|
+
border-block-start: 1px solid currentcolor;
|
|
33
33
|
margin: var(--bds-space_xxs) 0;
|
|
34
34
|
opacity: 0.15;
|
|
35
35
|
}
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
display: flex;
|
|
40
40
|
align-items: center;
|
|
41
41
|
gap: var(--bds-space_xs);
|
|
42
|
-
|
|
42
|
+
inline-size: 100%;
|
|
43
43
|
padding: var(--bds-space_xs) var(--bds-space_m);
|
|
44
44
|
font-family: var(--bds-font_family--body);
|
|
45
45
|
font-size: var(--bds-font_size--body);
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from 'react';
|
|
11
11
|
import css from './DropdownMenu.module.css';
|
|
12
12
|
import { cn } from '@boostdev/design-system-foundation';
|
|
13
|
+
import type { WithClassName } from '../../../types';
|
|
13
14
|
|
|
14
15
|
export interface DropdownMenuItem {
|
|
15
16
|
id: string;
|
|
@@ -20,11 +21,10 @@ export interface DropdownMenuItem {
|
|
|
20
21
|
separator?: boolean;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
interface DropdownMenuProps {
|
|
24
|
+
interface DropdownMenuProps extends WithClassName {
|
|
24
25
|
trigger: ReactElement;
|
|
25
26
|
items: DropdownMenuItem[];
|
|
26
27
|
placement?: 'bottom-start' | 'bottom-end';
|
|
27
|
-
className?: string;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export function DropdownMenu({
|
|
@@ -27,6 +27,23 @@ Floating panel anchored to a trigger element. Unlike `Tooltip`, popovers can con
|
|
|
27
27
|
|
|
28
28
|
<ArgTypes of={Stories} />
|
|
29
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>`--popover_color`</td><td>`var(--bds-color_bg)`</td><td>Panel background colour</td></tr>
|
|
39
|
+
<tr><td>`--popover_on-color`</td><td>`var(--bds-color_on-bg)`</td><td>Text colour</td></tr>
|
|
40
|
+
<tr><td>`--popover_shadow`</td><td>`var(--bds-shadow_m)`</td><td>Panel box shadow</td></tr>
|
|
41
|
+
<tr><td>`--popover_border`</td><td>`none`</td><td>Panel border</td></tr>
|
|
42
|
+
<tr><td>`--popover_radius`</td><td>`var(--bdc-outline_radius)`</td><td>Panel corner radius</td></tr>
|
|
43
|
+
</tbody>
|
|
44
|
+
</table>
|
|
45
|
+
|
|
46
|
+
|
|
30
47
|
## Accessibility
|
|
31
48
|
|
|
32
49
|
- Trigger element gets `aria-haspopup="true"` and `aria-expanded` toggled on open/close
|