@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,296 @@
|
|
|
1
|
+
# Switch
|
|
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/Switch/Switch.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 Switch from "./Switch";
|
|
18
|
+
import { within, expect, userEvent, fn } from "storybook/test";
|
|
19
|
+
import { expectNotPresent } from "@internal/test-utils-storybook/test-utils-storybook";
|
|
20
|
+
|
|
21
|
+
const meta: Meta<typeof Switch> = {
|
|
22
|
+
component: Switch,
|
|
23
|
+
argTypes: {
|
|
24
|
+
label: { control: "text" },
|
|
25
|
+
},
|
|
26
|
+
parameters: {
|
|
27
|
+
layout: "centered",
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export default meta;
|
|
31
|
+
|
|
32
|
+
export const Playground: StoryObj<typeof Switch> = {
|
|
33
|
+
args: {
|
|
34
|
+
id: "nice-switch",
|
|
35
|
+
label: "Label",
|
|
36
|
+
onChange: fn(),
|
|
37
|
+
},
|
|
38
|
+
play: async ({ canvasElement }) => {
|
|
39
|
+
const canvas = within(canvasElement);
|
|
40
|
+
await canvas.findByText("Label");
|
|
41
|
+
await expect(canvas.getByLabelText("Label")).not.toBeChecked();
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const Large: StoryObj<typeof Switch> = {
|
|
46
|
+
args: {
|
|
47
|
+
...Playground.args,
|
|
48
|
+
size: "large",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const NoLabel: StoryObj<typeof Switch> = {
|
|
53
|
+
args: {
|
|
54
|
+
...Playground.args,
|
|
55
|
+
label: undefined,
|
|
56
|
+
"aria-label": "Label",
|
|
57
|
+
},
|
|
58
|
+
play: async ({ canvasElement }) => {
|
|
59
|
+
const canvas = within(canvasElement);
|
|
60
|
+
await expectNotPresent(() => canvas.queryByText("Label"));
|
|
61
|
+
await canvas.findByLabelText("Label");
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const LabelLeft: StoryObj<typeof Switch> = {
|
|
66
|
+
args: {
|
|
67
|
+
...Playground.args,
|
|
68
|
+
labelPosition: "left",
|
|
69
|
+
},
|
|
70
|
+
play: async ({ canvasElement }) => {
|
|
71
|
+
const canvas = within(canvasElement);
|
|
72
|
+
await canvas.findByText("Label");
|
|
73
|
+
await expect(canvas.getByLabelText("Label")).not.toBeChecked();
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const DefaultSelected: StoryObj<typeof Switch> = {
|
|
78
|
+
args: {
|
|
79
|
+
...Playground.args,
|
|
80
|
+
defaultSelected: true,
|
|
81
|
+
},
|
|
82
|
+
play: async ({ canvasElement }) => {
|
|
83
|
+
const canvas = within(canvasElement);
|
|
84
|
+
await canvas.findByText("Label");
|
|
85
|
+
await expect(canvas.getByLabelText("Label")).toBeChecked();
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const Disabled: StoryObj<typeof Switch> = {
|
|
90
|
+
args: {
|
|
91
|
+
...Playground.args,
|
|
92
|
+
isDisabled: true,
|
|
93
|
+
},
|
|
94
|
+
play: async ({ canvasElement, args }) => {
|
|
95
|
+
const canvas = within(canvasElement);
|
|
96
|
+
await canvas.findByText("Label");
|
|
97
|
+
await expect(canvas.getByLabelText("Label")).not.toBeChecked();
|
|
98
|
+
await userEvent.click(canvas.getByLabelText("Label"));
|
|
99
|
+
await expect(args.onChange).toHaveBeenCalledTimes(0);
|
|
100
|
+
await expect(canvas.getByLabelText("Label")).not.toBeChecked();
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
export const DisabledSelected: StoryObj<typeof Switch> = {
|
|
104
|
+
args: {
|
|
105
|
+
...Playground.args,
|
|
106
|
+
isDisabled: true,
|
|
107
|
+
defaultSelected: true,
|
|
108
|
+
},
|
|
109
|
+
play: async ({ canvasElement, args }) => {
|
|
110
|
+
const canvas = within(canvasElement);
|
|
111
|
+
await canvas.findByText("Label");
|
|
112
|
+
await expect(canvas.getByLabelText("Label")).toBeChecked();
|
|
113
|
+
await userEvent.click(canvas.getByLabelText("Label"));
|
|
114
|
+
await expect(args.onChange).toHaveBeenCalledTimes(0);
|
|
115
|
+
await expect(canvas.getByLabelText("Label")).toBeChecked();
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const Focus: StoryObj<typeof Switch> = {
|
|
120
|
+
args: {
|
|
121
|
+
...Playground.args,
|
|
122
|
+
// @ts-ignore
|
|
123
|
+
"data-force-focus-visible": true,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const WithFormattedText: StoryObj<typeof Switch> = {
|
|
128
|
+
args: {
|
|
129
|
+
...Playground.args,
|
|
130
|
+
label: (
|
|
131
|
+
<div>
|
|
132
|
+
<h3 style={{ margin: 0 }}>Activate the wifi</h3>
|
|
133
|
+
<div>
|
|
134
|
+
The wi-fi will connect to your device and allow you to access the
|
|
135
|
+
internet.
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
),
|
|
139
|
+
},
|
|
140
|
+
decorators: [
|
|
141
|
+
(Story) => (
|
|
142
|
+
<div style={{ width: 300 }}>
|
|
143
|
+
<Story />
|
|
144
|
+
</div>
|
|
145
|
+
),
|
|
146
|
+
],
|
|
147
|
+
};
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## How to test this component
|
|
151
|
+
|
|
152
|
+
Here are some more advanced stories with more testing coverage and examples that you can read to understand how to test this component.
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { Meta, StoryObj } from "@storybook/react-vite";
|
|
156
|
+
import Switch from "../Switch";
|
|
157
|
+
import { userEvent, within, expect } from "storybook/test";
|
|
158
|
+
import { useState } from "react";
|
|
159
|
+
import { useController, useForm } from "react-hook-form";
|
|
160
|
+
|
|
161
|
+
const meta: Meta<typeof Switch> = {
|
|
162
|
+
component: Switch,
|
|
163
|
+
argTypes: {
|
|
164
|
+
label: { control: "text" },
|
|
165
|
+
},
|
|
166
|
+
parameters: {
|
|
167
|
+
layout: "centered",
|
|
168
|
+
chromatic: { disableSnapshot: true },
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
export default meta;
|
|
172
|
+
|
|
173
|
+
export const ExampleControlled: StoryObj<typeof Switch> = {
|
|
174
|
+
render: (args) => {
|
|
175
|
+
const ControlledExample = () => {
|
|
176
|
+
const [isSelected, setIsSelected] = useState(false);
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<div>
|
|
180
|
+
<div>Is selected: {isSelected ? "Yes" : "No"}</div>
|
|
181
|
+
|
|
182
|
+
<Switch
|
|
183
|
+
isSelected={isSelected}
|
|
184
|
+
onChange={() => setIsSelected(!isSelected)}
|
|
185
|
+
label="Wi-Fi"
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
return <ControlledExample {...args} />;
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const TestControlled: StoryObj<typeof Switch> = {
|
|
196
|
+
render: ExampleControlled.render,
|
|
197
|
+
play: async ({ canvasElement }) => {
|
|
198
|
+
const canvas = within(canvasElement);
|
|
199
|
+
await canvas.findByText("Is selected: No");
|
|
200
|
+
await expect(canvas.getByLabelText("Wi-Fi")).not.toBeChecked();
|
|
201
|
+
await userEvent.click(canvas.getByText("Wi-Fi"));
|
|
202
|
+
await expect(canvas.getByLabelText("Wi-Fi")).toBeChecked();
|
|
203
|
+
await canvas.findByText("Is selected: Yes");
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export const ExampleWithReactHookForm: StoryObj<typeof Switch> = {
|
|
208
|
+
render: (args) => {
|
|
209
|
+
const ReactHookFormExample = () => {
|
|
210
|
+
const { control } = useForm({
|
|
211
|
+
defaultValues: {
|
|
212
|
+
wifi: true,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const wifiField = useController({ control, name: "wifi" });
|
|
217
|
+
|
|
218
|
+
return (
|
|
219
|
+
<form>
|
|
220
|
+
<div>Is selected: {wifiField.field.value ? "Yes" : "No"}</div>
|
|
221
|
+
<Switch
|
|
222
|
+
id="wifi-id-1"
|
|
223
|
+
label="Wi-Fi"
|
|
224
|
+
isSelected={wifiField.field.value}
|
|
225
|
+
onChange={(isSelected) => wifiField.field.onChange(isSelected)}
|
|
226
|
+
/>
|
|
227
|
+
</form>
|
|
228
|
+
);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
return <ReactHookFormExample {...args} />;
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export const TestWithReactHookForm: StoryObj<typeof Switch> = {
|
|
236
|
+
render: ExampleWithReactHookForm.render,
|
|
237
|
+
play: async ({ canvasElement }) => {
|
|
238
|
+
const canvas = within(canvasElement);
|
|
239
|
+
const user = userEvent.setup({ delay: 50 });
|
|
240
|
+
await canvas.findByText("Is selected: Yes");
|
|
241
|
+
await expect(canvas.getByLabelText("Wi-Fi")).toBeChecked();
|
|
242
|
+
await user.click(canvas.getByText("Wi-Fi"));
|
|
243
|
+
await expect(canvas.getByLabelText("Wi-Fi")).not.toBeChecked();
|
|
244
|
+
await canvas.findByText("Is selected: No");
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Developer notes
|
|
250
|
+
|
|
251
|
+
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.
|
|
252
|
+
|
|
253
|
+
```mdx
|
|
254
|
+
import {
|
|
255
|
+
Canvas,
|
|
256
|
+
Meta,
|
|
257
|
+
Stories,
|
|
258
|
+
Controls,
|
|
259
|
+
Source,
|
|
260
|
+
} from "@storybook/addon-docs/blocks";
|
|
261
|
+
|
|
262
|
+
import * as Switch from "./Switch.stories";
|
|
263
|
+
import * as SwitchTests from "./tests/Switch.stories";
|
|
264
|
+
|
|
265
|
+
<Meta of={Switch} />
|
|
266
|
+
|
|
267
|
+
# Switch
|
|
268
|
+
|
|
269
|
+
A switch allows a user to turn a setting on or off.
|
|
270
|
+
|
|
271
|
+
<Canvas of={Switch.Playground} />
|
|
272
|
+
<Controls of={Switch.Playground} />
|
|
273
|
+
|
|
274
|
+
## Features
|
|
275
|
+
|
|
276
|
+
There is no native HTML element with switch styling.
|
|
277
|
+
`<input type="checkbox">` is the closest semantically, but isn't styled or exposed to assistive technology as a switch.
|
|
278
|
+
Switch helps achieve accessible switches that can be styled as needed.
|
|
279
|
+
|
|
280
|
+
- **Styleable** – Hover, press, keyboard focus, and selection states are provided for easy styling. These states only apply when interacting with an appropriate input device, unlike CSS pseudo classes.
|
|
281
|
+
- **Accessible** – Uses a visually hidden `<input>` element with role="switch" under the hood, which also enables HTML form integration and autofill. A label element is built-in to ensure the switch is usable with assistive technologies.
|
|
282
|
+
- **Cross-browser** – Mouse, touch, keyboard, and focus interactions are normalized to ensure consistency across browsers and devices.
|
|
283
|
+
|
|
284
|
+
In most cases, switches should have a visual label.
|
|
285
|
+
If the switch does not have a visible label, an `aria-label` or `aria-labelledby` prop must be passed instead to identify the element to assistive technology.
|
|
286
|
+
|
|
287
|
+
## Example usage (with state)
|
|
288
|
+
|
|
289
|
+
<Source of={SwitchTests.ExampleControlled} type="code" dark />
|
|
290
|
+
|
|
291
|
+
## Example usage (with react-hook-form)
|
|
292
|
+
|
|
293
|
+
<Source of={SwitchTests.ExampleWithReactHookForm} type="code" dark />
|
|
294
|
+
|
|
295
|
+
<Stories />
|
|
296
|
+
```
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# Tabs
|
|
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/Tabs/Tabs.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 Tabs from "./Tabs/Tabs";
|
|
18
|
+
import TabList from "./TabList/TabList";
|
|
19
|
+
import Tab from "./Tab/Tab";
|
|
20
|
+
import TabPanel from "./TabPanel/TabPanel";
|
|
21
|
+
import { Route, Routes, useLocation } from "react-router-dom";
|
|
22
|
+
import React from "react";
|
|
23
|
+
import { STORYBOOK_VIEWPORTS } from "@internal/test-utils-storybook/test-utils-storybook";
|
|
24
|
+
|
|
25
|
+
const meta: Meta<typeof Tabs> = {
|
|
26
|
+
component: Tabs,
|
|
27
|
+
parameters: {
|
|
28
|
+
layout: "centered",
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
export default meta;
|
|
32
|
+
|
|
33
|
+
export const SimpleTabs: StoryObj<typeof Tabs> = {
|
|
34
|
+
render: () => (
|
|
35
|
+
<Tabs defaultSelectedKey="1">
|
|
36
|
+
<TabList style={{ marginBottom: 16 }}>
|
|
37
|
+
<Tab id="1">Panel 1</Tab>
|
|
38
|
+
<Tab id="2">Panel 2</Tab>
|
|
39
|
+
<Tab id="3" badgeProps={{ nature: "negative", variant: "dot" }}>
|
|
40
|
+
Panel 3
|
|
41
|
+
</Tab>
|
|
42
|
+
<Tab id="disabled" isDisabled>
|
|
43
|
+
Disabled tab
|
|
44
|
+
</Tab>
|
|
45
|
+
</TabList>
|
|
46
|
+
<TabPanel id="1">Content for the panel 1</TabPanel>
|
|
47
|
+
<TabPanel id="2">Content for the panel 2</TabPanel>
|
|
48
|
+
<TabPanel id="3">Content for the panel 3</TabPanel>
|
|
49
|
+
<TabPanel id="disabled">Content for the panel disabled</TabPanel>
|
|
50
|
+
</Tabs>
|
|
51
|
+
),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const WithoutFlexWrapper: StoryObj<typeof Tabs> = {
|
|
55
|
+
parameters: {
|
|
56
|
+
layout: "padded",
|
|
57
|
+
},
|
|
58
|
+
render: SimpleTabs.render,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const ControlledMode: StoryObj<typeof Tabs> = {
|
|
62
|
+
render: () => {
|
|
63
|
+
function AppTabs() {
|
|
64
|
+
const [selectedKey, setSelectedKey] = React.useState("1");
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Tabs selectedKey={selectedKey} onSelectionChange={setSelectedKey}>
|
|
68
|
+
<TabList style={{ marginBottom: 16 }}>
|
|
69
|
+
<Tab id="1">Panel 1</Tab>
|
|
70
|
+
<Tab id="2">Panel 2</Tab>
|
|
71
|
+
</TabList>
|
|
72
|
+
<TabPanel id="1">Content for the panel 1</TabPanel>
|
|
73
|
+
<TabPanel id="2">Content for the panel 2</TabPanel>
|
|
74
|
+
</Tabs>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return <AppTabs />;
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const WithReactRouter: StoryObj<typeof Tabs> = {
|
|
83
|
+
render: () => {
|
|
84
|
+
function AppTabs() {
|
|
85
|
+
const { pathname } = useLocation();
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<Tabs selectedKey={pathname}>
|
|
89
|
+
<TabList aria-label="Tabs" style={{ marginBottom: 16 }}>
|
|
90
|
+
<Tab id="/pane-1" href="/pane-1">
|
|
91
|
+
Panel 1
|
|
92
|
+
</Tab>
|
|
93
|
+
<Tab id="/pane-2" href="/pane-2">
|
|
94
|
+
Panel 2
|
|
95
|
+
</Tab>
|
|
96
|
+
<Tab id="/pane-3" href="/pane-3">
|
|
97
|
+
Panel 3
|
|
98
|
+
</Tab>
|
|
99
|
+
<Tab id="/pane-disabled" href="/pane-disabled" isDisabled>
|
|
100
|
+
Panel disabled
|
|
101
|
+
</Tab>
|
|
102
|
+
</TabList>
|
|
103
|
+
<TabPanel id={pathname}>
|
|
104
|
+
<Routes>
|
|
105
|
+
<Route path="/pane-1" element="Content for the panel 1" />
|
|
106
|
+
<Route path="/pane-2" element="Content for the panel 2" />
|
|
107
|
+
<Route path="/pane-3" element="Content for the panel 3" />
|
|
108
|
+
<Route
|
|
109
|
+
path="/pane-disabled"
|
|
110
|
+
element="Content for the panel disabled"
|
|
111
|
+
/>
|
|
112
|
+
</Routes>
|
|
113
|
+
</TabPanel>
|
|
114
|
+
</Tabs>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return <AppTabs />;
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const WithOverflow: StoryObj<typeof Tabs> = {
|
|
123
|
+
parameters: {
|
|
124
|
+
...STORYBOOK_VIEWPORTS,
|
|
125
|
+
layout: "none",
|
|
126
|
+
},
|
|
127
|
+
globals: {
|
|
128
|
+
viewport: { value: "iphone6", isRotated: false },
|
|
129
|
+
},
|
|
130
|
+
render: () => {
|
|
131
|
+
function AppTabs() {
|
|
132
|
+
return (
|
|
133
|
+
<Tabs>
|
|
134
|
+
<TabList>
|
|
135
|
+
<Tab id="tab-1">Tab 1</Tab>
|
|
136
|
+
<Tab id="tab-2" badgeProps={{ nature: "negative", variant: "dot" }}>
|
|
137
|
+
Tab 2
|
|
138
|
+
</Tab>
|
|
139
|
+
<Tab id="tab-3" badgeProps={{ nature: "negative", variant: "dot" }}>
|
|
140
|
+
Tab 3
|
|
141
|
+
</Tab>
|
|
142
|
+
<Tab id="tab-4" badgeProps={{ nature: "negative", variant: "dot" }}>
|
|
143
|
+
Tab 4
|
|
144
|
+
</Tab>
|
|
145
|
+
<Tab id="tab-5" badgeProps={{ nature: "negative", variant: "dot" }}>
|
|
146
|
+
Tab 5
|
|
147
|
+
</Tab>
|
|
148
|
+
<Tab id="tab-6" badgeProps={{ nature: "negative", variant: "dot" }}>
|
|
149
|
+
Tab 6
|
|
150
|
+
</Tab>
|
|
151
|
+
<Tab id="tab-7" badgeProps={{ nature: "negative", variant: "dot" }}>
|
|
152
|
+
Tab 7
|
|
153
|
+
</Tab>
|
|
154
|
+
</TabList>
|
|
155
|
+
<div style={{ marginTop: "var(--spacing-md" }}>
|
|
156
|
+
<TabPanel id="tab-1">Content for the panel 1</TabPanel>
|
|
157
|
+
<TabPanel id="tab-2">Content for the panel 2</TabPanel>
|
|
158
|
+
<TabPanel id="tab-3">Content for the panel 3</TabPanel>
|
|
159
|
+
<TabPanel id="tab-4">Content for the panel 4</TabPanel>
|
|
160
|
+
<TabPanel id="tab-5">Content for the panel 5</TabPanel>
|
|
161
|
+
<TabPanel id="tab-6">Content for the panel 6</TabPanel>
|
|
162
|
+
<TabPanel id="tab-7">Content for the panel 7</TabPanel>
|
|
163
|
+
</div>
|
|
164
|
+
</Tabs>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return <AppTabs />;
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Developer notes
|
|
174
|
+
|
|
175
|
+
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.
|
|
176
|
+
|
|
177
|
+
````mdx
|
|
178
|
+
import {
|
|
179
|
+
Canvas,
|
|
180
|
+
Meta,
|
|
181
|
+
Stories,
|
|
182
|
+
ArgTypes,
|
|
183
|
+
Source,
|
|
184
|
+
} from "@storybook/addon-docs/blocks";
|
|
185
|
+
|
|
186
|
+
import * as Tabs from "./Tabs.stories";
|
|
187
|
+
import * as TabPanel from "./TabPanel/TabPanel.stories";
|
|
188
|
+
import * as TabList from "./TabList/TabList.stories";
|
|
189
|
+
import Tab from "./Tab/Tab";
|
|
190
|
+
|
|
191
|
+
<Meta of={Tabs} />
|
|
192
|
+
|
|
193
|
+
# Tabs
|
|
194
|
+
|
|
195
|
+
<Canvas of={Tabs.SimpleTabs} />
|
|
196
|
+
|
|
197
|
+
## Basic usage
|
|
198
|
+
|
|
199
|
+
Here is the simplest way to use the Tabs component. All the state is managed by the Tabs component.
|
|
200
|
+
You can define the default selected tab on the first render with the `defaultSelectedKey` prop.
|
|
201
|
+
|
|
202
|
+
<Source of={Tabs.SimpleTabs} type="code" dark />
|
|
203
|
+
|
|
204
|
+
## Controlled mode
|
|
205
|
+
|
|
206
|
+
If you need more control about the state of the Tabs component, you can use the `selectedKey` and `onSelectionChange` props to control the selected tab.
|
|
207
|
+
|
|
208
|
+
<Source of={Tabs.ControlledMode} type="code" dark />
|
|
209
|
+
|
|
210
|
+
## Using a Router to render the TabPanels
|
|
211
|
+
|
|
212
|
+
It is possible to use a Router to render the TabPanels, thus making the Tabs simple links that will update the URL using your framework-specific Router.
|
|
213
|
+
|
|
214
|
+
Doing so may be a little bit more complex than using simple state management, but comes with some benefits like SEO / preserving the browser history / keep the selected panel on page reloads / etc.
|
|
215
|
+
|
|
216
|
+
**It will also requires you to setup the [RouterProvider](?path=/docs/components-routerprovider--docs)** to make the Router available to the Tabs component.
|
|
217
|
+
|
|
218
|
+
Without a RouterProvider, the Tabs will render as simple links that will make a full page reload (instead of a `router.push` or `router.replace` call).
|
|
219
|
+
|
|
220
|
+
Here is a basic example using React Router v6.
|
|
221
|
+
|
|
222
|
+
<Source of={Tabs.WithReactRouter} type="code" dark />
|
|
223
|
+
|
|
224
|
+
If you use `<Outlet />` in your app to render your main routes, you may have to use nested route indications instead of giving a full URL.
|
|
225
|
+
|
|
226
|
+
First, in your main pages routes definitions, you should have a route that will render the same component for the nested paths:
|
|
227
|
+
|
|
228
|
+
```jsx
|
|
229
|
+
<Route path="/nested/*" element={<NestedPage />} />
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Then, inside the NestedPage component, you can use the Tabs component like this :
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
import { useLocation } from "react-router-dom";
|
|
236
|
+
|
|
237
|
+
export const NestedPage = () => {
|
|
238
|
+
const { pathname } = useLocation();
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<>
|
|
242
|
+
<Tabs selectedKey={pathname}>
|
|
243
|
+
<TabList>
|
|
244
|
+
<Tab id="/nested/pane-1" href="/nested/pane-1">
|
|
245
|
+
Panel 1
|
|
246
|
+
</Tab>
|
|
247
|
+
<Tab id="/nested/pane-2" href="/nested/pane-2">
|
|
248
|
+
Panel 2
|
|
249
|
+
</Tab>
|
|
250
|
+
<Tab id="/nested/pane-3" href="/nested/pane-3">
|
|
251
|
+
Panel 3
|
|
252
|
+
</Tab>
|
|
253
|
+
</TabList>
|
|
254
|
+
<TabPanel id={pathname}>
|
|
255
|
+
<Routes>
|
|
256
|
+
{/* 👇 paths are relative to the parent route, no need to give the absolute URL */}
|
|
257
|
+
<Route path="pane-1" element="Content for the panel 1" />
|
|
258
|
+
<Route path="pane-2" element="Content for the panel 2" />
|
|
259
|
+
<Route path="pane-3" element="Content for the panel 3" />
|
|
260
|
+
</Routes>
|
|
261
|
+
</TabPanel>
|
|
262
|
+
</Tabs>
|
|
263
|
+
</>
|
|
264
|
+
);
|
|
265
|
+
};
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Tabs component Props
|
|
269
|
+
|
|
270
|
+
`Tabs` is the root component of the whole tabs system, other "Tab\*" subcomponents must be above it in the tree.
|
|
271
|
+
|
|
272
|
+
<ArgTypes of={Tabs.SimpleTabs} />
|
|
273
|
+
|
|
274
|
+
## Tab component Props
|
|
275
|
+
|
|
276
|
+
The `Tab` component is used to display one of the choices. It can also be a link.
|
|
277
|
+
|
|
278
|
+
<ArgTypes of={Tab} />
|
|
279
|
+
|
|
280
|
+
## TabList component Props
|
|
281
|
+
|
|
282
|
+
The `TabList` is responsible to align correctly the different `Tab` component inside of it, and also enable a11y features like keyboard navigation and screen-reader props.
|
|
283
|
+
|
|
284
|
+
<ArgTypes of={TabList.Default} />
|
|
285
|
+
|
|
286
|
+
## TabPanel component Props
|
|
287
|
+
|
|
288
|
+
This optional component is required if you want the `Tabs` to fully handle the state (uncontrolled) and show the corresponding active panel depending on the selected Tab ID.
|
|
289
|
+
|
|
290
|
+
If you use the controlled mode, it's still good to use TabPanel but it's not required.
|
|
291
|
+
|
|
292
|
+
<ArgTypes of={TabPanel.Default} />
|
|
293
|
+
|
|
294
|
+
<Stories />
|
|
295
|
+
````
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Tag
|
|
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/Tag/Tag.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 Tag from "./Tag";
|
|
18
|
+
import { within } from "storybook/test";
|
|
19
|
+
import { ICON_NAMES_ARRAY } from "@components/Icon/Icon";
|
|
20
|
+
|
|
21
|
+
const meta: Meta<typeof Tag> = {
|
|
22
|
+
component: Tag,
|
|
23
|
+
tags: ["autodocs"],
|
|
24
|
+
argTypes: {
|
|
25
|
+
children: { type: "string" },
|
|
26
|
+
iconLeft: {
|
|
27
|
+
options: ["", ...ICON_NAMES_ARRAY],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
parameters: {
|
|
31
|
+
layout: "centered",
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export default meta;
|
|
35
|
+
|
|
36
|
+
export const Default: StoryObj<typeof Tag> = {
|
|
37
|
+
args: {
|
|
38
|
+
children: "[Insert tag]",
|
|
39
|
+
nature: "default",
|
|
40
|
+
},
|
|
41
|
+
play: async ({ canvasElement, args }) => {
|
|
42
|
+
const canvas = within(canvasElement);
|
|
43
|
+
await canvas.findByText(args.children as string);
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const WithIcon: StoryObj<typeof Tag> = {
|
|
48
|
+
args: {
|
|
49
|
+
...Default.args,
|
|
50
|
+
iconLeft: "help_outline",
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const Informative: StoryObj<typeof Tag> = {
|
|
55
|
+
args: {
|
|
56
|
+
...WithIcon.args,
|
|
57
|
+
nature: "informative",
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const Negative: StoryObj<typeof Tag> = {
|
|
62
|
+
args: {
|
|
63
|
+
...WithIcon.args,
|
|
64
|
+
nature: "negative",
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const Positive: StoryObj<typeof Tag> = {
|
|
69
|
+
args: {
|
|
70
|
+
...WithIcon.args,
|
|
71
|
+
nature: "positive",
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const Warning: StoryObj<typeof Tag> = {
|
|
76
|
+
args: {
|
|
77
|
+
...WithIcon.args,
|
|
78
|
+
nature: "warning",
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
```
|