@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,651 @@
1
+ # PageLayout
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/PageLayout/PageLayout.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 { fn } from "storybook/test";
18
+ import PageLayout from "./PageLayout";
19
+ import { STORYBOOK_VIEWPORTS } from "@internal/test-utils-storybook/test-utils-storybook";
20
+ import NavigationItem from "../NavigationItem/NavigationItem";
21
+ import Button from "../Button/Button";
22
+ import Navigation from "../Navigation/Navigation";
23
+ import Tag from "../Tag/Tag";
24
+ import Breadcrumbs from "../Breadcrumbs/Breadcrumbs";
25
+ import Header from "../Header/Header";
26
+ import Select from "../Select/Select";
27
+ import SelectItem from "../SelectItem/SelectItem";
28
+ import Tabs from "../Tabs/Tabs/Tabs";
29
+ import TabList from "../Tabs/TabList/TabList";
30
+ import Tab from "../Tabs/Tab/Tab";
31
+ import TabPanel from "../Tabs/TabPanel/TabPanel";
32
+ import arrayOfLength from "@internal/arrayOfLength/arrayOfLength";
33
+ import { I18nProvider } from "react-aria-components";
34
+ import { useMemo } from "react";
35
+ import {
36
+ createColumnHelper,
37
+ getCoreRowModel,
38
+ getFilteredRowModel,
39
+ getPaginationRowModel,
40
+ getSortedRowModel,
41
+ useReactTable,
42
+ } from "@tanstack/react-table";
43
+ import DataTableHeader from "../DataTable/DataTableHeader/DataTableHeader";
44
+ import DataTableCell from "../DataTable/DataTableCell/DataTableCell";
45
+ import Radio from "../Radio/Radio";
46
+ import Filter, { TypeTableColumnWithFilter } from "../Filter/Filter";
47
+ import CheckboxGroup from "../CheckboxGroup/CheckboxGroup";
48
+ import Checkbox from "../Checkbox/Checkbox";
49
+ import Switch from "../Switch/Switch";
50
+ import DataTable from "../DataTable/DataTable";
51
+
52
+ const meta: Meta<typeof PageLayout> = {
53
+ component: PageLayout,
54
+ parameters: {
55
+ layout: "none",
56
+ },
57
+ decorators: [
58
+ (Story) => {
59
+ return (
60
+ <I18nProvider locale="fr">
61
+ <Story />
62
+ </I18nProvider>
63
+ );
64
+ },
65
+ ],
66
+ };
67
+ export default meta;
68
+
69
+ type Story = StoryObj<typeof meta>;
70
+
71
+ export const Desktop: Story = {
72
+ args: {
73
+ navigationComponent: (
74
+ <Navigation logo="agregio" onParametersClick={fn()} onLogoutClick={fn()}>
75
+ <>
76
+ {arrayOfLength(10).map((index) => (
77
+ <NavigationItem
78
+ key={index}
79
+ label={`Item ${index}`}
80
+ href={`/item-${index}`}
81
+ iconLeft="home"
82
+ />
83
+ ))}
84
+ <NavigationItem
85
+ label={`Item Dropdown`}
86
+ iconLeft="home"
87
+ subLinks={[
88
+ { href: "/", label: "Sous-libellé 1" },
89
+ { href: "/", label: "Sous-libellé 2" },
90
+ ]}
91
+ />
92
+ </>
93
+ </Navigation>
94
+ ),
95
+ children: (
96
+ <>
97
+ <Header
98
+ title="Insert title"
99
+ subtitle="Insert subtitle"
100
+ tagComponent={<Tag iconLeft="help_outline">[Insert tag]</Tag>}
101
+ logo="agregio"
102
+ onGoBack={fn()}
103
+ breadcrumbsComponent={
104
+ <Breadcrumbs
105
+ links={[
106
+ { href: "#link-1", text: "Link 1" },
107
+ { href: "#link-2", text: "Link 2" },
108
+ { href: "#link-3", text: "Link 3" },
109
+ ]}
110
+ />
111
+ }
112
+ helperIconComponent={
113
+ <Button
114
+ iconLeft="help_outline"
115
+ onClick={() => alert("Help")}
116
+ mode="tertiary"
117
+ />
118
+ }
119
+ notificationIconComponent={
120
+ <Button
121
+ iconLeft="notifications_none"
122
+ onClick={() => alert("Notifications")}
123
+ mode="tertiary"
124
+ />
125
+ }
126
+ additionalActions={
127
+ <Select
128
+ mode="single"
129
+ id="select"
130
+ aria-label="Select"
131
+ value={null}
132
+ onChange={fn()}
133
+ placeholder="Select"
134
+ >
135
+ <SelectItem id="select-item-1" text="Select Item 1" />
136
+ <SelectItem id="select-item-2" text="Select Item 2" />
137
+ <SelectItem id="select-item-3" text="Select Item 3" />
138
+ </Select>
139
+ }
140
+ tabsComponent={
141
+ <Tabs>
142
+ <TabList>
143
+ <Tab id="tab-1">Tab 1</Tab>
144
+ <Tab
145
+ id="tab-2"
146
+ badgeProps={{ nature: "negative", variant: "dot" }}
147
+ >
148
+ Tab 2
149
+ </Tab>
150
+ <Tab id="tab-3" badgeProps={{ value: 8, nature: "negative" }}>
151
+ Tab 3
152
+ </Tab>
153
+ </TabList>
154
+ <div style={{ marginTop: "var(--spacing-md" }}>
155
+ <TabPanel id="tab-1">Content for the panel 1</TabPanel>
156
+ <TabPanel id="tab-2">Content for the panel 2</TabPanel>
157
+ <TabPanel id="tab-3">Content for the panel 3</TabPanel>
158
+ </div>
159
+ </Tabs>
160
+ }
161
+ />
162
+ </>
163
+ ),
164
+ },
165
+ };
166
+
167
+ export const Mobile: Story = {
168
+ args: {
169
+ ...Desktop.args,
170
+ },
171
+ parameters: {
172
+ ...STORYBOOK_VIEWPORTS,
173
+ layout: "none",
174
+ },
175
+ globals: {
176
+ viewport: { value: "iphone6", isRotated: false },
177
+ },
178
+ };
179
+
180
+ export const PageLayoutWithDataTable: Story = {
181
+ args: {
182
+ ...Desktop.args,
183
+ },
184
+ render: () => {
185
+ const ParentComponent = () => {
186
+ // Below are just type definition of the data and its generation
187
+ // Typically you would get this from your API and will not have to create them here
188
+ // We let it so you can fully understand how the filter works based on the data provided
189
+
190
+ type User = {
191
+ firstname: string;
192
+ lastname: string;
193
+ email: string;
194
+ isValidated?: boolean;
195
+ day?: string;
196
+ foods?: Array<string>;
197
+ organization?: { id: string; name: string };
198
+ isActive?: boolean;
199
+ };
200
+
201
+ const ALL_FOODS = useMemo(
202
+ () => ["Pizza", "Hamburger", "Ramen", "Sushi", "Fries"],
203
+ [],
204
+ );
205
+
206
+ const FOODS = useMemo(
207
+ () => [
208
+ [ALL_FOODS[0], ALL_FOODS[1]],
209
+ [ALL_FOODS[1], ALL_FOODS[2]],
210
+ [ALL_FOODS[2], ALL_FOODS[3]],
211
+ [ALL_FOODS[3], ALL_FOODS[4]],
212
+ [ALL_FOODS[4], ALL_FOODS[0]],
213
+ ],
214
+ [ALL_FOODS],
215
+ );
216
+
217
+ const ORGANIZATIONS = useMemo(
218
+ () => [
219
+ { id: "1", name: "EDF Store and Forecast" },
220
+ { id: "2", name: "Agregio Solutions" },
221
+ { id: "3", name: "Dalkia" },
222
+ ],
223
+ [],
224
+ );
225
+
226
+ const DAYS = useMemo(
227
+ () => ["Monday", "Tuesday", "Wednesday", "Thursday"],
228
+ [],
229
+ );
230
+
231
+ const data: Array<User> = useMemo(
232
+ () =>
233
+ arrayOfLength(300).map((index) => ({
234
+ firstname: `Firstname ${index + 1}`,
235
+ lastname: `Lastname ${index + 1}`,
236
+ email: `john-doe${index + 1}@example.com`,
237
+ isValidated: index % 2 === 0,
238
+ foods: FOODS[index % FOODS.length],
239
+ day: DAYS[index % DAYS.length],
240
+ organization: ORGANIZATIONS[index % ORGANIZATIONS.length],
241
+ isActive: index % 2 === 0,
242
+ })),
243
+ [DAYS, FOODS, ORGANIZATIONS],
244
+ );
245
+
246
+ // Here starts the interesting stuff: columns definitions.
247
+ // All the features of the filters comes from those columns definitions
248
+ // Please read carefully each column, they all serve as a different example of how to use the filters
249
+ const columnHelper = createColumnHelper<User>();
250
+
251
+ const columns = useMemo(
252
+ () => [
253
+ columnHelper.accessor("firstname", {
254
+ // Here, because there is no "FilterInDrawer" component, there will be nothing in the filter drawer related to the firstname
255
+ // But the firstname can still be filtered by the global filter (search input)
256
+ header: ({ header }) => (
257
+ <DataTableHeader
258
+ onSortClick={header.column.getToggleSortingHandler()}
259
+ sortDirection={header.column.getIsSorted() || "none"}
260
+ text="Firstname"
261
+ />
262
+ ),
263
+ cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
264
+ }),
265
+ // Here is an example using a "FilterInDrawer" component that render a radio group to filter the day
266
+ // Please note the "filterFn" is "arrIncludes" for this king of value (Array<string>)
267
+ // Also note the usage of `satisfies TypeTableColumnWithFilter<User>` to help you type check the column definition (because meta is a custom property)
268
+ // Finally, you will note that the Chips are auto-generated, if you do not want to use them you can set the `skipFilterChips` to true
269
+ columnHelper.accessor("day", {
270
+ id: "Day",
271
+ header: () => <DataTableHeader text="Day" />,
272
+ cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
273
+ filterFn: "equals",
274
+ meta: {
275
+ FilterInDrawer: ({ column }) => (
276
+ <div>
277
+ <b>Filter by day:</b>
278
+
279
+ {DAYS.map((day) => (
280
+ <Radio
281
+ label={day}
282
+ key={day}
283
+ checked={column.getFilterValue() === day}
284
+ onChange={() => column.setFilterValue(day)}
285
+ />
286
+ ))}
287
+ </div>
288
+ ),
289
+ } satisfies TypeTableColumnWithFilter<User>,
290
+ }),
291
+ // Here is another example using a "FilterInDrawer" component that render a checkbox group to filter the food
292
+ // Please note the "filterFn" is "arrIncludesSome" -> we want to include all entries that match at least one food
293
+ // Also note the usage of `satisfies TypeTableColumnWithFilter<User>` to help you type check the column definition (because meta is a custom property)
294
+ // Finally, you will note that the Chips are auto-generated, if you do not want to use them you can set the `skipFilterChips` to true
295
+ columnHelper.accessor("foods", {
296
+ id: "Foods",
297
+ header: () => <DataTableHeader text="Foods" />,
298
+ cell: (props) => (
299
+ <DataTableCell>{props.getValue()?.join(", ")}</DataTableCell>
300
+ ),
301
+ filterFn: "arrIncludesSome",
302
+ meta: {
303
+ FilterInDrawer: ({ column }) => (
304
+ <div style={{ marginTop: 8 }}>
305
+ <b>Filter by food (at least one):</b>
306
+ <CheckboxGroup
307
+ value={column.getFilterValue() as any}
308
+ onChange={column.setFilterValue}
309
+ aria-label="Filter by food (at least one)"
310
+ >
311
+ {ALL_FOODS.map((food) => (
312
+ <div key={food}>
313
+ <Checkbox label={food} value={food} />
314
+ </div>
315
+ ))}
316
+ </CheckboxGroup>
317
+ </div>
318
+ ),
319
+ } satisfies TypeTableColumnWithFilter<User>,
320
+ }),
321
+ // Here is another example using a "FilterInDrawer" component that render a select to filter the organization
322
+ // Please note the "filterFn" is a custom function that will filter the data based on the organization id
323
+ // Also note the usage of `satisfies TypeTableColumnWithFilter<User>` to help you type check the column definition (because meta is a custom property)
324
+ // And most important here, we have a custom meta property `filterChips` that will generate a chip to display the selected organization
325
+ // The Filter can not handle automatic chips when the data is a complex object, so you have to handle that manually
326
+ columnHelper.accessor("organization", {
327
+ id: "Organization",
328
+ header: () => <DataTableHeader text="Organization" />,
329
+ cell: (props) => (
330
+ <DataTableCell>{props.getValue()?.name}</DataTableCell>
331
+ ),
332
+ filterFn: (row, _, filterValue) => {
333
+ if (!filterValue) return true;
334
+ return row.original.organization?.id === filterValue;
335
+ },
336
+ meta: {
337
+ filterChips: ({ column }) => {
338
+ const value = column.getFilterValue();
339
+ const organization = ORGANIZATIONS.find(
340
+ (org) => org.id === value,
341
+ );
342
+ if (!organization) return [];
343
+ return [
344
+ {
345
+ value: `Organization: ${organization.name}`,
346
+ onClose: () => column.setFilterValue(null),
347
+ },
348
+ ];
349
+ },
350
+ FilterInDrawer: ({ column }) => (
351
+ <Select
352
+ id="organization-select"
353
+ label="Filter by organization:"
354
+ mode="single"
355
+ value={column.getFilterValue() as any}
356
+ onChange={column.setFilterValue}
357
+ fullWidth
358
+ wrapperProps={{ style: { marginBottom: 8 } }}
359
+ >
360
+ {ORGANIZATIONS.map((organization) => (
361
+ <SelectItem
362
+ text={organization.name}
363
+ id={organization.id}
364
+ key={organization.id}
365
+ />
366
+ ))}
367
+ </Select>
368
+ ),
369
+ } satisfies TypeTableColumnWithFilter<User>,
370
+ }),
371
+ // Here is an example that uses a filter outside of the drawer.
372
+ // See the "extraLeftContent" prop to see how to connect it to react-table filtering features
373
+ columnHelper.accessor("isActive", {
374
+ header: () => <DataTableHeader text="Active" />,
375
+ cell: (props) => (
376
+ <DataTableCell>{props.getValue() ? "✅" : "❌"}</DataTableCell>
377
+ ),
378
+ filterFn: (row, _, filterValue) => {
379
+ if (!filterValue) return true;
380
+ return !!row.original.isActive;
381
+ },
382
+ }),
383
+ // Here is an example of an empty column, for example to display an actions bar
384
+ // You can note that the header is empty, the text is optional
385
+ columnHelper.display({
386
+ id: "actions",
387
+ header: () => <DataTableHeader />,
388
+ cell: () => (
389
+ <DataTableCell>
390
+ <Button mode="secondary" iconLeft="edit" />
391
+ </DataTableCell>
392
+ ),
393
+ }),
394
+ ],
395
+ [ALL_FOODS, DAYS, ORGANIZATIONS, columnHelper],
396
+ );
397
+
398
+ // And here we define the table as usual
399
+ const table = useReactTable({
400
+ data,
401
+ columns,
402
+ initialState: {
403
+ globalFilter: "",
404
+ pagination: {
405
+ pageSize: 50,
406
+ },
407
+ },
408
+ getCoreRowModel: getCoreRowModel(),
409
+ getSortedRowModel: getSortedRowModel(),
410
+ getFilteredRowModel: getFilteredRowModel(),
411
+ getPaginationRowModel: getPaginationRowModel(),
412
+ });
413
+
414
+ // Get the pagination state
415
+ const pagination = table.getState().pagination;
416
+
417
+ // Render the table using the base DataTable component
418
+ return (
419
+ <PageLayout
420
+ navigationComponent={
421
+ <Navigation
422
+ logo="agregio"
423
+ onParametersClick={fn()}
424
+ onLogoutClick={fn()}
425
+ >
426
+ <>
427
+ {arrayOfLength(10).map((index) => (
428
+ <NavigationItem
429
+ key={index}
430
+ label={`Item ${index}`}
431
+ href={`/item-${index}`}
432
+ iconLeft="home"
433
+ />
434
+ ))}
435
+ <NavigationItem
436
+ label={`Item Dropdown`}
437
+ iconLeft="home"
438
+ subLinks={[
439
+ { href: "/", label: "Sous-libellé 1" },
440
+ { href: "/", label: "Sous-libellé 2" },
441
+ ]}
442
+ />
443
+ </>
444
+ </Navigation>
445
+ }
446
+ >
447
+ <>
448
+ <Header title={"Test"} />
449
+ <Filter<User>
450
+ onDownloadClick={() => alert("Download")}
451
+ onSettingsClick={() => alert("Settings")}
452
+ table={table}
453
+ extraLeftContent={
454
+ <>
455
+ <Switch
456
+ label="Only active"
457
+ isSelected={!!table.getColumn("isActive")?.getFilterValue()}
458
+ onChange={() =>
459
+ table
460
+ .getColumn("isActive")
461
+ ?.setFilterValue((prevValue: boolean) => !prevValue)
462
+ }
463
+ />
464
+ </>
465
+ }
466
+ extraRightContent={
467
+ <>
468
+ <Button
469
+ mode="secondary"
470
+ onClick={() => alert("Secondary")}
471
+ text="Secondary"
472
+ />
473
+ <Button
474
+ mode="primary"
475
+ onClick={() => alert("Primary")}
476
+ text="Primary"
477
+ />
478
+ </>
479
+ }
480
+ />
481
+ <DataTable table={table} pagination={pagination} />
482
+ </>
483
+ </PageLayout>
484
+ );
485
+ };
486
+
487
+ return <ParentComponent />;
488
+ },
489
+ };
490
+ ```
491
+
492
+ ## How to test this component
493
+
494
+ Here are some more advanced stories with more testing coverage and examples that you can read to understand how to test this component.
495
+
496
+ ```tsx
497
+ import { Meta, StoryObj } from "@storybook/react-vite";
498
+ import { expect, userEvent, within } from "storybook/test";
499
+ import PageLayout from "../PageLayout";
500
+ import { STORYBOOK_VIEWPORTS } from "@internal/test-utils-storybook/test-utils-storybook";
501
+ import * as PageLayoutStories from "../PageLayout.stories";
502
+
503
+ const meta: Meta<typeof PageLayout> = {
504
+ ...PageLayoutStories.default,
505
+ parameters: {
506
+ ...PageLayoutStories.default.parameters,
507
+ chromatic: { disableSnapshot: true },
508
+ },
509
+ component: PageLayout,
510
+ };
511
+ export default meta;
512
+
513
+ type Story = StoryObj<typeof meta>;
514
+
515
+ export const Desktop: Story = {
516
+ args: {
517
+ ...PageLayoutStories.Desktop.args,
518
+ },
519
+ play: async ({ canvasElement }) => {
520
+ const canvas = within(canvasElement);
521
+ const user = userEvent.setup();
522
+
523
+ await expect(canvas.getByText("Item 0")).toBeVisible();
524
+ await expect(canvas.getByLabelText("Menu mobile")).not.toBeVisible();
525
+ await expect(
526
+ canvas.getByTestId("navigation-mobile-collapsed-menu"),
527
+ ).toBeVisible();
528
+ await expect(
529
+ canvas.getByTestId("navigation-mobile-close-menu"),
530
+ ).not.toBeVisible();
531
+
532
+ await user.click(canvas.getByTestId("navigation-mobile-collapsed-menu"));
533
+
534
+ await expect(canvas.getByLabelText("Menu mobile")).not.toBeVisible();
535
+ await expect(canvas.getByText("Item 0")).not.toBeVisible();
536
+ await expect(
537
+ canvas.getByTestId("navigation-mobile-collapsed-menu"),
538
+ ).toBeVisible();
539
+ await expect(
540
+ canvas.getByTestId("navigation-mobile-close-menu"),
541
+ ).not.toBeVisible();
542
+
543
+ await user.click(canvas.getByTestId("navigation-mobile-collapsed-menu"));
544
+
545
+ await expect(canvas.getByText("Item 0")).toBeVisible();
546
+ },
547
+ };
548
+
549
+ export const MobileShouldOpenNavigationOnClickOnMobileBurgerMenu: Story = {
550
+ args: {
551
+ ...Desktop.args,
552
+ },
553
+ parameters: {
554
+ ...STORYBOOK_VIEWPORTS,
555
+ layout: "none",
556
+ },
557
+ globals: {
558
+ viewport: { value: "iphone6", isRotated: false },
559
+ },
560
+ // TODO: see https://github.com/storybookjs/test-runner/issues/85
561
+ // play: async ({ canvasElement }) => {
562
+ // const canvas = within(canvasElement);
563
+ // const user = userEvent.setup();
564
+
565
+ // await expect(canvas.getByText("Item 0")).not.toBeVisible();
566
+ // await expectPresent(() => canvas.getByLabelText("Menu mobile"));
567
+ // await expect(
568
+ // canvas.getByTestId("navigation-mobile-close-menu"),
569
+ // ).not.toBeVisible();
570
+
571
+ // await user.click(canvas.getByLabelText("Menu mobile"));
572
+
573
+ // await expect(canvas.getByText("Item 0")).toBeVisible();
574
+ // await expect(
575
+ // canvas.getByTestId("navigation-mobile-close-menu"),
576
+ // ).toBeVisible();
577
+
578
+ // await user.click(canvas.getByTestId("navigation-mobile-close-menu"));
579
+
580
+ // await expect(
581
+ // canvas.getByTestId("navigation-mobile-close-menu"),
582
+ // ).not.toBeVisible();
583
+ // await expect(canvas.getByText("Item 0")).not.toBeVisible();
584
+ // },
585
+ };
586
+
587
+ export const MobileShouldCloseNavigationOnClickOnNavigationItem: Story = {
588
+ args: {
589
+ ...Desktop.args,
590
+ },
591
+ parameters: {
592
+ ...STORYBOOK_VIEWPORTS,
593
+ layout: "none",
594
+ },
595
+ globals: {
596
+ viewport: { value: "iphone6", isRotated: false },
597
+ },
598
+ // TODO: see https://github.com/storybookjs/test-runner/issues/85
599
+ // play: async ({ canvasElement }) => {
600
+ // const canvas = within(canvasElement);
601
+ // const user = userEvent.setup();
602
+
603
+ // await expect(canvas.getByText("Item 0")).not.toBeVisible();
604
+ // await expectPresent(() => canvas.getByLabelText("Menu mobile"));
605
+ // await expect(
606
+ // canvas.getByTestId("navigation-mobile-close-menu"),
607
+ // ).not.toBeVisible();
608
+
609
+ // await user.click(canvas.getByTestId("navigation-mobile-close-menu"));
610
+
611
+ // await expect(canvas.getByText("Item 0")).toBeVisible();
612
+ // await expect(
613
+ // canvas.getByTestId("navigation-mobile-close-menu"),
614
+ // ).toBeVisible();
615
+
616
+ // await user.click(canvas.getByText("Item 1"));
617
+
618
+ // await expect(
619
+ // canvas.getByTestId("navigation-mobile-close-menu"),
620
+ // ).not.toBeVisible();
621
+ // await expect(canvas.queryByText("Item 1")).not.toBeVisible();
622
+ // },
623
+ };
624
+ ```
625
+
626
+ ## Developer notes
627
+
628
+ 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.
629
+
630
+ ```mdx
631
+ import {
632
+ Canvas,
633
+ Meta,
634
+ Stories,
635
+ Controls,
636
+ Source,
637
+ } from "@storybook/addon-docs/blocks";
638
+
639
+ import * as PageLayout from "./PageLayout.stories";
640
+ import * as PageLayoutTests from "./tests/PageLayout.stories";
641
+
642
+ <Meta of={PageLayout} />
643
+
644
+ # PageLayout
645
+
646
+ A page layout is a container for all the content of a page.
647
+
648
+ ## Example usage
649
+
650
+ Coming soon...
651
+ ```