@agregio-solutions/design-system 1.90.1 → 1.92.0

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.
Files changed (68) hide show
  1. package/dist/design-system.cjs +9 -5
  2. package/dist/design-system.js +14 -6
  3. package/dist/packages/components/Accordion/doc.md +342 -0
  4. package/dist/packages/components/Badge/doc.md +192 -0
  5. package/dist/packages/components/Breadcrumbs/doc.md +332 -0
  6. package/dist/packages/components/Button/doc.md +425 -0
  7. package/dist/packages/components/Calendar/doc.md +465 -0
  8. package/dist/packages/components/ChartLegend/doc.md +151 -0
  9. package/dist/packages/components/ChartTooltip/doc.md +124 -0
  10. package/dist/packages/components/Checkbox/doc.md +329 -0
  11. package/dist/packages/components/CheckboxGroup/doc.md +242 -0
  12. package/dist/packages/components/Chip/doc.md +99 -0
  13. package/dist/packages/components/Combobox/Combobox.d.ts +8 -0
  14. package/dist/packages/components/Combobox/doc.md +680 -0
  15. package/dist/packages/components/DataTable/doc.md +1124 -0
  16. package/dist/packages/components/DatePicker/doc.md +579 -0
  17. package/dist/packages/components/DateRangePicker/doc.md +638 -0
  18. package/dist/packages/components/Drawer/doc.md +338 -0
  19. package/dist/packages/components/Dropdown/Dropdown.d.ts +4 -0
  20. package/dist/packages/components/Dropdown/doc.md +205 -0
  21. package/dist/packages/components/EmptyState/doc.md +101 -0
  22. package/dist/packages/components/FileUpload/doc.md +449 -0
  23. package/dist/packages/components/Filter/doc.md +196 -0
  24. package/dist/packages/components/Header/doc.md +373 -0
  25. package/dist/packages/components/I18nProvider/doc.md +187 -0
  26. package/dist/packages/components/Icon/doc.md +63 -0
  27. package/dist/packages/components/Label/doc.md +60 -0
  28. package/dist/packages/components/LinearProgressBar/doc.md +148 -0
  29. package/dist/packages/components/Link/doc.md +206 -0
  30. package/dist/packages/components/List/doc.md +481 -0
  31. package/dist/packages/components/Loader/doc.md +53 -0
  32. package/dist/packages/components/Menu/Menu.d.ts +5 -1
  33. package/dist/packages/components/Menu/doc.md +231 -0
  34. package/dist/packages/components/Message/doc.md +166 -0
  35. package/dist/packages/components/Modal/doc.md +289 -0
  36. package/dist/packages/components/Navigation/doc.md +992 -0
  37. package/dist/packages/components/NavigationItem/doc.md +167 -0
  38. package/dist/packages/components/NotificationCard/doc.md +206 -0
  39. package/dist/packages/components/Notifications/doc.md +240 -0
  40. package/dist/packages/components/NumberField/doc.md +582 -0
  41. package/dist/packages/components/PageLayout/doc.md +651 -0
  42. package/dist/packages/components/Pagination/doc.md +227 -0
  43. package/dist/packages/components/Popover/doc.md +245 -0
  44. package/dist/packages/components/Radio/doc.md +370 -0
  45. package/dist/packages/components/RouterProvider/doc.md +64 -0
  46. package/dist/packages/components/SearchBar/doc.md +504 -0
  47. package/dist/packages/components/SegmentedControl/doc.md +398 -0
  48. package/dist/packages/components/Select/Select.d.ts +4 -0
  49. package/dist/packages/components/Select/doc.md +1133 -0
  50. package/dist/packages/components/Skeleton/doc.md +129 -0
  51. package/dist/packages/components/Slider/doc.md +362 -0
  52. package/dist/packages/components/Stepper/doc.md +104 -0
  53. package/dist/packages/components/Switch/doc.md +296 -0
  54. package/dist/packages/components/Tabs/doc.md +295 -0
  55. package/dist/packages/components/Tag/doc.md +81 -0
  56. package/dist/packages/components/TextInput/doc.md +490 -0
  57. package/dist/packages/components/TimeField/doc.md +353 -0
  58. package/dist/packages/components/Timeline/doc.md +1046 -0
  59. package/dist/packages/components/Toaster/doc.md +263 -0
  60. package/dist/packages/components/ToggleButton/doc.md +108 -0
  61. package/dist/packages/components/ToggleButtonGroup/doc.md +307 -0
  62. package/dist/packages/components/Tooltip/doc.md +206 -0
  63. package/dist/packages/components/YearMonthPicker/YearMonthPicker.d.ts +8 -0
  64. package/dist/packages/components/YearMonthPicker/doc.md +638 -0
  65. package/dist/public_docs/components.md +68 -0
  66. package/dist/public_docs/index.md +30 -0
  67. package/dist/public_docs/tokens.md +121 -0
  68. package/package.json +3 -2
@@ -0,0 +1,231 @@
1
+ # Menu
2
+
3
+ ## Props
4
+
5
+ The complete Props documentation with JS doc for this component is available at this path:
6
+
7
+ node_modules/@agregio-solutions/design-system/dist/packages/components/Menu/Menu.d.ts
8
+
9
+ ## Example usage
10
+
11
+ Here are the Storybook Stories.
12
+
13
+ Base stories:
14
+
15
+ ```tsx
16
+ import { Meta, StoryObj } from "@storybook/react-vite";
17
+ import Menu from "./Menu";
18
+ import MenuItem from "../MenuItem/MenuItem";
19
+
20
+ const meta: Meta<typeof Menu> = {
21
+ component: Menu,
22
+ parameters: {
23
+ layout: "centered",
24
+ },
25
+ argTypes: {
26
+ children: { control: false },
27
+ },
28
+ };
29
+ export default meta;
30
+
31
+ type Story = StoryObj<typeof meta>;
32
+
33
+ export const Playground: Story = {
34
+ args: {
35
+ buttonProps: {
36
+ iconLeft: "menu",
37
+ mode: "secondary",
38
+ },
39
+ children: (
40
+ <>
41
+ <MenuItem onClick={() => alert("open")} text="Open" />
42
+ <MenuItem onClick={() => alert("rename")} text="Rename…" />
43
+ <MenuItem onClick={() => alert("duplicate")} text="Duplicate" />
44
+ <MenuItem onClick={() => alert("share")} text="Share…" />
45
+ <MenuItem
46
+ onClick={() => alert("disabled")}
47
+ text="Disabled"
48
+ isDisabled
49
+ />
50
+ </>
51
+ ),
52
+ },
53
+ };
54
+
55
+ export const OpenByDefault: Story = {
56
+ args: {
57
+ ...Playground.args,
58
+ defaultOpen: true,
59
+ },
60
+ };
61
+ ```
62
+
63
+ ## How to test this component
64
+
65
+ Here are some more advanced stories with more testing coverage and examples that you can read to understand how to test this component.
66
+
67
+ ```tsx
68
+ import { Meta, StoryObj } from "@storybook/react-vite";
69
+ import { expect, userEvent, within, screen } from "storybook/test";
70
+ import Menu from "../Menu";
71
+
72
+ import * as MenuStories from "../Menu.stories";
73
+ import MenuItem from "@packages/components/MenuItem/MenuItem";
74
+ import { useState } from "react";
75
+
76
+ const meta: Meta<typeof Menu> = {
77
+ component: Menu,
78
+ ...MenuStories.default,
79
+ parameters: {
80
+ ...MenuStories.default.parameters,
81
+ chromatic: { disableSnapshot: true },
82
+ },
83
+ };
84
+ export default meta;
85
+
86
+ type Story = StoryObj<typeof meta>;
87
+
88
+ export const ExampleUsage: Story = {
89
+ render: () => {
90
+ const Parent = () => {
91
+ const [selectedItem, setSelectedItem] = useState("");
92
+
93
+ return (
94
+ <div>
95
+ <p>The selected item is: {selectedItem}</p>
96
+
97
+ <Menu
98
+ buttonProps={{
99
+ iconLeft: "menu",
100
+ "aria-label": "Menu",
101
+ mode: "secondary",
102
+ }}
103
+ >
104
+ <MenuItem text="Item 1" onClick={() => setSelectedItem("Item 1")} />
105
+ <MenuItem text="Item 2" onClick={() => setSelectedItem("Item 2")} />
106
+ <MenuItem text="Item 3" onClick={() => setSelectedItem("Item 3")} />
107
+ <MenuItem
108
+ text="Disabled item"
109
+ onClick={() => setSelectedItem("Item disabled")}
110
+ isDisabled
111
+ />
112
+ </Menu>
113
+ </div>
114
+ );
115
+ };
116
+
117
+ return <Parent />;
118
+ },
119
+ };
120
+ export const ControlledOpenState: Story = {
121
+ render: () => {
122
+ const Parent = () => {
123
+ const [isOpen, setIsOpen] = useState(false);
124
+
125
+ return (
126
+ <Menu
127
+ isOpen={isOpen}
128
+ onOpenChange={setIsOpen}
129
+ buttonProps={{
130
+ iconLeft: "menu",
131
+ "aria-label": "Menu",
132
+ mode: "secondary",
133
+ }}
134
+ >
135
+ <MenuItem text="Item 1" />
136
+ <MenuItem text="Item 2" />
137
+ </Menu>
138
+ );
139
+ };
140
+
141
+ return <Parent />;
142
+ },
143
+ };
144
+
145
+ export const TestShouldOpenAndClickOnItem: Story = {
146
+ render: ExampleUsage.render,
147
+ play: async ({ canvasElement }) => {
148
+ const canvas = within(canvasElement);
149
+ const user = userEvent.setup();
150
+ await user.click(canvas.getByLabelText("Menu"));
151
+ await screen.findByText("Item 1");
152
+ await user.click(screen.getByText("Item 1"));
153
+ expect(
154
+ canvas.getByText("The selected item is: Item 1"),
155
+ ).toBeInTheDocument();
156
+ await expect(canvas.queryByText("Item 1")).not.toBeInTheDocument();
157
+ },
158
+ };
159
+
160
+ export const TestShouldNotOpenAndClickOnDisabledItem: Story = {
161
+ render: ExampleUsage.render,
162
+ play: async ({ canvasElement }) => {
163
+ const canvas = within(canvasElement);
164
+ const user = userEvent.setup();
165
+ await user.click(canvas.getByLabelText("Menu"));
166
+ await screen.findByText("Disabled item");
167
+ await user.click(screen.getByText("Disabled item"));
168
+ expect(
169
+ canvas.queryByText("The selected item is: Disabled item"),
170
+ ).not.toBeInTheDocument();
171
+ await screen.findByText("Disabled item");
172
+ },
173
+ };
174
+ ```
175
+
176
+ ## Developer notes
177
+
178
+ Here are the notes available for the developer on the built Storybook, you can read them to understand the component and how to use it.
179
+
180
+ ```mdx
181
+ import {
182
+ Canvas,
183
+ Meta,
184
+ Stories,
185
+ Controls,
186
+ Source,
187
+ } from "@storybook/addon-docs/blocks";
188
+
189
+ import * as Menu from "./Menu.stories";
190
+ import * as MenuTests from "./tests/Menu.stories";
191
+
192
+ <Meta of={Menu} />
193
+
194
+ # Menu
195
+
196
+ A menu displays a list of actions or options that a user can choose.
197
+
198
+ ## Example usage
199
+
200
+ <Source of={MenuTests.ExampleUsage} type="code" dark />
201
+
202
+ ## Features
203
+
204
+ There is no native element to implement a menu in HTML that is widely supported.
205
+ Menu and MenuItem help achieve accessible menu components that are nicely styled and easy to use.
206
+
207
+ - **Keyboard navigation** – Menu items can be navigated using the arrow keys, along with page up/down, home/end, etc. Typeahead, auto scrolling, and disabled items are supported as well.
208
+ - **Trigger interactions** – Menus can be triggered by pressing with a mouse or touch, or optionally, with a long press interaction. The arrow keys also open the menu with a keyboard, automatically focusing the first or last item accordingly.
209
+ - **Accessible** – Follows the ARIA menu pattern, with keyboard shortcut elements within each item for improved screen reader announcement.
210
+
211
+ ## Controlled open state
212
+
213
+ The open state of the menu can be controlled via the `defaultOpen` and `isOpen` props.
214
+
215
+ <Source of={MenuTests.ControlledOpenState} type="code" dark />
216
+
217
+ Alternatively, you can use the `defaultOpen` if you just want the menu to be
218
+ open by default (without having to manage the open state yourself).
219
+
220
+ ## How to test this component?
221
+
222
+ It is really simple to test a menu. Here is an example:
223
+
224
+ <Source of={MenuTests.TestShouldOpenAndClickOnItem} type="code" dark />
225
+
226
+ ## Playground & Props
227
+
228
+ <Canvas of={Menu.Playground} />
229
+
230
+ <Controls of={Menu.Playground} />
231
+ ```
@@ -0,0 +1,166 @@
1
+ # Message
2
+
3
+ ## Props
4
+
5
+ The complete Props documentation with JS doc for this component is available at this path:
6
+
7
+ node_modules/@agregio-solutions/design-system/dist/packages/components/Message/Message.d.ts
8
+
9
+ ## Example usage
10
+
11
+ Here are the Storybook Stories.
12
+
13
+ Base stories:
14
+
15
+ ```tsx
16
+ import { Meta, StoryObj } from "@storybook/react-vite";
17
+ import { expect, fn, userEvent, within } from "storybook/test";
18
+ import Message from "./Message";
19
+ import { I18nProvider } from "react-aria-components";
20
+ import { STORYBOOK_VIEWPORTS } from "@internal/test-utils-storybook/test-utils-storybook";
21
+
22
+ const meta: Meta<typeof Message> = {
23
+ component: Message,
24
+ tags: ["autodocs"],
25
+ parameters: {
26
+ layout: "centered",
27
+ },
28
+ argTypes: {
29
+ children: { control: { type: "text" } },
30
+ },
31
+ decorators: [
32
+ (Story) => (
33
+ <I18nProvider locale="en">
34
+ <Story />
35
+ </I18nProvider>
36
+ ),
37
+ ],
38
+ };
39
+ export default meta;
40
+
41
+ type Story = StoryObj<typeof meta>;
42
+
43
+ export const Playground: Story = {
44
+ args: {
45
+ nature: "negative",
46
+ onClose: fn(),
47
+ actions: [
48
+ { text: "Primary Action", mode: "primary" },
49
+ { text: "Secondary Action", mode: "secondary" },
50
+ ],
51
+ title: "The Title",
52
+ children: "Some error message",
53
+ },
54
+ play: async ({ canvasElement, args }) => {
55
+ const canvas = within(canvasElement);
56
+ const user = userEvent.setup();
57
+ await canvas.findByText(args.title as string);
58
+ await canvas.findByText(args.children as string);
59
+ await canvas.findByText("Primary Action");
60
+ await canvas.findByText("Secondary Action");
61
+ await user.click(await canvas.findByLabelText("Close alert"));
62
+ await expect(args.onClose).toHaveBeenCalledTimes(1);
63
+ await user.click(await canvas.findByText(args.children as string)); // Just to avoid the close button being focused in Chromatic snapshot
64
+ },
65
+ };
66
+
67
+ export const Positive: Story = {
68
+ args: {
69
+ ...Playground.args,
70
+ nature: "positive",
71
+ },
72
+ };
73
+
74
+ export const Warning: Story = {
75
+ args: {
76
+ ...Playground.args,
77
+ nature: "warning",
78
+ },
79
+ };
80
+
81
+ export const Informative: Story = {
82
+ args: {
83
+ ...Playground.args,
84
+ nature: "informative",
85
+ },
86
+ };
87
+
88
+ export const Negative: Story = {
89
+ args: {
90
+ ...Playground.args,
91
+ nature: "negative",
92
+ },
93
+ };
94
+
95
+ export const Minimal: Story = {
96
+ args: {
97
+ children: "Some error message",
98
+ },
99
+ };
100
+
101
+ export const PixelPerfect: Story = {
102
+ args: {
103
+ onClose: fn(),
104
+ title: "[Insert title]",
105
+ actions: [
106
+ { text: "[Insert name]", mode: "primary" },
107
+ { text: "[Insert name]", mode: "secondary" },
108
+ { text: "[Insert name]", mode: "secondary" },
109
+ ],
110
+ children: "[Insert description]",
111
+ },
112
+ };
113
+
114
+ export const StressTestOverflow: Story = {
115
+ decorators: [
116
+ (Story) => (
117
+ <div style={{ width: "800px" }}>
118
+ <Story />
119
+ </div>
120
+ ),
121
+ ],
122
+ args: {
123
+ ...PixelPerfect.args,
124
+ children:
125
+ "qsdhqjshdjhqskjdhjkqsdhqkshdjkqhsdhqjksdhjkqhsdkjhqjksdhkjqsjkhdkjqhsdkjhqkdsjdsqqsdhqjshdjhqskjdhjkqsdhqkshdjkqhsdhqjksdhjkqhsdkjhqjksdhkjqsjkhdkjqhsdkjhqkdsjdsq",
126
+ },
127
+ };
128
+
129
+ export const Mobile: Story = {
130
+ parameters: {
131
+ ...STORYBOOK_VIEWPORTS,
132
+ layout: "padded",
133
+ },
134
+ globals: {
135
+ viewport: { value: "iphone6", isRotated: false },
136
+ },
137
+ args: {
138
+ ...PixelPerfect.args,
139
+ },
140
+ };
141
+
142
+ export const FullWidth: Story = {
143
+ parameters: {
144
+ layout: "padded",
145
+ },
146
+ args: {
147
+ ...PixelPerfect.args,
148
+ },
149
+ };
150
+
151
+ export const AutoHide: Story = {
152
+ args: {
153
+ ...Playground.args,
154
+ autoHide: true,
155
+ },
156
+ play: async ({ canvasElement, args }) => {
157
+ const canvas = within(canvasElement);
158
+ const user = userEvent.setup();
159
+ await canvas.findByText(args.children as string);
160
+ await user.click(await canvas.findByLabelText("Close alert"));
161
+ await expect(
162
+ canvas.queryByText(args.children as string),
163
+ ).not.toBeInTheDocument();
164
+ },
165
+ };
166
+ ```
@@ -0,0 +1,289 @@
1
+ # Modal
2
+
3
+ ## Props
4
+
5
+ The complete Props documentation with JS doc for this component is available at this path:
6
+
7
+ node_modules/@agregio-solutions/design-system/dist/packages/components/Modal/Modal.d.ts
8
+
9
+ ## Example usage
10
+
11
+ Here are the Storybook Stories.
12
+
13
+ Base stories:
14
+
15
+ ```tsx
16
+ import { Meta, StoryObj } from "@storybook/react-vite";
17
+ import Modal, { ModalActions, ModalContent, Props } from "./Modal";
18
+ import { screen } from "storybook/test";
19
+ import { useState } from "react";
20
+ import Button from "@components/Button/Button";
21
+ import { expectNotPresent } from "@internal/test-utils-storybook/test-utils-storybook";
22
+
23
+ const meta: Meta<typeof Modal> = {
24
+ component: Modal,
25
+ argTypes: {
26
+ children: { control: false },
27
+ },
28
+ parameters: {
29
+ layout: "none",
30
+ },
31
+ render: (args) => {
32
+ function ParentComponent(props: Props) {
33
+ const [isOpen, setIsOpen] = useState(props.isOpen);
34
+
35
+ return (
36
+ <div
37
+ style={{
38
+ display: "flex",
39
+ alignItems: "center",
40
+ justifyContent: "center",
41
+ height: "100vh",
42
+ }}
43
+ >
44
+ <Modal {...props} isOpen={isOpen} onOpenChange={setIsOpen}>
45
+ <ModalContent>{props.children}</ModalContent>
46
+
47
+ <ModalActions>
48
+ <Button
49
+ onClick={() => setIsOpen(false)}
50
+ text="Close"
51
+ mode="secondary"
52
+ />
53
+ <Button onClick={() => setIsOpen(false)} text="Apply" />
54
+ </ModalActions>
55
+ </Modal>
56
+
57
+ <div style={{ textAlign: "center" }}>
58
+ <Button onClick={() => setIsOpen(true)} text="Open Modal" />
59
+ <div style={{ marginTop: 16 }}>
60
+ The Modal is {isOpen ? "open" : "closed"}
61
+ </div>
62
+ </div>
63
+ </div>
64
+ );
65
+ }
66
+
67
+ return <ParentComponent {...args}>{args.children}</ParentComponent>;
68
+ },
69
+ };
70
+ export default meta;
71
+
72
+ export const Playground: StoryObj<typeof Modal> = {
73
+ args: {
74
+ title: "[Insert Modal title]",
75
+ children: <div>The content</div>,
76
+ isOpen: true,
77
+ },
78
+ play: async ({ args }) => {
79
+ await screen.findByText(args.title);
80
+ await screen.findByText("The content");
81
+ await screen.findByText("Close");
82
+ await screen.findByLabelText("Close");
83
+ await expectNotPresent(() => screen.queryByText("Submit"));
84
+ },
85
+ };
86
+
87
+ export const OverflowingContent: StoryObj<typeof Modal> = {
88
+ args: {
89
+ ...Playground.args,
90
+ children: "The content. ".repeat(400),
91
+ },
92
+ };
93
+
94
+ export const CustomWidth: StoryObj<typeof Modal> = {
95
+ args: {
96
+ ...Playground.args,
97
+ width: "auto",
98
+ },
99
+ };
100
+ ```
101
+
102
+ ## How to test this component
103
+
104
+ Here are some more advanced stories with more testing coverage and examples that you can read to understand how to test this component.
105
+
106
+ ```tsx
107
+ import { Meta, StoryObj } from "@storybook/react-vite";
108
+ import Modal, { ModalActions, ModalContent } from "../Modal";
109
+ import { userEvent, within, screen } from "storybook/test";
110
+ import { useState } from "react";
111
+ import Button from "@components/Button/Button";
112
+ import { expectNotPresent } from "@internal/test-utils-storybook/test-utils-storybook";
113
+ import { Playground } from "../Modal.stories";
114
+ import baseMeta from "../Modal.stories";
115
+ import { useForm } from "react-hook-form";
116
+ import TextInput from "@components/TextInput/TextInput";
117
+ import { ModalForm } from "../Modal.styled";
118
+
119
+ const meta: Meta<typeof Modal> = {
120
+ component: Modal,
121
+ parameters: {
122
+ layout: "none",
123
+ chromatic: { disableSnapshot: true },
124
+ },
125
+ render: baseMeta.render,
126
+ };
127
+ export default meta;
128
+
129
+ export const TestCloseButtonNextTitleShouldCloseModal: StoryObj<typeof Modal> =
130
+ {
131
+ args: {
132
+ ...Playground.args,
133
+ },
134
+ play: async ({ canvasElement, args }) => {
135
+ const canvas = within(canvasElement);
136
+ const user = userEvent.setup({ delay: 50 });
137
+ await canvas.findByText("The Modal is open");
138
+ await screen.findByText(args.title);
139
+ await user.click(screen.getByLabelText("Close"));
140
+ await canvas.findByText("The Modal is closed");
141
+ await expectNotPresent(() => screen.queryByText(args.title));
142
+ },
143
+ };
144
+
145
+ export const TestFooterCloseButtonShouldCloseModal: StoryObj<typeof Modal> = {
146
+ args: {
147
+ ...Playground.args,
148
+ },
149
+ play: async ({ canvasElement, args }) => {
150
+ const canvas = within(canvasElement);
151
+ const user = userEvent.setup({ delay: 50 });
152
+ await canvas.findByText("The Modal is open");
153
+ await screen.findByText(args.title);
154
+ await user.click(screen.getByText("Close"));
155
+ await canvas.findByText("The Modal is closed");
156
+ await expectNotPresent(() => screen.queryByText(args.title));
157
+ },
158
+ };
159
+
160
+ export const ExampleUsage: StoryObj<typeof Modal> = {
161
+ render: () => {
162
+ function ParentComponent() {
163
+ const [isOpen, setIsOpen] = useState(false);
164
+
165
+ return (
166
+ <div>
167
+ <div>
168
+ <Button onClick={() => setIsOpen(true)} text="Open modal" />
169
+ </div>
170
+ <Modal isOpen={isOpen} onOpenChange={setIsOpen} title="Modal title">
171
+ <ModalContent>Modal content</ModalContent>
172
+ <ModalActions>
173
+ <Button
174
+ onClick={() => setIsOpen(false)}
175
+ text="Close"
176
+ mode="secondary"
177
+ />
178
+ <Button onClick={() => setIsOpen(false)} text="Apply" />
179
+ </ModalActions>
180
+ </Modal>
181
+ </div>
182
+ );
183
+ }
184
+
185
+ return <ParentComponent />;
186
+ },
187
+ };
188
+
189
+ export const ExampleWithReactHookForm: StoryObj<typeof Modal> = {
190
+ render: () => {
191
+ type Form = {
192
+ name: string;
193
+ email: string;
194
+ };
195
+
196
+ type FormProps = {
197
+ onClose: () => void;
198
+ onSubmit: (values: Form) => void;
199
+ };
200
+
201
+ function Form({ onClose, onSubmit }: FormProps) {
202
+ const form = useForm<Form>();
203
+
204
+ return (
205
+ <ModalForm
206
+ onSubmit={form.handleSubmit((data: Form) => {
207
+ onSubmit(data);
208
+ })}
209
+ >
210
+ <ModalContent>
211
+ <TextInput
212
+ {...form.register("name", { required: "Name is required" })}
213
+ id="name"
214
+ label="Name"
215
+ errorHelperText={form.formState.errors.name?.message}
216
+ />
217
+ </ModalContent>
218
+ <ModalActions>
219
+ <Button onClick={onClose} text="Close" mode="secondary" />
220
+ <Button type="submit" text="Create user" />
221
+ </ModalActions>
222
+ </ModalForm>
223
+ );
224
+ }
225
+
226
+ function ParentComponent() {
227
+ const [isOpen, setIsOpen] = useState(false);
228
+
229
+ return (
230
+ <div>
231
+ <Button onClick={() => setIsOpen(true)} text="Open form" />
232
+
233
+ <Modal
234
+ isOpen={isOpen}
235
+ onOpenChange={setIsOpen}
236
+ title="Creating a new user"
237
+ >
238
+ <Form
239
+ onClose={() => setIsOpen(false)}
240
+ onSubmit={(values) => {
241
+ // API call for example
242
+ console.log(values); // eslint-disable-line no-console
243
+ setIsOpen(false);
244
+ }}
245
+ />
246
+ </Modal>
247
+ </div>
248
+ );
249
+ }
250
+
251
+ return <ParentComponent />;
252
+ },
253
+ };
254
+ ```
255
+
256
+ ## Developer notes
257
+
258
+ Here are the notes available for the developer on the built Storybook, you can read them to understand the component and how to use it.
259
+
260
+ ```mdx
261
+ import { Meta, Controls, Source } from "@storybook/addon-docs/blocks";
262
+
263
+ import * as Modal from "./Modal.stories";
264
+ import * as ModalTests from "./tests/Modal.stories";
265
+
266
+ <Meta of={Modal} />
267
+
268
+ # Modal
269
+
270
+ The HTML `<dialog>` element can be used to build modals.
271
+ However, it is not yet widely supported across browsers, and building fully accessible custom dialogs from scratch is very difficult and error prone.
272
+ Modal helps achieve accessible modals.
273
+
274
+ - **Accessible** – Content outside the model is hidden from assistive technologies while it is open. The modal closes when interacting outside, or pressing the Escape key.
275
+ - **Focus management** – Focus is moved into the modal on mount, and restored to the trigger element on unmount. While open, focus is contained within the modal, preventing the user from tabbing outside.
276
+ - **Scroll locking** – Scrolling the page behind the modal is prevented while it is open, including in mobile browsers.
277
+
278
+ ## Example usage
279
+
280
+ <Source of={ModalTests.ExampleUsage} type="code" dark />
281
+
282
+ ## Example with a form
283
+
284
+ <Source of={ModalTests.ExampleWithReactHookForm} type="code" dark />
285
+
286
+ ## Props
287
+
288
+ <Controls of={Modal.Playground} />
289
+ ```