@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.
- package/dist/design-system.cjs +9 -5
- package/dist/design-system.js +14 -6
- package/dist/packages/components/Accordion/doc.md +342 -0
- package/dist/packages/components/Badge/doc.md +192 -0
- package/dist/packages/components/Breadcrumbs/doc.md +332 -0
- package/dist/packages/components/Button/doc.md +425 -0
- package/dist/packages/components/Calendar/doc.md +465 -0
- package/dist/packages/components/ChartLegend/doc.md +151 -0
- package/dist/packages/components/ChartTooltip/doc.md +124 -0
- package/dist/packages/components/Checkbox/doc.md +329 -0
- package/dist/packages/components/CheckboxGroup/doc.md +242 -0
- package/dist/packages/components/Chip/doc.md +99 -0
- package/dist/packages/components/Combobox/Combobox.d.ts +8 -0
- package/dist/packages/components/Combobox/doc.md +680 -0
- package/dist/packages/components/DataTable/doc.md +1124 -0
- package/dist/packages/components/DatePicker/doc.md +579 -0
- package/dist/packages/components/DateRangePicker/doc.md +638 -0
- package/dist/packages/components/Drawer/doc.md +338 -0
- package/dist/packages/components/Dropdown/Dropdown.d.ts +4 -0
- package/dist/packages/components/Dropdown/doc.md +205 -0
- package/dist/packages/components/EmptyState/doc.md +101 -0
- package/dist/packages/components/FileUpload/doc.md +449 -0
- package/dist/packages/components/Filter/doc.md +196 -0
- package/dist/packages/components/Header/doc.md +373 -0
- package/dist/packages/components/I18nProvider/doc.md +187 -0
- package/dist/packages/components/Icon/doc.md +63 -0
- package/dist/packages/components/Label/doc.md +60 -0
- package/dist/packages/components/LinearProgressBar/doc.md +148 -0
- package/dist/packages/components/Link/doc.md +206 -0
- package/dist/packages/components/List/doc.md +481 -0
- package/dist/packages/components/Loader/doc.md +53 -0
- package/dist/packages/components/Menu/Menu.d.ts +5 -1
- package/dist/packages/components/Menu/doc.md +231 -0
- package/dist/packages/components/Message/doc.md +166 -0
- package/dist/packages/components/Modal/doc.md +289 -0
- package/dist/packages/components/Navigation/doc.md +992 -0
- package/dist/packages/components/NavigationItem/doc.md +167 -0
- package/dist/packages/components/NotificationCard/doc.md +206 -0
- package/dist/packages/components/Notifications/doc.md +240 -0
- package/dist/packages/components/NumberField/doc.md +582 -0
- package/dist/packages/components/PageLayout/doc.md +651 -0
- package/dist/packages/components/Pagination/doc.md +227 -0
- package/dist/packages/components/Popover/doc.md +245 -0
- package/dist/packages/components/Radio/doc.md +370 -0
- package/dist/packages/components/RouterProvider/doc.md +64 -0
- package/dist/packages/components/SearchBar/doc.md +504 -0
- package/dist/packages/components/SegmentedControl/doc.md +398 -0
- package/dist/packages/components/Select/Select.d.ts +4 -0
- package/dist/packages/components/Select/doc.md +1133 -0
- package/dist/packages/components/Skeleton/doc.md +129 -0
- package/dist/packages/components/Slider/doc.md +362 -0
- package/dist/packages/components/Stepper/doc.md +104 -0
- package/dist/packages/components/Switch/doc.md +296 -0
- package/dist/packages/components/Tabs/doc.md +295 -0
- package/dist/packages/components/Tag/doc.md +81 -0
- package/dist/packages/components/TextInput/doc.md +490 -0
- package/dist/packages/components/TimeField/doc.md +353 -0
- package/dist/packages/components/Timeline/doc.md +1046 -0
- package/dist/packages/components/Toaster/doc.md +263 -0
- package/dist/packages/components/ToggleButton/doc.md +108 -0
- package/dist/packages/components/ToggleButtonGroup/doc.md +307 -0
- package/dist/packages/components/Tooltip/doc.md +206 -0
- package/dist/packages/components/YearMonthPicker/YearMonthPicker.d.ts +8 -0
- package/dist/packages/components/YearMonthPicker/doc.md +638 -0
- package/dist/public_docs/components.md +68 -0
- package/dist/public_docs/index.md +30 -0
- package/dist/public_docs/tokens.md +121 -0
- package/package.json +3 -2
|
@@ -0,0 +1,992 @@
|
|
|
1
|
+
# Navigation
|
|
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/Navigation/Navigation.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 Navigation from "./Navigation";
|
|
18
|
+
import NavigationItem from "../NavigationItem/NavigationItem";
|
|
19
|
+
import { I18nProvider } from "react-aria-components";
|
|
20
|
+
import { fn } from "storybook/test";
|
|
21
|
+
|
|
22
|
+
const meta: Meta<typeof Navigation> = {
|
|
23
|
+
component: Navigation,
|
|
24
|
+
parameters: {
|
|
25
|
+
layout: "none",
|
|
26
|
+
},
|
|
27
|
+
argTypes: {
|
|
28
|
+
children: { control: false },
|
|
29
|
+
},
|
|
30
|
+
decorators: [
|
|
31
|
+
(Story) => (
|
|
32
|
+
<I18nProvider locale="EN-en">
|
|
33
|
+
<Story />
|
|
34
|
+
</I18nProvider>
|
|
35
|
+
),
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
export default meta;
|
|
39
|
+
|
|
40
|
+
type Story = StoryObj<typeof meta>;
|
|
41
|
+
|
|
42
|
+
export const Playground: Story = {
|
|
43
|
+
args: {
|
|
44
|
+
logo: "agregio",
|
|
45
|
+
onParametersClick: fn(),
|
|
46
|
+
onLogoutClick: fn(),
|
|
47
|
+
children: (
|
|
48
|
+
<>
|
|
49
|
+
<NavigationItem label="Item 1" href="/item-1" iconLeft="home" />
|
|
50
|
+
|
|
51
|
+
<NavigationItem
|
|
52
|
+
label="Item 2"
|
|
53
|
+
iconLeft="business"
|
|
54
|
+
subLinks={[
|
|
55
|
+
{ href: "/item-2-1", label: "Item 2.1" },
|
|
56
|
+
{ href: "/item-2-2", label: "Item 2.2" },
|
|
57
|
+
]}
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
<NavigationItem
|
|
61
|
+
label="Item 3"
|
|
62
|
+
href="/item-3"
|
|
63
|
+
iconLeft="bolt"
|
|
64
|
+
isActive
|
|
65
|
+
/>
|
|
66
|
+
</>
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const NoSettings: Story = {
|
|
72
|
+
args: {
|
|
73
|
+
...Playground.args,
|
|
74
|
+
onParametersClick: undefined,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const NoLogout: Story = {
|
|
79
|
+
args: {
|
|
80
|
+
...Playground.args,
|
|
81
|
+
onLogoutClick: undefined,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const NoFooter: Story = {
|
|
86
|
+
args: {
|
|
87
|
+
...Playground.args,
|
|
88
|
+
onParametersClick: undefined,
|
|
89
|
+
onLogoutClick: undefined,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const InitialCollapsed: Story = {
|
|
94
|
+
args: {
|
|
95
|
+
...Playground.args,
|
|
96
|
+
initialIsCollapsed: true,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const WithSubItemActive: Story = {
|
|
101
|
+
args: {
|
|
102
|
+
...Playground.args,
|
|
103
|
+
children: (
|
|
104
|
+
<>
|
|
105
|
+
<NavigationItem
|
|
106
|
+
label="Item 1"
|
|
107
|
+
iconLeft="business"
|
|
108
|
+
defaultOpen
|
|
109
|
+
subLinks={[
|
|
110
|
+
{ href: "/item-2-1", label: "Item 1.1" },
|
|
111
|
+
{ href: "/item-2-2", label: "Item 1.2", isActive: true },
|
|
112
|
+
]}
|
|
113
|
+
/>
|
|
114
|
+
</>
|
|
115
|
+
),
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const LotsOfItems: Story = {
|
|
120
|
+
args: {
|
|
121
|
+
...Playground.args,
|
|
122
|
+
children: (
|
|
123
|
+
<>
|
|
124
|
+
{Array.from({ length: 20 }).map((_, index) => (
|
|
125
|
+
<NavigationItem
|
|
126
|
+
key={index}
|
|
127
|
+
label={`Item ${index + 1}`}
|
|
128
|
+
iconLeft="business"
|
|
129
|
+
/>
|
|
130
|
+
))}
|
|
131
|
+
</>
|
|
132
|
+
),
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const WithFooterElements: Story = {
|
|
137
|
+
args: {
|
|
138
|
+
...Playground.args,
|
|
139
|
+
footerElements: (
|
|
140
|
+
<>
|
|
141
|
+
<NavigationItem label="FAQ" iconLeft="help_outline" />
|
|
142
|
+
</>
|
|
143
|
+
),
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## How to test this component
|
|
149
|
+
|
|
150
|
+
Here are some more advanced stories with more testing coverage and examples that you can read to understand how to test this component.
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import { Meta, StoryObj } from "@storybook/react-vite";
|
|
154
|
+
import Navigation from "../Navigation";
|
|
155
|
+
import NavigationItem from "../../NavigationItem/NavigationItem";
|
|
156
|
+
import * as NavigationStories from "../Navigation.stories";
|
|
157
|
+
import { expect, userEvent, waitFor, within } from "storybook/test";
|
|
158
|
+
import {
|
|
159
|
+
expectNotPresent,
|
|
160
|
+
expectPresent,
|
|
161
|
+
} from "@internal/test-utils-storybook/test-utils-storybook";
|
|
162
|
+
import { Link, Outlet, Route, Routes, useLocation } from "react-router-dom";
|
|
163
|
+
|
|
164
|
+
const meta: Meta<typeof Navigation> = {
|
|
165
|
+
...NavigationStories.default,
|
|
166
|
+
parameters: {
|
|
167
|
+
...NavigationStories.default.parameters,
|
|
168
|
+
chromatic: { disableSnapshot: true },
|
|
169
|
+
},
|
|
170
|
+
component: Navigation,
|
|
171
|
+
};
|
|
172
|
+
export default meta;
|
|
173
|
+
|
|
174
|
+
type Story = StoryObj<typeof meta>;
|
|
175
|
+
|
|
176
|
+
export const BasicExampleUsage: Story = {
|
|
177
|
+
render: () => (
|
|
178
|
+
<Navigation logo="agregio">
|
|
179
|
+
<NavigationItem
|
|
180
|
+
label="Item 1"
|
|
181
|
+
iconLeft="business"
|
|
182
|
+
subLinks={[
|
|
183
|
+
{ href: "/page-2/item-1", label: "Item 1.1" },
|
|
184
|
+
{ href: "/page-2/item-2", label: "Item 1.2" },
|
|
185
|
+
]}
|
|
186
|
+
/>
|
|
187
|
+
</Navigation>
|
|
188
|
+
),
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export const ShouldHandleSubLinksWhenTheNavigationIsNotCollapsed: Story = {
|
|
192
|
+
render: BasicExampleUsage.render,
|
|
193
|
+
play: async ({ canvasElement }) => {
|
|
194
|
+
const canvas = within(canvasElement);
|
|
195
|
+
const user = userEvent.setup();
|
|
196
|
+
await canvas.findByText("Item 1");
|
|
197
|
+
await expectNotPresent(() => canvas.queryByText("Item 1.1"));
|
|
198
|
+
await expectNotPresent(() => canvas.queryByText("Item 1.2"));
|
|
199
|
+
|
|
200
|
+
await user.click(canvas.getByText("Item 1"));
|
|
201
|
+
await expectPresent(() => canvas.queryByText("Item 1.1"));
|
|
202
|
+
await expectPresent(() => canvas.queryByText("Item 1.2"));
|
|
203
|
+
|
|
204
|
+
await user.click(canvas.getByText("Item 1"));
|
|
205
|
+
await expectNotPresent(() => canvas.queryByText("Item 1.1"));
|
|
206
|
+
await expectNotPresent(() => canvas.queryByText("Item 1.2"));
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
export const ShouldHandleSubLinksWhenTheNavigationIsCollapsed: Story = {
|
|
211
|
+
render: BasicExampleUsage.render,
|
|
212
|
+
play: async ({ canvasElement }) => {
|
|
213
|
+
const canvas = within(canvasElement);
|
|
214
|
+
const user = userEvent.setup();
|
|
215
|
+
|
|
216
|
+
await user.click(canvas.getByTestId("navigation-mobile-collapsed-menu"));
|
|
217
|
+
|
|
218
|
+
await expect(canvas.getByText("Item 1")).not.toBeVisible();
|
|
219
|
+
await expectNotPresent(() => canvas.queryByText("Item 1.1"));
|
|
220
|
+
await expectNotPresent(() => canvas.queryByText("Item 1.2"));
|
|
221
|
+
|
|
222
|
+
await user.click(canvas.getByLabelText("Item 1"));
|
|
223
|
+
|
|
224
|
+
await canvas.findByText("Item 1");
|
|
225
|
+
await expectPresent(() => canvas.queryByText("Item 1.1"));
|
|
226
|
+
await expectPresent(() => canvas.queryByText("Item 1.2"));
|
|
227
|
+
|
|
228
|
+
await user.click(canvas.getByTestId("navigation-mobile-collapsed-menu"));
|
|
229
|
+
await expect(canvas.getByText("Item 1")).not.toBeVisible();
|
|
230
|
+
await expectNotPresent(() => canvas.queryByText("Item 1.1"));
|
|
231
|
+
await expectNotPresent(() => canvas.queryByText("Item 1.2"));
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export const BaseStoryForTheOpenStateTests: Story = {
|
|
236
|
+
render: () => {
|
|
237
|
+
const AppExample = () => {
|
|
238
|
+
const location = useLocation();
|
|
239
|
+
const path = location.pathname;
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
<div style={{ display: "flex", gap: "1rem" }}>
|
|
243
|
+
<Navigation logo="agregio" currentPath={path}>
|
|
244
|
+
<NavigationItem
|
|
245
|
+
label="Page 1"
|
|
246
|
+
iconLeft="business"
|
|
247
|
+
subLinks={[
|
|
248
|
+
{ href: "/page-1/item-1", label: "Item 1.1" },
|
|
249
|
+
{ href: "/page-1/item-2", label: "Item 1.2" },
|
|
250
|
+
]}
|
|
251
|
+
/>
|
|
252
|
+
<NavigationItem
|
|
253
|
+
label="Page 2"
|
|
254
|
+
iconLeft="access_time"
|
|
255
|
+
subLinks={[
|
|
256
|
+
{ href: "/page-2/item-1", label: "Item 2.1" },
|
|
257
|
+
{ href: "/page-2/item-2", label: "Item 2.2" },
|
|
258
|
+
]}
|
|
259
|
+
/>
|
|
260
|
+
</Navigation>
|
|
261
|
+
|
|
262
|
+
<main>
|
|
263
|
+
<ul>
|
|
264
|
+
<li>
|
|
265
|
+
<Link to="/">Link to: Home</Link>
|
|
266
|
+
</li>
|
|
267
|
+
<li>
|
|
268
|
+
<Link to="/page-1/item-1">Link to: Item 1.1</Link>
|
|
269
|
+
</li>
|
|
270
|
+
<li>
|
|
271
|
+
<Link to="/page-1/item-2">Link to: Item 1.2</Link>
|
|
272
|
+
</li>
|
|
273
|
+
<li>
|
|
274
|
+
<Link to="/page-2/item-1">Link to: Item 2.1</Link>
|
|
275
|
+
</li>
|
|
276
|
+
<li>
|
|
277
|
+
<Link to="/page-2/item-2">Link to: Item 2.2</Link>
|
|
278
|
+
</li>
|
|
279
|
+
</ul>
|
|
280
|
+
|
|
281
|
+
<Outlet />
|
|
282
|
+
</main>
|
|
283
|
+
</div>
|
|
284
|
+
);
|
|
285
|
+
};
|
|
286
|
+
return (
|
|
287
|
+
<Routes>
|
|
288
|
+
<Route element={<AppExample />}>
|
|
289
|
+
<Route path="/" element={<div>Page: /</div>} />
|
|
290
|
+
<Route
|
|
291
|
+
path="/page-1/item-1"
|
|
292
|
+
element={<div>Page: /page-1/item-1</div>}
|
|
293
|
+
/>
|
|
294
|
+
<Route
|
|
295
|
+
path="/page-1/item-2"
|
|
296
|
+
element={<div>Page: /page-1/item-2</div>}
|
|
297
|
+
/>
|
|
298
|
+
<Route
|
|
299
|
+
path="/page-2/item-1"
|
|
300
|
+
element={<div>Page: /page-2/item-1</div>}
|
|
301
|
+
/>
|
|
302
|
+
<Route
|
|
303
|
+
path="/page-2/item-2"
|
|
304
|
+
element={<div>Page: /page-2/item-2</div>}
|
|
305
|
+
/>
|
|
306
|
+
</Route>
|
|
307
|
+
</Routes>
|
|
308
|
+
);
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// # Tests en commençant avec l'URL / et en cliquant en premier sur "Page 1"
|
|
313
|
+
|
|
314
|
+
// ## Test : L'utilisateur clique sur "Page 1"
|
|
315
|
+
// - le dropdown de "Page 1" s'ouvre
|
|
316
|
+
|
|
317
|
+
export const ShouldOpenTheDropDownMenu: Story = {
|
|
318
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
319
|
+
parameters: {
|
|
320
|
+
reactRouter: {
|
|
321
|
+
initialEntries: ["/"],
|
|
322
|
+
customRoutes: true,
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
play: async ({ canvasElement }) => {
|
|
326
|
+
const canvas = within(canvasElement);
|
|
327
|
+
const user = userEvent.setup();
|
|
328
|
+
|
|
329
|
+
await canvas.findByText("Page: /");
|
|
330
|
+
await canvas.findByText("Page 1");
|
|
331
|
+
await expect(canvas.queryByText("Item 1.1")).not.toBeInTheDocument();
|
|
332
|
+
await expect(canvas.queryByText("Item 1.2")).not.toBeInTheDocument();
|
|
333
|
+
await canvas.findByText("Page 2");
|
|
334
|
+
await expect(canvas.queryByText("Item 2.1")).not.toBeInTheDocument();
|
|
335
|
+
await expect(canvas.queryByText("Item 2.2")).not.toBeInTheDocument();
|
|
336
|
+
|
|
337
|
+
await user.click(canvas.getByText("Page 1"));
|
|
338
|
+
|
|
339
|
+
await canvas.findByText("Page 1");
|
|
340
|
+
await canvas.findByText("Item 1.1");
|
|
341
|
+
await canvas.findByText("Item 1.2");
|
|
342
|
+
await canvas.findByText("Page 2");
|
|
343
|
+
await expect(canvas.queryByText("Item 2.1")).not.toBeInTheDocument();
|
|
344
|
+
await expect(canvas.queryByText("Item 2.2")).not.toBeInTheDocument();
|
|
345
|
+
await canvas.findByText("Page: /");
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// ## Test: L'utilisateur clique sur "Page 1" puis à nouveau sur "Page 1"
|
|
350
|
+
// - le dropdown de "Page 1" s'ouvre puis se ferme
|
|
351
|
+
|
|
352
|
+
export const ShouldToggleTheDropDownMenu: Story = {
|
|
353
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
354
|
+
parameters: {
|
|
355
|
+
reactRouter: {
|
|
356
|
+
initialEntries: ["/"],
|
|
357
|
+
customRoutes: true,
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
play: async ({ canvasElement }) => {
|
|
361
|
+
const canvas = within(canvasElement);
|
|
362
|
+
const user = userEvent.setup();
|
|
363
|
+
|
|
364
|
+
await canvas.findByText("Page: /");
|
|
365
|
+
await canvas.findByText("Page 1");
|
|
366
|
+
await expect(canvas.queryByText("Item 1.1")).not.toBeInTheDocument();
|
|
367
|
+
await expect(canvas.queryByText("Item 1.2")).not.toBeInTheDocument();
|
|
368
|
+
|
|
369
|
+
await user.click(canvas.getByText("Page 1"));
|
|
370
|
+
await canvas.findByText("Item 1.1");
|
|
371
|
+
await canvas.findByText("Item 1.2");
|
|
372
|
+
|
|
373
|
+
await user.click(canvas.getByText("Page 1"));
|
|
374
|
+
await expect(canvas.queryByText("Item 1.1")).not.toBeInTheDocument();
|
|
375
|
+
await expect(canvas.queryByText("Item 1.2")).not.toBeInTheDocument();
|
|
376
|
+
await canvas.findByText("Page: /");
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
// ## Test : L'utilisateur clique sur "Page 1" puis sur "Page 2"
|
|
381
|
+
// - le dropdown de "Page 1" s'ouvre
|
|
382
|
+
// - le dropdown de "Page 2" s'ouvre
|
|
383
|
+
// - les 2 dropdowns sont ouverts
|
|
384
|
+
|
|
385
|
+
export const ShouldOpenBothDropDownMenus: Story = {
|
|
386
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
387
|
+
parameters: {
|
|
388
|
+
reactRouter: {
|
|
389
|
+
initialEntries: ["/"],
|
|
390
|
+
customRoutes: true,
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
play: async ({ canvasElement }) => {
|
|
394
|
+
const canvas = within(canvasElement);
|
|
395
|
+
const user = userEvent.setup();
|
|
396
|
+
|
|
397
|
+
await canvas.findByText("Page: /");
|
|
398
|
+
await canvas.findByText("Page 1");
|
|
399
|
+
await expect(canvas.queryByText("Item 1.1")).not.toBeInTheDocument();
|
|
400
|
+
await expect(canvas.queryByText("Item 1.2")).not.toBeInTheDocument();
|
|
401
|
+
await canvas.findByText("Page 2");
|
|
402
|
+
await expect(canvas.queryByText("Item 2.1")).not.toBeInTheDocument();
|
|
403
|
+
await expect(canvas.queryByText("Item 2.2")).not.toBeInTheDocument();
|
|
404
|
+
|
|
405
|
+
await user.click(canvas.getByText("Page 1"));
|
|
406
|
+
await canvas.findByText("Page 1");
|
|
407
|
+
await canvas.findByText("Item 1.1");
|
|
408
|
+
await canvas.findByText("Item 1.2");
|
|
409
|
+
await canvas.findByText("Page 2");
|
|
410
|
+
await expect(canvas.queryByText("Item 2.1")).not.toBeInTheDocument();
|
|
411
|
+
await expect(canvas.queryByText("Item 2.2")).not.toBeInTheDocument();
|
|
412
|
+
|
|
413
|
+
await user.click(canvas.getByText("Page 2"));
|
|
414
|
+
await canvas.findByText("Page 1");
|
|
415
|
+
await canvas.findByText("Item 1.1");
|
|
416
|
+
await canvas.findByText("Item 1.2");
|
|
417
|
+
await canvas.findByText("Page 2");
|
|
418
|
+
await canvas.findByText("Item 2.1");
|
|
419
|
+
await canvas.findByText("Item 2.2");
|
|
420
|
+
await canvas.findByText("Page: /");
|
|
421
|
+
},
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
// ## Test : L'utilisateur clique sur "Page 1" puis sur "Sous menu 1.1"
|
|
425
|
+
// - le dropdown de "Page 1" s'ouvre
|
|
426
|
+
// - "Sous menu 1.1" devient "active"
|
|
427
|
+
|
|
428
|
+
export const ShouldClickOnPage1AndThenOnItem1: Story = {
|
|
429
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
430
|
+
parameters: {
|
|
431
|
+
reactRouter: {
|
|
432
|
+
initialEntries: ["/"],
|
|
433
|
+
customRoutes: true,
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
play: async ({ canvasElement }) => {
|
|
437
|
+
const canvas = within(canvasElement);
|
|
438
|
+
const user = userEvent.setup();
|
|
439
|
+
|
|
440
|
+
await canvas.findByText("Page: /");
|
|
441
|
+
await user.click(canvas.getByText("Page 1"));
|
|
442
|
+
await user.click(canvas.getByText("Item 1.1"));
|
|
443
|
+
|
|
444
|
+
await waitFor(async () => {
|
|
445
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
446
|
+
await canvas.findByText("Page 1");
|
|
447
|
+
await expect(canvas.getByText("Page 1").parentElement).toHaveAttribute(
|
|
448
|
+
"data-active",
|
|
449
|
+
"true",
|
|
450
|
+
);
|
|
451
|
+
await expect(canvas.getByText("Item 1.1").parentElement).toHaveAttribute(
|
|
452
|
+
"data-active",
|
|
453
|
+
"true",
|
|
454
|
+
);
|
|
455
|
+
await canvas.findByText("Item 1.2");
|
|
456
|
+
await canvas.findByText("Page 2");
|
|
457
|
+
await expect(canvas.queryByText("Item 2.1")).not.toBeInTheDocument();
|
|
458
|
+
await expect(canvas.queryByText("Item 2.2")).not.toBeInTheDocument();
|
|
459
|
+
});
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
// ## Test : L'utilisateur clique sur "Page 1" puis sur "Sous menu 1.1" puis à nouveau sur "Page 1"
|
|
464
|
+
// - le dropdown de "Page 1" s'ouvre
|
|
465
|
+
// - "Sous menu 1.1" devient "active" ainsi que "Page 1"
|
|
466
|
+
// - le dropdown de "Page 1" se ferme mais reste "active"
|
|
467
|
+
|
|
468
|
+
export const DropdownShouldStayActiveIfItHasASubLinkActiveButIsCosed: Story = {
|
|
469
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
470
|
+
parameters: {
|
|
471
|
+
reactRouter: {
|
|
472
|
+
initialEntries: ["/"],
|
|
473
|
+
customRoutes: true,
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
play: async ({ canvasElement }) => {
|
|
477
|
+
const canvas = within(canvasElement);
|
|
478
|
+
const user = userEvent.setup();
|
|
479
|
+
|
|
480
|
+
await canvas.findByText("Page: /");
|
|
481
|
+
await user.click(canvas.getByText("Page 1"));
|
|
482
|
+
await user.click(canvas.getByText("Item 1.1"));
|
|
483
|
+
await user.click(canvas.getByText("Page 1"));
|
|
484
|
+
|
|
485
|
+
await waitFor(async () => {
|
|
486
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
487
|
+
await canvas.findByText("Page 1");
|
|
488
|
+
await expect(canvas.getByText("Page 1").parentElement).toHaveAttribute(
|
|
489
|
+
"data-active",
|
|
490
|
+
"true",
|
|
491
|
+
);
|
|
492
|
+
await expect(canvas.queryByText("Item 1.1")).not.toBeInTheDocument();
|
|
493
|
+
await expect(canvas.queryByText("Item 1.2")).not.toBeInTheDocument();
|
|
494
|
+
await canvas.findByText("Page 2");
|
|
495
|
+
await expect(canvas.queryByText("Item 2.1")).not.toBeInTheDocument();
|
|
496
|
+
await expect(canvas.queryByText("Item 2.2")).not.toBeInTheDocument();
|
|
497
|
+
});
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
// # Tests en commençant avec l'URL / et en suivant un lien qui mène à "Sous menu 1.1"
|
|
502
|
+
|
|
503
|
+
// ## Test : L'utilisateur suit un lien qui mène à "Sous menu 1.1"
|
|
504
|
+
// - le dropdown de "Page 1" s'ouvre
|
|
505
|
+
// - "Sous menu 1.1" devient "active"
|
|
506
|
+
|
|
507
|
+
export const UserFollowsALinkThatLeadsToItem1: Story = {
|
|
508
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
509
|
+
parameters: {
|
|
510
|
+
reactRouter: {
|
|
511
|
+
initialEntries: ["/"],
|
|
512
|
+
customRoutes: true,
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
play: async ({ canvasElement }) => {
|
|
516
|
+
const canvas = within(canvasElement);
|
|
517
|
+
const user = userEvent.setup();
|
|
518
|
+
|
|
519
|
+
await canvas.findByText("Page: /");
|
|
520
|
+
await user.click(canvas.getByText("Link to: Item 1.1"));
|
|
521
|
+
|
|
522
|
+
await waitFor(async () => {
|
|
523
|
+
await canvas.findByText("Page 1");
|
|
524
|
+
await canvas.findByText("Item 1.1");
|
|
525
|
+
await canvas.findByText("Item 1.2");
|
|
526
|
+
await canvas.findByText("Page 2");
|
|
527
|
+
await expect(canvas.queryByText("Item 2.1")).not.toBeInTheDocument();
|
|
528
|
+
await expect(canvas.queryByText("Item 2.2")).not.toBeInTheDocument();
|
|
529
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
530
|
+
});
|
|
531
|
+
},
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
// ## Test : L'utilisateur suit un lien qui mène à "Sous menu 1.1" puis clique sur "Page 1"
|
|
535
|
+
// - le dropdown de "Page 1" s'ouvre
|
|
536
|
+
// - "Sous menu 1.1" devient "active"
|
|
537
|
+
// - puis le dropdown de "Page 1" se ferme
|
|
538
|
+
|
|
539
|
+
export const UserFollowsALinkThatLeadsToItem1AndThenClosesTheDropdown: Story = {
|
|
540
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
541
|
+
parameters: {
|
|
542
|
+
reactRouter: {
|
|
543
|
+
initialEntries: ["/"],
|
|
544
|
+
customRoutes: true,
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
play: async ({ canvasElement }) => {
|
|
548
|
+
const canvas = within(canvasElement);
|
|
549
|
+
const user = userEvent.setup();
|
|
550
|
+
|
|
551
|
+
await canvas.findByText("Page: /");
|
|
552
|
+
await user.click(canvas.getByText("Link to: Item 1.1"));
|
|
553
|
+
await user.click(canvas.getByText("Page 1"));
|
|
554
|
+
|
|
555
|
+
await waitFor(async () => {
|
|
556
|
+
await canvas.findByText("Page 1");
|
|
557
|
+
await expect(canvas.getByText("Page 1").parentElement).toHaveAttribute(
|
|
558
|
+
"data-active",
|
|
559
|
+
"true",
|
|
560
|
+
);
|
|
561
|
+
await expect(canvas.queryByText("Item 1.1")).not.toBeInTheDocument();
|
|
562
|
+
await expect(canvas.queryByText("Item 1.2")).not.toBeInTheDocument();
|
|
563
|
+
await canvas.findByText("Page 2");
|
|
564
|
+
await expect(canvas.queryByText("Item 2.1")).not.toBeInTheDocument();
|
|
565
|
+
await expect(canvas.queryByText("Item 2.2")).not.toBeInTheDocument();
|
|
566
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
567
|
+
});
|
|
568
|
+
},
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
// ## Test : L'utilisateur suit un lien qui mène à "Sous menu 1.1" puis clique sur "Page 2"
|
|
572
|
+
// - le dropdown de "Page 1" s'ouvre
|
|
573
|
+
// - "Sous menu 1.1" devient "active"
|
|
574
|
+
// - le dropdown de "Page 2" s'ouvre
|
|
575
|
+
// - les 2 dropdowns sont ouverts
|
|
576
|
+
|
|
577
|
+
export const UserFollowsALinkThatLeadsToItem1AndThenOpensTheDropdown2: Story = {
|
|
578
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
579
|
+
parameters: {
|
|
580
|
+
reactRouter: {
|
|
581
|
+
initialEntries: ["/"],
|
|
582
|
+
customRoutes: true,
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
play: async ({ canvasElement }) => {
|
|
586
|
+
const canvas = within(canvasElement);
|
|
587
|
+
const user = userEvent.setup();
|
|
588
|
+
|
|
589
|
+
await canvas.findByText("Page: /");
|
|
590
|
+
await user.click(canvas.getByText("Link to: Item 1.1"));
|
|
591
|
+
await user.click(canvas.getByText("Page 2"));
|
|
592
|
+
|
|
593
|
+
await waitFor(async () => {
|
|
594
|
+
await canvas.findByText("Page 1");
|
|
595
|
+
await expect(canvas.getByText("Page 1").parentElement).toHaveAttribute(
|
|
596
|
+
"data-active",
|
|
597
|
+
"true",
|
|
598
|
+
);
|
|
599
|
+
await expect(canvas.getByText("Item 1.1").parentElement).toHaveAttribute(
|
|
600
|
+
"data-active",
|
|
601
|
+
"true",
|
|
602
|
+
);
|
|
603
|
+
await canvas.findByText("Item 1.2");
|
|
604
|
+
await canvas.findByText("Page 2");
|
|
605
|
+
await canvas.findByText("Item 2.1");
|
|
606
|
+
await canvas.findByText("Item 2.2");
|
|
607
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
608
|
+
});
|
|
609
|
+
},
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// # Tests en commençant avec l'URL de "Sous menu 1.1"
|
|
613
|
+
|
|
614
|
+
// ## Test : L'utilisateur ouvre une page qui mène à "Sous menu 1.1"
|
|
615
|
+
|
|
616
|
+
// - le dropdown de "Page 1" est déjà ouvert
|
|
617
|
+
// - "Sous menu 1.1" est déjà "active"
|
|
618
|
+
|
|
619
|
+
export const UserStartsFromItem1: Story = {
|
|
620
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
621
|
+
parameters: {
|
|
622
|
+
reactRouter: {
|
|
623
|
+
initialEntries: ["/page-1/item-1"],
|
|
624
|
+
customRoutes: true,
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
play: async ({ canvasElement }) => {
|
|
628
|
+
const canvas = within(canvasElement);
|
|
629
|
+
|
|
630
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
631
|
+
await expect(canvas.getByText("Page 1").parentElement).toHaveAttribute(
|
|
632
|
+
"data-active",
|
|
633
|
+
"true",
|
|
634
|
+
);
|
|
635
|
+
await canvas.findByText("Item 1.1");
|
|
636
|
+
await expect(canvas.getByText("Item 1.1").parentElement).toHaveAttribute(
|
|
637
|
+
"data-active",
|
|
638
|
+
"true",
|
|
639
|
+
);
|
|
640
|
+
await canvas.findByText("Item 1.2");
|
|
641
|
+
await canvas.findByText("Page 2");
|
|
642
|
+
await expect(canvas.queryByText("Item 2.1")).not.toBeInTheDocument();
|
|
643
|
+
await expect(canvas.queryByText("Item 2.2")).not.toBeInTheDocument();
|
|
644
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
645
|
+
},
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
// ## Test : L'utilisateur ouvre une page qui mène à "Sous menu 1.1" puis clique sur "Page 1"
|
|
649
|
+
|
|
650
|
+
// - le dropdown de "Page 1" est déjà ouvert
|
|
651
|
+
// - "Sous menu 1.1" est déjà "active"
|
|
652
|
+
// - puis le dropdown de "Page 1" se ferme
|
|
653
|
+
export const UserStartsFromItem1AndThenOpensTheDropdown1: Story = {
|
|
654
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
655
|
+
parameters: {
|
|
656
|
+
reactRouter: {
|
|
657
|
+
initialEntries: ["/page-1/item-1"],
|
|
658
|
+
customRoutes: true,
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
play: async ({ canvasElement }) => {
|
|
662
|
+
const canvas = within(canvasElement);
|
|
663
|
+
const user = userEvent.setup();
|
|
664
|
+
|
|
665
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
666
|
+
await user.click(canvas.getByText("Page 1"));
|
|
667
|
+
|
|
668
|
+
await waitFor(async () => {
|
|
669
|
+
await expect(canvas.getByText("Page 1").parentElement).toHaveAttribute(
|
|
670
|
+
"data-active",
|
|
671
|
+
"true",
|
|
672
|
+
);
|
|
673
|
+
await expect(canvas.queryByText("Item 1.1")).not.toBeInTheDocument();
|
|
674
|
+
await expect(canvas.queryByText("Item 1.2")).not.toBeInTheDocument();
|
|
675
|
+
await canvas.findByText("Page 2");
|
|
676
|
+
await expect(canvas.queryByText("Item 2.1")).not.toBeInTheDocument();
|
|
677
|
+
await expect(canvas.queryByText("Item 2.2")).not.toBeInTheDocument();
|
|
678
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
679
|
+
});
|
|
680
|
+
},
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// ## Test : L'utilisateur ouvre une page qui mène à "Sous menu 1.1" puis clique sur "Page 2"
|
|
684
|
+
|
|
685
|
+
// - le dropdown de "Page 1" s'ouvre
|
|
686
|
+
// - le dropdown de "Page 2" s'ouvre
|
|
687
|
+
// - les 2 dropdowns sont ouverts
|
|
688
|
+
export const UserStartsFromItem1AndThenOpensTheDropdown2: Story = {
|
|
689
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
690
|
+
parameters: {
|
|
691
|
+
reactRouter: {
|
|
692
|
+
initialEntries: ["/page-1/item-1"],
|
|
693
|
+
customRoutes: true,
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
play: async ({ canvasElement }) => {
|
|
697
|
+
const canvas = within(canvasElement);
|
|
698
|
+
const user = userEvent.setup();
|
|
699
|
+
|
|
700
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
701
|
+
await user.click(canvas.getByText("Page 2"));
|
|
702
|
+
|
|
703
|
+
await waitFor(async () => {
|
|
704
|
+
await expect(canvas.getByText("Page 1").parentElement).toHaveAttribute(
|
|
705
|
+
"data-active",
|
|
706
|
+
"true",
|
|
707
|
+
);
|
|
708
|
+
await expect(canvas.getByText("Item 1.1").parentElement).toHaveAttribute(
|
|
709
|
+
"data-active",
|
|
710
|
+
"true",
|
|
711
|
+
);
|
|
712
|
+
await canvas.findByText("Item 1.2");
|
|
713
|
+
await canvas.findByText("Page 2");
|
|
714
|
+
await canvas.findByText("Item 2.1");
|
|
715
|
+
await canvas.findByText("Item 2.2");
|
|
716
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
717
|
+
});
|
|
718
|
+
},
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
// ## Test : L'utilisateur ouvre une page qui mène à "Sous menu 1.1" puis suit un lien vers "Sous menu 1.2"
|
|
722
|
+
|
|
723
|
+
// - le dropdown de "Page 1" est déjà ouvert
|
|
724
|
+
// - "Sous menu 1.1" est déjà "active"
|
|
725
|
+
// - "Sous menu 1.2" devient "active" à la place de "Sous menu 1.1"
|
|
726
|
+
export const UserStartsFromItem1AndThenFollowsALinkToItem12: Story = {
|
|
727
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
728
|
+
parameters: {
|
|
729
|
+
reactRouter: {
|
|
730
|
+
initialEntries: ["/page-1/item-1"],
|
|
731
|
+
customRoutes: true,
|
|
732
|
+
},
|
|
733
|
+
},
|
|
734
|
+
play: async ({ canvasElement }) => {
|
|
735
|
+
const canvas = within(canvasElement);
|
|
736
|
+
const user = userEvent.setup();
|
|
737
|
+
|
|
738
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
739
|
+
await user.click(canvas.getByText("Link to: Item 1.2"));
|
|
740
|
+
|
|
741
|
+
await waitFor(async () => {
|
|
742
|
+
await expect(canvas.getByText("Page 1").parentElement).toHaveAttribute(
|
|
743
|
+
"data-active",
|
|
744
|
+
"true",
|
|
745
|
+
);
|
|
746
|
+
await canvas.findByText("Item 1.1");
|
|
747
|
+
await expect(canvas.getByText("Item 1.2").parentElement).toHaveAttribute(
|
|
748
|
+
"data-active",
|
|
749
|
+
"true",
|
|
750
|
+
);
|
|
751
|
+
await canvas.findByText("Page 2");
|
|
752
|
+
await expect(canvas.queryByText("Item 2.1")).not.toBeInTheDocument();
|
|
753
|
+
await expect(canvas.queryByText("Item 2.2")).not.toBeInTheDocument();
|
|
754
|
+
await canvas.findByText("Page: /page-1/item-2");
|
|
755
|
+
});
|
|
756
|
+
},
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
// ## Test : L'utilisateur ouvre une page qui mène à "Sous menu 1.1" puis suit un lien vers "Sous menu 2.1"
|
|
760
|
+
|
|
761
|
+
// - le dropdown de "Page 1" est déjà ouvert
|
|
762
|
+
// - "Sous menu 1.1" est déjà "active"
|
|
763
|
+
// - le dropdown de "Page 2" s'ouvre
|
|
764
|
+
// - "Sous menu 2.1" devient "active" à la place de "Sous menu 1.1"
|
|
765
|
+
// - les 2 dropdowns sont ouverts
|
|
766
|
+
export const UserStartsFromItem1AndThenFollowsALinkToItem21: Story = {
|
|
767
|
+
render: BaseStoryForTheOpenStateTests.render,
|
|
768
|
+
parameters: {
|
|
769
|
+
reactRouter: {
|
|
770
|
+
initialEntries: ["/page-1/item-1"],
|
|
771
|
+
customRoutes: true,
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
play: async ({ canvasElement }) => {
|
|
775
|
+
const canvas = within(canvasElement);
|
|
776
|
+
const user = userEvent.setup();
|
|
777
|
+
|
|
778
|
+
await canvas.findByText("Page: /page-1/item-1");
|
|
779
|
+
await user.click(canvas.getByText("Link to: Item 2.1"));
|
|
780
|
+
|
|
781
|
+
await waitFor(async () => {
|
|
782
|
+
await canvas.findByText("Page 1");
|
|
783
|
+
await canvas.findByText("Item 1.1");
|
|
784
|
+
await canvas.findByText("Item 1.2");
|
|
785
|
+
await expect(canvas.getByText("Page 2").parentElement).toHaveAttribute(
|
|
786
|
+
"data-active",
|
|
787
|
+
"true",
|
|
788
|
+
);
|
|
789
|
+
await expect(canvas.getByText("Item 2.1").parentElement).toHaveAttribute(
|
|
790
|
+
"data-active",
|
|
791
|
+
"true",
|
|
792
|
+
);
|
|
793
|
+
await canvas.findByText("Item 2.2");
|
|
794
|
+
await canvas.findByText("Page: /page-2/item-1");
|
|
795
|
+
});
|
|
796
|
+
},
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
// # Tests pour le NavigationItem avec href="/"
|
|
800
|
+
export const BaseStoryForRootPathTests: Story = {
|
|
801
|
+
render: () => {
|
|
802
|
+
const AppExample = () => {
|
|
803
|
+
const location = useLocation();
|
|
804
|
+
const path = location.pathname;
|
|
805
|
+
|
|
806
|
+
return (
|
|
807
|
+
<div style={{ display: "flex", gap: "1rem" }}>
|
|
808
|
+
<Navigation logo="agregio" currentPath={path}>
|
|
809
|
+
<NavigationItem label="Home" iconLeft="home" href="/" />
|
|
810
|
+
<NavigationItem label="Page 1" iconLeft="business" href="/page-1" />
|
|
811
|
+
</Navigation>
|
|
812
|
+
|
|
813
|
+
<main>
|
|
814
|
+
<ul>
|
|
815
|
+
<li>
|
|
816
|
+
<Link to="/">Link to: Home</Link>
|
|
817
|
+
</li>
|
|
818
|
+
<li>
|
|
819
|
+
<Link to="/page-1">Link to: Page 1</Link>
|
|
820
|
+
</li>
|
|
821
|
+
</ul>
|
|
822
|
+
<Outlet />
|
|
823
|
+
</main>
|
|
824
|
+
</div>
|
|
825
|
+
);
|
|
826
|
+
};
|
|
827
|
+
return (
|
|
828
|
+
<Routes>
|
|
829
|
+
<Route element={<AppExample />}>
|
|
830
|
+
<Route path="/" element={<div>Page: /</div>} />
|
|
831
|
+
<Route path="/page-1" element={<div>Page: /page-1</div>} />
|
|
832
|
+
</Route>
|
|
833
|
+
</Routes>
|
|
834
|
+
);
|
|
835
|
+
},
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
// ## Test : L'item "Home" (href="/") est actif sur la route "/"
|
|
839
|
+
// - "Home" a data-active="true"
|
|
840
|
+
// - "Page 1" a data-active="false"
|
|
841
|
+
export const HomeItemShouldBeActiveOnRootPath: Story = {
|
|
842
|
+
render: BaseStoryForRootPathTests.render,
|
|
843
|
+
parameters: {
|
|
844
|
+
reactRouter: {
|
|
845
|
+
initialEntries: ["/"],
|
|
846
|
+
customRoutes: true,
|
|
847
|
+
},
|
|
848
|
+
},
|
|
849
|
+
play: async ({ canvasElement }) => {
|
|
850
|
+
const canvas = within(canvasElement);
|
|
851
|
+
|
|
852
|
+
await canvas.findByText("Page: /");
|
|
853
|
+
await expect(canvas.getByText("Home").parentElement).toHaveAttribute(
|
|
854
|
+
"data-active",
|
|
855
|
+
"true",
|
|
856
|
+
);
|
|
857
|
+
await expect(canvas.getByText("Page 1").parentElement).toHaveAttribute(
|
|
858
|
+
"data-active",
|
|
859
|
+
"false",
|
|
860
|
+
);
|
|
861
|
+
},
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
// ## Test : L'item "Home" (href="/") n'est PAS actif sur une autre route
|
|
865
|
+
// - "Home" a data-active="false"
|
|
866
|
+
// - "Page 1" a data-active="true"
|
|
867
|
+
export const HomeItemShouldNotBeActiveOnOtherPaths: Story = {
|
|
868
|
+
render: BaseStoryForRootPathTests.render,
|
|
869
|
+
parameters: {
|
|
870
|
+
reactRouter: {
|
|
871
|
+
initialEntries: ["/page-1"],
|
|
872
|
+
customRoutes: true,
|
|
873
|
+
},
|
|
874
|
+
},
|
|
875
|
+
play: async ({ canvasElement }) => {
|
|
876
|
+
const canvas = within(canvasElement);
|
|
877
|
+
|
|
878
|
+
await canvas.findByText("Page: /page-1");
|
|
879
|
+
await expect(canvas.getByText("Home").parentElement).toHaveAttribute(
|
|
880
|
+
"data-active",
|
|
881
|
+
"false",
|
|
882
|
+
);
|
|
883
|
+
await expect(canvas.getByText("Page 1").parentElement).toHaveAttribute(
|
|
884
|
+
"data-active",
|
|
885
|
+
"true",
|
|
886
|
+
);
|
|
887
|
+
},
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
export const WithPermissionsWrapperOnSubItem: Story = {
|
|
891
|
+
render: () => {
|
|
892
|
+
const PermissionsWrapper: React.FC<{ children: React.ReactNode }> = () => {
|
|
893
|
+
return null;
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
return (
|
|
897
|
+
<Navigation logo="agregio">
|
|
898
|
+
<NavigationItem
|
|
899
|
+
label="Item 1"
|
|
900
|
+
iconLeft="business"
|
|
901
|
+
subLinks={[
|
|
902
|
+
{ href: "/page-2/item-1", label: "Item 1.1" },
|
|
903
|
+
{
|
|
904
|
+
href: "/page-2/item-2",
|
|
905
|
+
label: "Item 1.2",
|
|
906
|
+
wrapperComponent: (children) => (
|
|
907
|
+
<PermissionsWrapper>{children}</PermissionsWrapper>
|
|
908
|
+
),
|
|
909
|
+
},
|
|
910
|
+
{ href: "/page-2/item-3", label: "Item 1.3" },
|
|
911
|
+
]}
|
|
912
|
+
/>
|
|
913
|
+
</Navigation>
|
|
914
|
+
);
|
|
915
|
+
},
|
|
916
|
+
play: async ({ canvasElement }) => {
|
|
917
|
+
const canvas = within(canvasElement);
|
|
918
|
+
const user = userEvent.setup();
|
|
919
|
+
|
|
920
|
+
await user.click(canvas.getByText("Item 1"));
|
|
921
|
+
await canvas.findByText("Item 1.1");
|
|
922
|
+
await expectNotPresent(() => canvas.queryByText("Item 1.2"));
|
|
923
|
+
await canvas.findByText("Item 1.3");
|
|
924
|
+
},
|
|
925
|
+
};
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
## Developer notes
|
|
929
|
+
|
|
930
|
+
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.
|
|
931
|
+
|
|
932
|
+
```mdx
|
|
933
|
+
import {
|
|
934
|
+
Canvas,
|
|
935
|
+
Meta,
|
|
936
|
+
Stories,
|
|
937
|
+
Controls,
|
|
938
|
+
Source,
|
|
939
|
+
} from "@storybook/addon-docs/blocks";
|
|
940
|
+
|
|
941
|
+
import * as Navigation from "./Navigation.stories";
|
|
942
|
+
import * as NavigationTests from "./tests/Navigation.stories";
|
|
943
|
+
|
|
944
|
+
<Meta of={Navigation} />
|
|
945
|
+
|
|
946
|
+
# Navigation
|
|
947
|
+
|
|
948
|
+
<Controls of={Navigation.Playground} />
|
|
949
|
+
|
|
950
|
+
## Usage
|
|
951
|
+
|
|
952
|
+
Here is a basic example of how to use the `Navigation` component.
|
|
953
|
+
|
|
954
|
+
The example includes :
|
|
955
|
+
|
|
956
|
+
- a basic Routes definitions with react-router
|
|
957
|
+
- the `Navigation` component (using the `currentPath` prop to highlight the current active page)
|
|
958
|
+
- a `main` element that will display the page that is currently selected
|
|
959
|
+
|
|
960
|
+
Your application code may be different (depending on your router and your application structure), but the `Navigation` should work the same way.
|
|
961
|
+
|
|
962
|
+
<Source of={NavigationTests.BaseStoryForTheOpenStateTests} type="code" dark />
|
|
963
|
+
|
|
964
|
+
**Please note that this example is using the `currentPath` prop, which is currently experimental.** We are actually testing this feature and it is the way we want to handle the open and active states of the navigation items.
|
|
965
|
+
|
|
966
|
+
This prop enable a complete internal control of the open and active states of the navigation items.
|
|
967
|
+
|
|
968
|
+
For example, with this prop, the `Navigation` component will be able to highlight the active item based on the current path.
|
|
969
|
+
It will also allows the dropdown to open if the user follow a link that is one of its `subLinks`.
|
|
970
|
+
|
|
971
|
+
## Manually control the open and active states of the navigation items
|
|
972
|
+
|
|
973
|
+
In case you need more control over the open and active states of the navigation items, you can use some some of the following props:
|
|
974
|
+
|
|
975
|
+
- `initialIsCollapsed` on the `Navigation` component
|
|
976
|
+
- `isActive` on the `NavigationItem` component
|
|
977
|
+
- `defaultOpen` on the `NavigationItem` component
|
|
978
|
+
- `onClick` on the `NavigationItem` component
|
|
979
|
+
- `isActive` on the `subLinks` of the `NavigationItem` component
|
|
980
|
+
|
|
981
|
+
**Please note that those props are actually considered to be replaced by the `currentPath` prop in the future.**
|
|
982
|
+
|
|
983
|
+
## Regarding the logos
|
|
984
|
+
|
|
985
|
+
There is an exhaustive list of logos to display in the `Navigation` component.
|
|
986
|
+
|
|
987
|
+
This allows us to adjust its position and size to fit the Navigation component.
|
|
988
|
+
|
|
989
|
+
If you need a logo that is not in the list, feel free to ask us to add it!
|
|
990
|
+
|
|
991
|
+
<Stories />
|
|
992
|
+
```
|