@agregio-solutions/design-system 1.102.6 → 2.0.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 +542 -623
- package/dist/design-system.js +7756 -27691
- package/dist/packages/components/Button/doc.md +33 -2
- package/dist/packages/components/Combobox/Combobox.d.ts +2 -2
- package/dist/packages/components/Combobox/doc.md +0 -4
- package/dist/packages/components/DataTable/DataTable.d.ts +15 -6
- package/dist/packages/components/DataTable/DataTableRow/DataTableRow.d.ts +1 -6
- package/dist/packages/components/DataTable/doc.md +495 -389
- package/dist/packages/components/DatePicker/DatePicker.d.ts +2 -2
- package/dist/packages/components/DatePicker/doc.md +0 -1
- package/dist/packages/components/DateRangePicker/DateRangePicker.d.ts +2 -2
- package/dist/packages/components/DateRangePicker/doc.md +0 -1
- package/dist/packages/components/Drawer/Drawer.d.ts +7 -24
- package/dist/packages/components/Drawer/doc.md +136 -106
- package/dist/packages/components/Dropdown/Dropdown.d.ts +1 -7
- package/dist/packages/components/Dropdown/doc.md +2 -29
- package/dist/packages/components/FileUpload/doc.md +0 -2
- package/dist/packages/components/Filter/Filter.d.ts +24 -10
- package/dist/packages/components/Filter/doc.md +31 -1
- package/dist/packages/components/Modal/doc.md +1 -2
- package/dist/packages/components/Navigation/Navigation.d.ts +2 -1
- package/dist/packages/components/Navigation/doc.md +14 -9
- package/dist/packages/components/NavigationItem/NavigationItem.d.ts +4 -0
- package/dist/packages/components/NavigationItem/doc.md +15 -3
- package/dist/packages/components/PageLayout/doc.md +190 -149
- package/dist/packages/components/Select/Select.d.ts +1 -1
- package/dist/packages/components/Select/doc.md +0 -9
- package/dist/packages/components/Slider/Slider.d.ts +3 -9
- package/dist/packages/components/Slider/doc.md +1 -4
- package/dist/packages/components/Tabs/Tab/Tab.d.ts +0 -5
- package/dist/packages/components/TextInput/TextInput.d.ts +2 -2
- package/dist/packages/components/TextInput/doc.md +0 -22
- package/dist/packages/components/TimeField/TimeField.d.ts +2 -2
- package/dist/packages/components/TimeField/doc.md +0 -1
- package/dist/packages/components/TimePicker/TimePicker.d.ts +2 -2
- package/dist/packages/components/Timeline/Timeline.styled.d.ts +15 -20
- package/dist/packages/components/Timeline/components/BlockLineContainer/BlockLineContainer.styled.d.ts +23 -27
- package/dist/packages/components/Timeline/components/GroupLineContainer/GroupLineContainer.styled.d.ts +3 -4
- package/dist/packages/components/Timeline/components/TimelineRange/TimelineRange.styled.d.ts +12 -16
- package/dist/packages/components/Timeline/doc.md +38 -1
- package/dist/packages/components/YearMonthPicker/YearMonthPicker.d.ts +2 -2
- package/dist/packages/components/YearMonthPicker/doc.md +0 -1
- package/dist/packages/ds-design-tokens/index.d.ts +1 -2
- package/dist/packages/ds-design-tokens/src/tokens.style.d.ts +138 -135
- package/dist/packages/index.d.ts +3 -4
- package/dist/packages/internal-components/FormGroupWrapper/FormGroupWrapper.d.ts +0 -12
- package/dist/public_docs/index.md +4 -1
- package/dist/public_docs/migration-guide-v2.md +444 -0
- package/package.json +6 -8
- package/dist/packages/ds-design-tokens/src/fontSize.style.d.ts +0 -60
- package/dist/packages/mui/mui-theme.d.ts +0 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Props as FormGroupWrapperProps } from '../../internal-components/FormGroupWrapper/FormGroupWrapper';
|
|
2
2
|
import { default as React } from 'react';
|
|
3
3
|
import { Props as LabelProps } from '../Label/Label';
|
|
4
|
-
export type Props = Pick<LabelProps, "label" | "labelIconRight" | "labelIconRightTooltip" | "required"> & Pick<FormGroupWrapperProps, "
|
|
4
|
+
export type Props = Pick<LabelProps, "label" | "labelIconRight" | "labelIconRightTooltip" | "required"> & Pick<FormGroupWrapperProps, "helperText" | "helperTextIcon" | "errorHelperText" | "errorHelperTextIcon" | "successHelperText" | "successHelperTextIcon" | "warningHelperText" | "warningHelperTextIcon" | "orientation"> & {
|
|
5
5
|
/**
|
|
6
6
|
* Required id, used mainly to link the label to the field
|
|
7
7
|
*/
|
|
@@ -114,7 +114,7 @@ export type Props = Pick<LabelProps, "label" | "labelIconRight" | "labelIconRigh
|
|
|
114
114
|
*/
|
|
115
115
|
fullWidth?: boolean;
|
|
116
116
|
};
|
|
117
|
-
declare const DatePicker: React.ForwardRefExoticComponent<Pick<LabelProps, "label" | "required" | "labelIconRight" | "labelIconRightTooltip"> & Pick<FormGroupWrapperProps, "orientation" | "
|
|
117
|
+
declare const DatePicker: React.ForwardRefExoticComponent<Pick<LabelProps, "label" | "required" | "labelIconRight" | "labelIconRightTooltip"> & Pick<FormGroupWrapperProps, "orientation" | "helperText" | "helperTextIcon" | "errorHelperText" | "errorHelperTextIcon" | "successHelperText" | "successHelperTextIcon" | "warningHelperText" | "warningHelperTextIcon"> & {
|
|
118
118
|
/**
|
|
119
119
|
* Required id, used mainly to link the label to the field
|
|
120
120
|
*/
|
|
@@ -28,7 +28,6 @@ const meta: Meta<typeof DatePicker> = {
|
|
|
28
28
|
options: ["America/New_York", "Europe/Paris"],
|
|
29
29
|
},
|
|
30
30
|
shouldCloseOnSelect: { control: "boolean" },
|
|
31
|
-
description: { control: "text" },
|
|
32
31
|
helperText: { control: "text" },
|
|
33
32
|
errorHelperText: { control: "text" },
|
|
34
33
|
successHelperText: { control: "text" },
|
|
@@ -5,7 +5,7 @@ export type DatePickerValue = null | {
|
|
|
5
5
|
start: string;
|
|
6
6
|
end: string;
|
|
7
7
|
};
|
|
8
|
-
export type Props = Pick<LabelProps, "label" | "labelIconRight" | "labelIconRightTooltip" | "required"> & Pick<FormGroupWrapperProps, "
|
|
8
|
+
export type Props = Pick<LabelProps, "label" | "labelIconRight" | "labelIconRightTooltip" | "required"> & Pick<FormGroupWrapperProps, "helperText" | "helperTextIcon" | "errorHelperText" | "errorHelperTextIcon" | "successHelperText" | "successHelperTextIcon" | "warningHelperText" | "warningHelperTextIcon" | "orientation"> & {
|
|
9
9
|
/**
|
|
10
10
|
* Required id, used mainly to link the label to the field
|
|
11
11
|
*/
|
|
@@ -114,7 +114,7 @@ export type Props = Pick<LabelProps, "label" | "labelIconRight" | "labelIconRigh
|
|
|
114
114
|
*/
|
|
115
115
|
fullWidth?: boolean;
|
|
116
116
|
};
|
|
117
|
-
declare const DateRangePicker: React.ForwardRefExoticComponent<Pick<LabelProps, "label" | "required" | "labelIconRight" | "labelIconRightTooltip"> & Pick<FormGroupWrapperProps, "orientation" | "
|
|
117
|
+
declare const DateRangePicker: React.ForwardRefExoticComponent<Pick<LabelProps, "label" | "required" | "labelIconRight" | "labelIconRightTooltip"> & Pick<FormGroupWrapperProps, "orientation" | "helperText" | "helperTextIcon" | "errorHelperText" | "errorHelperTextIcon" | "successHelperText" | "successHelperTextIcon" | "warningHelperText" | "warningHelperTextIcon"> & {
|
|
118
118
|
/**
|
|
119
119
|
* Required id, used mainly to link the label to the field
|
|
120
120
|
*/
|
|
@@ -28,7 +28,6 @@ const meta: Meta<typeof DateRangePicker> = {
|
|
|
28
28
|
options: ["America/New_York", "Europe/Paris"],
|
|
29
29
|
},
|
|
30
30
|
shouldCloseOnSelect: { control: "boolean" },
|
|
31
|
-
description: { control: "text" },
|
|
32
31
|
helperText: { control: "text" },
|
|
33
32
|
errorHelperText: { control: "text" },
|
|
34
33
|
successHelperText: { control: "text" },
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
+
import { DrawerActions, DrawerContent, DrawerForm } from './Drawer.styled';
|
|
2
3
|
export interface Props {
|
|
3
4
|
/**
|
|
4
5
|
* Determines if the drawer is open or closed
|
|
@@ -13,34 +14,16 @@ export interface Props {
|
|
|
13
14
|
*/
|
|
14
15
|
title: string;
|
|
15
16
|
/**
|
|
16
|
-
* Content to be rendered inside the drawer
|
|
17
|
+
* Content to be rendered inside the drawer, below the title.
|
|
18
|
+
* Compose it with `DrawerContent` (scrollable padded body) and `DrawerActions` (bottom action bar),
|
|
19
|
+
* wrapped in `DrawerForm` when the drawer holds a form.
|
|
17
20
|
*/
|
|
18
21
|
children: React.ReactNode;
|
|
19
22
|
/**
|
|
20
|
-
*
|
|
21
|
-
*/
|
|
22
|
-
onPrimaryClick?: () => void;
|
|
23
|
-
/**
|
|
24
|
-
* Label for the primary action button
|
|
25
|
-
*/
|
|
26
|
-
primaryLabel?: string;
|
|
27
|
-
/**
|
|
28
|
-
* Handler call on click on the secondary action button
|
|
29
|
-
*/
|
|
30
|
-
onSecondaryClick?: () => void;
|
|
31
|
-
/**
|
|
32
|
-
* Label for the secondary action button
|
|
33
|
-
*/
|
|
34
|
-
secondaryLabel?: string;
|
|
35
|
-
/**
|
|
36
|
-
* Whether the user can close the modal by clicking outside of it or pressing the escape key
|
|
23
|
+
* Whether the user can close the drawer by clicking outside of it or pressing the escape key
|
|
37
24
|
* @default true
|
|
38
25
|
*/
|
|
39
26
|
isDismissable?: boolean;
|
|
40
|
-
/**
|
|
41
|
-
* Custom implementation of the bottom action bar (with primary and secondary buttons)
|
|
42
|
-
* Please ensure there is at least a close button.
|
|
43
|
-
*/
|
|
44
|
-
customActionsBar?: React.ReactNode;
|
|
45
27
|
}
|
|
46
|
-
export default function Drawer({ children,
|
|
28
|
+
export default function Drawer({ children, isOpen, onOpenChange, title, isDismissable, }: Props): React.JSX.Element;
|
|
29
|
+
export { DrawerContent, DrawerActions, DrawerForm };
|
|
@@ -14,8 +14,8 @@ Base stories:
|
|
|
14
14
|
|
|
15
15
|
```tsx
|
|
16
16
|
import { Meta, StoryObj } from "@storybook/react-vite";
|
|
17
|
-
import Drawer, { Props } from "./Drawer";
|
|
18
|
-
import { screen, fn } from "storybook/test";
|
|
17
|
+
import Drawer, { DrawerActions, DrawerContent, Props } from "./Drawer";
|
|
18
|
+
import { screen, fn, expect } from "storybook/test";
|
|
19
19
|
import { useState } from "react";
|
|
20
20
|
import Button from "@components/Button/Button";
|
|
21
21
|
import {
|
|
@@ -35,17 +35,6 @@ const meta: Meta<typeof Drawer> = {
|
|
|
35
35
|
function ParentComponent(props: Props) {
|
|
36
36
|
const [isOpen, setIsOpen] = useState(props.isOpen);
|
|
37
37
|
|
|
38
|
-
// Trick to make the drawer close on button with text "Close" (to avoid generating a lot of render stories)
|
|
39
|
-
const onPrimaryClick =
|
|
40
|
-
props.primaryLabel === "Close"
|
|
41
|
-
? () => setIsOpen(false)
|
|
42
|
-
: props.onPrimaryClick;
|
|
43
|
-
|
|
44
|
-
const onSecondaryClick =
|
|
45
|
-
props.secondaryLabel === "Close"
|
|
46
|
-
? () => setIsOpen(false)
|
|
47
|
-
: props.onSecondaryClick;
|
|
48
|
-
|
|
49
38
|
return (
|
|
50
39
|
<div
|
|
51
40
|
style={{
|
|
@@ -55,14 +44,17 @@ const meta: Meta<typeof Drawer> = {
|
|
|
55
44
|
height: "100vh",
|
|
56
45
|
}}
|
|
57
46
|
>
|
|
58
|
-
<Drawer
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
<Drawer {...props} isOpen={isOpen} onOpenChange={setIsOpen}>
|
|
48
|
+
<DrawerContent>{props.children}</DrawerContent>
|
|
49
|
+
|
|
50
|
+
<DrawerActions>
|
|
51
|
+
<Button
|
|
52
|
+
onClick={() => setIsOpen(false)}
|
|
53
|
+
text="Close"
|
|
54
|
+
mode="secondary"
|
|
55
|
+
/>
|
|
56
|
+
<Button onClick={() => setIsOpen(false)} text="Apply" />
|
|
57
|
+
</DrawerActions>
|
|
66
58
|
</Drawer>
|
|
67
59
|
|
|
68
60
|
<div style={{ textAlign: "center" }}>
|
|
@@ -85,29 +77,31 @@ export const Playground: StoryObj<typeof Drawer> = {
|
|
|
85
77
|
title: "[Insert drawer title]",
|
|
86
78
|
children: <div>The content</div>,
|
|
87
79
|
isOpen: true,
|
|
88
|
-
primaryLabel: "Close",
|
|
89
|
-
onPrimaryClick: fn(),
|
|
90
|
-
onSecondaryClick: fn(),
|
|
91
80
|
onOpenChange: fn(),
|
|
92
81
|
},
|
|
93
82
|
play: async ({ args }) => {
|
|
94
83
|
await screen.findByText(args.title);
|
|
95
84
|
await screen.findByText("The content");
|
|
96
85
|
await screen.findByText("Close");
|
|
86
|
+
await screen.findByText("Apply");
|
|
97
87
|
await screen.findByLabelText("Close");
|
|
98
|
-
await expectNotPresent(() => screen.queryByText("Submit"));
|
|
99
88
|
},
|
|
100
89
|
};
|
|
101
90
|
|
|
102
|
-
export const
|
|
91
|
+
export const WithoutActions: StoryObj<typeof Drawer> = {
|
|
103
92
|
args: {
|
|
104
93
|
...Playground.args,
|
|
105
|
-
primaryLabel: "Send",
|
|
106
|
-
secondaryLabel: "Close",
|
|
107
94
|
},
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
95
|
+
render: (args) => (
|
|
96
|
+
<Drawer {...args}>
|
|
97
|
+
<DrawerContent>{args.children}</DrawerContent>
|
|
98
|
+
</Drawer>
|
|
99
|
+
),
|
|
100
|
+
play: async ({ args }) => {
|
|
101
|
+
const drawer = await screen.findByRole("dialog", { name: args.title });
|
|
102
|
+
await screen.findByText("The content");
|
|
103
|
+
await expectNotPresent(() => screen.queryByText("Apply"));
|
|
104
|
+
await expect(drawer.querySelector("footer")).toBeNull();
|
|
111
105
|
},
|
|
112
106
|
};
|
|
113
107
|
|
|
@@ -116,6 +110,10 @@ export const OverflowingContent: StoryObj<typeof Drawer> = {
|
|
|
116
110
|
...Playground.args,
|
|
117
111
|
children: "The content. ".repeat(300),
|
|
118
112
|
},
|
|
113
|
+
play: async ({ args }) => {
|
|
114
|
+
await screen.findByText(args.title);
|
|
115
|
+
await screen.findByText("Apply");
|
|
116
|
+
},
|
|
119
117
|
};
|
|
120
118
|
|
|
121
119
|
export const Mobile: StoryObj<typeof Drawer> = {
|
|
@@ -130,17 +128,9 @@ export const Mobile: StoryObj<typeof Drawer> = {
|
|
|
130
128
|
...Playground.args,
|
|
131
129
|
children: "The content. ".repeat(300),
|
|
132
130
|
},
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
args: {
|
|
137
|
-
...Playground.args,
|
|
138
|
-
customActionsBar: (
|
|
139
|
-
<>
|
|
140
|
-
<Button text="Close" mode="secondary" />
|
|
141
|
-
<Button text="Validate" isLoading />
|
|
142
|
-
</>
|
|
143
|
-
),
|
|
131
|
+
play: async ({ args }) => {
|
|
132
|
+
await screen.findByText(args.title);
|
|
133
|
+
await screen.findByText("Apply");
|
|
144
134
|
},
|
|
145
135
|
};
|
|
146
136
|
```
|
|
@@ -151,12 +141,12 @@ Here are some more advanced stories with more testing coverage and examples that
|
|
|
151
141
|
|
|
152
142
|
```tsx
|
|
153
143
|
import { Meta, StoryObj } from "@storybook/react-vite";
|
|
154
|
-
import Drawer from "../Drawer";
|
|
155
|
-
import { userEvent, within, screen, expect } from "storybook/test";
|
|
144
|
+
import Drawer, { DrawerActions, DrawerContent, DrawerForm } from "../Drawer";
|
|
145
|
+
import { userEvent, within, screen, expect, fn } from "storybook/test";
|
|
156
146
|
import { useState } from "react";
|
|
157
147
|
import Button from "@components/Button/Button";
|
|
158
148
|
import { expectNotPresent } from "@internal/test-utils-storybook/test-utils-storybook";
|
|
159
|
-
import { Playground
|
|
149
|
+
import { Playground } from "../Drawer.stories";
|
|
160
150
|
import baseMeta from "../Drawer.stories";
|
|
161
151
|
import { useForm } from "react-hook-form";
|
|
162
152
|
import TextInput from "@packages/components/TextInput/TextInput";
|
|
@@ -203,21 +193,6 @@ export const TestFooterCloseButtonShouldCloseDrawer: StoryObj<typeof Drawer> = {
|
|
|
203
193
|
},
|
|
204
194
|
};
|
|
205
195
|
|
|
206
|
-
export const TestShouldShowASecondaryButtonIfGiven: StoryObj<typeof Drawer> = {
|
|
207
|
-
args: {
|
|
208
|
-
...WithSecondaryAction.args,
|
|
209
|
-
secondaryLabel: "Dismiss",
|
|
210
|
-
},
|
|
211
|
-
play: async ({ args }) => {
|
|
212
|
-
const user = userEvent.setup({ delay: 50 });
|
|
213
|
-
await screen.findByText(args.title);
|
|
214
|
-
await user.click(screen.getByText("Send"));
|
|
215
|
-
await expect(args.onPrimaryClick).toHaveBeenCalledTimes(1);
|
|
216
|
-
await user.click(screen.getByText("Dismiss"));
|
|
217
|
-
await expect(args.onSecondaryClick).toHaveBeenCalledTimes(1);
|
|
218
|
-
},
|
|
219
|
-
};
|
|
220
|
-
|
|
221
196
|
export const ExampleUsage: StoryObj<typeof Drawer> = {
|
|
222
197
|
render: () => {
|
|
223
198
|
function ParentComponent() {
|
|
@@ -228,14 +203,16 @@ export const ExampleUsage: StoryObj<typeof Drawer> = {
|
|
|
228
203
|
<div>
|
|
229
204
|
<Button onClick={() => setIsOpen(true)} text="Open drawer" />
|
|
230
205
|
</div>
|
|
231
|
-
<Drawer
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
206
|
+
<Drawer isOpen={isOpen} onOpenChange={setIsOpen} title="Drawer title">
|
|
207
|
+
<DrawerContent>Drawer content</DrawerContent>
|
|
208
|
+
<DrawerActions>
|
|
209
|
+
<Button
|
|
210
|
+
onClick={() => setIsOpen(false)}
|
|
211
|
+
text="Close"
|
|
212
|
+
mode="secondary"
|
|
213
|
+
/>
|
|
214
|
+
<Button onClick={() => setIsOpen(false)} text="Apply" />
|
|
215
|
+
</DrawerActions>
|
|
239
216
|
</Drawer>
|
|
240
217
|
</div>
|
|
241
218
|
);
|
|
@@ -245,24 +222,46 @@ export const ExampleUsage: StoryObj<typeof Drawer> = {
|
|
|
245
222
|
},
|
|
246
223
|
};
|
|
247
224
|
|
|
225
|
+
type UserForm = {
|
|
226
|
+
name: string;
|
|
227
|
+
email: string;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
type UserFormProps = {
|
|
231
|
+
onClose: () => void;
|
|
232
|
+
onSubmit: (values: UserForm) => Promise<void> | void;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
function CreateUserForm({ onClose, onSubmit }: UserFormProps) {
|
|
236
|
+
const form = useForm<UserForm>();
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<DrawerForm onSubmit={form.handleSubmit(onSubmit)} noValidate>
|
|
240
|
+
<DrawerContent>
|
|
241
|
+
<TextInput
|
|
242
|
+
{...form.register("name", { required: "Name is required" })}
|
|
243
|
+
id="name"
|
|
244
|
+
label="Name"
|
|
245
|
+
errorHelperText={form.formState.errors.name?.message}
|
|
246
|
+
/>
|
|
247
|
+
<TextInput {...form.register("email")} id="email" label="Email" />
|
|
248
|
+
</DrawerContent>
|
|
249
|
+
<DrawerActions>
|
|
250
|
+
<Button onClick={onClose} text="Close" mode="secondary" />
|
|
251
|
+
<Button
|
|
252
|
+
type="submit"
|
|
253
|
+
text="Create user"
|
|
254
|
+
isLoading={form.formState.isSubmitting}
|
|
255
|
+
/>
|
|
256
|
+
</DrawerActions>
|
|
257
|
+
</DrawerForm>
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
248
261
|
export const ExampleWithReactHookForm: StoryObj<typeof Drawer> = {
|
|
249
262
|
render: () => {
|
|
250
|
-
type Form = {
|
|
251
|
-
name: string;
|
|
252
|
-
email: string;
|
|
253
|
-
};
|
|
254
|
-
|
|
255
263
|
function ParentComponent() {
|
|
256
264
|
const [isOpen, setIsOpen] = useState(false);
|
|
257
|
-
const form = useForm<Form>();
|
|
258
|
-
|
|
259
|
-
const onSubmit = form.handleSubmit(async (data: Form) => {
|
|
260
|
-
// API call for example
|
|
261
|
-
console.log(data); // eslint-disable-line no-console
|
|
262
|
-
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
263
|
-
form.reset();
|
|
264
|
-
setIsOpen(false);
|
|
265
|
-
});
|
|
266
265
|
|
|
267
266
|
return (
|
|
268
267
|
<>
|
|
@@ -275,31 +274,16 @@ export const ExampleWithReactHookForm: StoryObj<typeof Drawer> = {
|
|
|
275
274
|
isOpen={isOpen}
|
|
276
275
|
onOpenChange={setIsOpen}
|
|
277
276
|
title="Creating a new user"
|
|
278
|
-
customActionsBar={
|
|
279
|
-
<>
|
|
280
|
-
<Button
|
|
281
|
-
onClick={() => setIsOpen(false)}
|
|
282
|
-
text="Close"
|
|
283
|
-
mode="secondary"
|
|
284
|
-
/>
|
|
285
|
-
<Button
|
|
286
|
-
type="submit"
|
|
287
|
-
form="create-user-form" // <- link the form to the button (by the form id)
|
|
288
|
-
text="Create user"
|
|
289
|
-
isLoading={form.formState.isSubmitting}
|
|
290
|
-
/>
|
|
291
|
-
</>
|
|
292
|
-
}
|
|
293
277
|
>
|
|
294
|
-
<
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
278
|
+
<CreateUserForm
|
|
279
|
+
onClose={() => setIsOpen(false)}
|
|
280
|
+
onSubmit={async (values) => {
|
|
281
|
+
// API call for example
|
|
282
|
+
console.log(values); // eslint-disable-line no-console
|
|
283
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
284
|
+
setIsOpen(false);
|
|
285
|
+
}}
|
|
286
|
+
/>
|
|
303
287
|
</Drawer>
|
|
304
288
|
</>
|
|
305
289
|
);
|
|
@@ -308,6 +292,52 @@ export const ExampleWithReactHookForm: StoryObj<typeof Drawer> = {
|
|
|
308
292
|
return <ParentComponent />;
|
|
309
293
|
},
|
|
310
294
|
};
|
|
295
|
+
|
|
296
|
+
const onSubmitUser = fn();
|
|
297
|
+
|
|
298
|
+
export const TestFormShouldSubmitOnEnterFromAField: StoryObj<typeof Drawer> = {
|
|
299
|
+
args: {
|
|
300
|
+
...Playground.args,
|
|
301
|
+
},
|
|
302
|
+
render: (args) => (
|
|
303
|
+
<Drawer {...args}>
|
|
304
|
+
<CreateUserForm onClose={() => {}} onSubmit={onSubmitUser} />
|
|
305
|
+
</Drawer>
|
|
306
|
+
),
|
|
307
|
+
beforeEach: () => {
|
|
308
|
+
onSubmitUser.mockClear();
|
|
309
|
+
},
|
|
310
|
+
play: async () => {
|
|
311
|
+
const user = userEvent.setup({ delay: 50 });
|
|
312
|
+
await user.type(await screen.findByLabelText("Name"), "Jane{Enter}");
|
|
313
|
+
await expect(onSubmitUser).toHaveBeenCalledTimes(1);
|
|
314
|
+
await expect(onSubmitUser.mock.calls[0][0]).toMatchObject({
|
|
315
|
+
name: "Jane",
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
export const TestFormShouldShowErrorsWhenSubmittedInvalid: StoryObj<
|
|
321
|
+
typeof Drawer
|
|
322
|
+
> = {
|
|
323
|
+
args: {
|
|
324
|
+
...Playground.args,
|
|
325
|
+
},
|
|
326
|
+
render: (args) => (
|
|
327
|
+
<Drawer {...args}>
|
|
328
|
+
<CreateUserForm onClose={() => {}} onSubmit={onSubmitUser} />
|
|
329
|
+
</Drawer>
|
|
330
|
+
),
|
|
331
|
+
beforeEach: () => {
|
|
332
|
+
onSubmitUser.mockClear();
|
|
333
|
+
},
|
|
334
|
+
play: async () => {
|
|
335
|
+
const user = userEvent.setup({ delay: 50 });
|
|
336
|
+
await user.click(await screen.findByText("Create user"));
|
|
337
|
+
await screen.findByText("Name is required");
|
|
338
|
+
await expect(onSubmitUser).not.toHaveBeenCalled();
|
|
339
|
+
},
|
|
340
|
+
};
|
|
311
341
|
```
|
|
312
342
|
|
|
313
343
|
## Developer notes
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import { Props as DropdownListItemProps } from './components/DropdownListItem/DropdownListItem';
|
|
3
2
|
export type Props = {
|
|
4
3
|
/**
|
|
5
|
-
* The
|
|
6
|
-
* @deprecated use `children` instead
|
|
7
|
-
*/
|
|
8
|
-
items?: Array<DropdownListItemProps>;
|
|
9
|
-
/**
|
|
10
|
-
* Render you items manually if you prefer to use composition instead of props.
|
|
4
|
+
* The `DropdownListItem` components to display in the dropdown.
|
|
11
5
|
*/
|
|
12
6
|
children?: React.ReactNode;
|
|
13
7
|
/**
|
|
@@ -31,7 +31,6 @@ const meta: Meta<typeof Dropdown> = {
|
|
|
31
31
|
},
|
|
32
32
|
argTypes: {
|
|
33
33
|
children: { control: false },
|
|
34
|
-
items: { control: false },
|
|
35
34
|
},
|
|
36
35
|
};
|
|
37
36
|
export default meta;
|
|
@@ -143,29 +142,6 @@ export const WithControlledSelectedItem: StoryObj<typeof Dropdown> = {
|
|
|
143
142
|
);
|
|
144
143
|
},
|
|
145
144
|
};
|
|
146
|
-
|
|
147
|
-
export const WithDeprecatedItemsProp: StoryObj<typeof Dropdown> = {
|
|
148
|
-
args: {
|
|
149
|
-
items: generateItems(5),
|
|
150
|
-
style: {
|
|
151
|
-
width: 315,
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
play: async ({ canvasElement }) => {
|
|
155
|
-
const canvas = within(canvasElement);
|
|
156
|
-
await canvas.findByText("Item 1");
|
|
157
|
-
await canvas.findByText("Item 2");
|
|
158
|
-
await canvas.findByText("Item 3");
|
|
159
|
-
await expect(canvas.queryByText("Item 4")?.parentElement).toHaveAttribute(
|
|
160
|
-
"data-selected",
|
|
161
|
-
"true",
|
|
162
|
-
);
|
|
163
|
-
await expect(canvas.queryByText("Item 5")?.parentElement).toHaveAttribute(
|
|
164
|
-
"data-disabled",
|
|
165
|
-
"true",
|
|
166
|
-
);
|
|
167
|
-
},
|
|
168
|
-
};
|
|
169
145
|
```
|
|
170
146
|
|
|
171
147
|
## Developer notes
|
|
@@ -186,10 +162,7 @@ The dropdown is a simple component that allows you to create a dropdown menu.
|
|
|
186
162
|
|
|
187
163
|
This component **does not handle it's absolute positioning**, you need to do it yourself with whatever method or lib you prefer.
|
|
188
164
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
- ✅ **Recommended:** pass a list of `DropdownListItem` components as `children`
|
|
192
|
-
- ⚠️ **Deprecated:** pass an array of `items` which values are the same as [DropdownListItem](?path=/docs/components-dropdown-components-dropdownlistitem--docs) props. (this prop may be removed in a future version)
|
|
165
|
+
Pass a list of [DropdownListItem](?path=/docs/components-dropdown-components-dropdownlistitem--docs) components as `children`.
|
|
193
166
|
|
|
194
167
|
<Canvas of={Dropdown.Default} />
|
|
195
168
|
|
|
@@ -197,7 +170,7 @@ There is two ways of using this component:
|
|
|
197
170
|
|
|
198
171
|
<Controls of={Dropdown.Default} />
|
|
199
172
|
|
|
200
|
-
###
|
|
173
|
+
### DropdownListItem props
|
|
201
174
|
|
|
202
175
|
<Controls of={DropdownListItem.Default} />
|
|
203
176
|
|
|
@@ -42,7 +42,6 @@ export const Playground: Story = {
|
|
|
42
42
|
formGroupWrapperProps: {
|
|
43
43
|
htmlForId: "file-upload",
|
|
44
44
|
label: "[Insert label]",
|
|
45
|
-
description: "[Insert description]",
|
|
46
45
|
errorHelperText: "[Insert helper]",
|
|
47
46
|
errorHelperTextIcon: "error_outline",
|
|
48
47
|
required: true,
|
|
@@ -77,7 +76,6 @@ export const Playground: Story = {
|
|
|
77
76
|
play: async ({ canvasElement }) => {
|
|
78
77
|
const canvas = within(canvasElement);
|
|
79
78
|
await canvas.findByText("[Insert label]");
|
|
80
|
-
await canvas.findByText("[Insert description]");
|
|
81
79
|
await canvas.findByText("[Insert helper]");
|
|
82
80
|
await canvas.findByText("[Filename.exe]");
|
|
83
81
|
await canvas.findByText("test 2.txt");
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import { Column,
|
|
3
|
-
|
|
2
|
+
import { Column, ReactTable, RowData, Table_ColumnFiltering, Table_GlobalFiltering, TableFeatures, TableState_All } from '@tanstack/react-table';
|
|
3
|
+
/**
|
|
4
|
+
* Since `react-table` v9, an API only exists on the instance when its feature is
|
|
5
|
+
* registered through `tableFeatures()`. `Filter` reads the filtering APIs
|
|
6
|
+
* optionally so a table without them still renders.
|
|
7
|
+
*/
|
|
8
|
+
type OptionalFilteringApi<TFeatures extends TableFeatures, TData extends RowData> =
|
|
9
|
+
/** Requires `columnFilteringFeature`. */
|
|
10
|
+
Partial<Pick<Table_ColumnFiltering, "resetColumnFilters">> &
|
|
11
|
+
/** Requires `globalFilteringFeature`. */
|
|
12
|
+
Partial<Pick<Table_GlobalFiltering<TFeatures, TData>, "setGlobalFilter">>;
|
|
13
|
+
type Props<TFeatures extends TableFeatures, TData extends RowData> = {
|
|
4
14
|
/**
|
|
5
|
-
* Table instance from react-table
|
|
15
|
+
* Table instance from react-table.
|
|
16
|
+
* Call `useTable` without a state selector: `table.state` only holds the
|
|
17
|
+
* slices the selector keeps, and this component reads `globalFilter`.
|
|
6
18
|
*/
|
|
7
|
-
table:
|
|
19
|
+
table: ReactTable<TFeatures, TData> & OptionalFilteringApi<TFeatures, TData> & {
|
|
20
|
+
state: TableState_All;
|
|
21
|
+
};
|
|
8
22
|
/**
|
|
9
23
|
* Aria label for the filter input
|
|
10
24
|
*/
|
|
@@ -36,12 +50,12 @@ type Props<TData> = {
|
|
|
36
50
|
*/
|
|
37
51
|
divProps?: React.HTMLProps<HTMLDivElement>;
|
|
38
52
|
};
|
|
39
|
-
export type TypeTableColumnWithFilter<TData> = {
|
|
53
|
+
export type TypeTableColumnWithFilter<TFeatures extends TableFeatures, TData extends RowData> = {
|
|
40
54
|
/**
|
|
41
55
|
* Filter form component that will be rendered in the drawer
|
|
42
56
|
*/
|
|
43
|
-
FilterInDrawer?: ({ column }: {
|
|
44
|
-
column: Column<TData>;
|
|
57
|
+
FilterInDrawer?: ({ column, }: {
|
|
58
|
+
column: Column<TFeatures, TData, unknown>;
|
|
45
59
|
}) => React.ReactNode;
|
|
46
60
|
/**
|
|
47
61
|
* If true, the automatic filter chips will not be displayed (even if there is active filters for this column)
|
|
@@ -52,12 +66,12 @@ export type TypeTableColumnWithFilter<TData> = {
|
|
|
52
66
|
* use it if you really need to manipulate the display of the chips
|
|
53
67
|
* or if you have complexe data objects that cannont be automatically transformed into Chips by the Filter component
|
|
54
68
|
*/
|
|
55
|
-
filterChips?: ({ column }: {
|
|
56
|
-
column: Column<TData>;
|
|
69
|
+
filterChips?: ({ column, }: {
|
|
70
|
+
column: Column<TFeatures, TData, unknown>;
|
|
57
71
|
}) => Array<{
|
|
58
72
|
value: string;
|
|
59
73
|
onClose: () => void;
|
|
60
74
|
}>;
|
|
61
75
|
};
|
|
62
|
-
export default function Filter<TData>({ extraLeftContent, extraRightContent, divProps, onDownloadClick: onDownload, onSettingsClick: onSettings, table, searchAriaLabel, searchPlaceholder, }: Props<TData>): React.JSX.Element;
|
|
76
|
+
export default function Filter<TFeatures extends TableFeatures, TData extends RowData>({ extraLeftContent, extraRightContent, divProps, onDownloadClick: onDownload, onSettingsClick: onSettings, table, searchAriaLabel, searchPlaceholder, }: Props<TFeatures, TData>): React.JSX.Element;
|
|
63
77
|
export {};
|
|
@@ -163,10 +163,21 @@ To enable the search bar, when creating your `table` instance with `react-table`
|
|
|
163
163
|
|
|
164
164
|
To remove it, simply do not set it (or set it to `undefined`).
|
|
165
165
|
|
|
166
|
+
Since `react-table` v9, the `globalFilteringFeature` also has to be registered in
|
|
167
|
+
`tableFeatures()`, otherwise the `globalFilter` state does not exist.
|
|
168
|
+
|
|
166
169
|
Code example to **enable** the search bar:
|
|
167
170
|
|
|
168
171
|
```
|
|
169
|
-
const
|
|
172
|
+
const features = tableFeatures({
|
|
173
|
+
globalFilteringFeature, // <- Without it, there is no globalFilter state
|
|
174
|
+
columnFilteringFeature,
|
|
175
|
+
filterFns: { includesString: filterFn_includesString }, // Only the built-ins you use
|
|
176
|
+
filteredRowModel: createFilteredRowModel(),
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const table = useTable({
|
|
180
|
+
features,
|
|
170
181
|
data,
|
|
171
182
|
columns,
|
|
172
183
|
initialState: {
|
|
@@ -181,6 +192,25 @@ As you can see in the source code for the example story, adding a filter into th
|
|
|
181
192
|
|
|
182
193
|
Please use the exported type `TypeTableColumnWithFilter` to benefit from the type-safety and autocompletion features of your IDE.
|
|
183
194
|
|
|
195
|
+
Since `react-table` v9, declare it through module augmentation so that every column
|
|
196
|
+
definition gets it, and so that `column` is correctly typed inside `FilterInDrawer`
|
|
197
|
+
and `filterChips`:
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
declare module "@tanstack/react-table" {
|
|
201
|
+
interface ColumnMeta<
|
|
202
|
+
TFeatures extends TableFeatures,
|
|
203
|
+
TData extends RowData,
|
|
204
|
+
TValue,
|
|
205
|
+
> extends TypeTableColumnWithFilter<TFeatures, TData> {}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
v9 also offers a per-table `columnMeta` slot (`metaHelper<MyMeta>()` inside
|
|
210
|
+
`tableFeatures()`), but it cannot carry `TypeTableColumnWithFilter`: the type needs
|
|
211
|
+
`typeof features`, which would reference the features object inside its own
|
|
212
|
+
initializer.
|
|
213
|
+
|
|
184
214
|
Here are the available keys for the `meta` object with filter abilities:
|
|
185
215
|
|
|
186
216
|
- `FilterInDrawer`: function that must return a React node, typically the form to filter the column entry (like a Select, Radios, etc.)
|
|
@@ -105,7 +105,7 @@ Here are some more advanced stories with more testing coverage and examples that
|
|
|
105
105
|
|
|
106
106
|
```tsx
|
|
107
107
|
import { Meta, StoryObj } from "@storybook/react-vite";
|
|
108
|
-
import Modal, { ModalActions, ModalContent } from "../Modal";
|
|
108
|
+
import Modal, { ModalActions, ModalContent, ModalForm } from "../Modal";
|
|
109
109
|
import { userEvent, within, screen } from "storybook/test";
|
|
110
110
|
import { useState } from "react";
|
|
111
111
|
import Button from "@components/Button/Button";
|
|
@@ -114,7 +114,6 @@ import { Playground } from "../Modal.stories";
|
|
|
114
114
|
import baseMeta from "../Modal.stories";
|
|
115
115
|
import { useForm } from "react-hook-form";
|
|
116
116
|
import TextInput from "@components/TextInput/TextInput";
|
|
117
|
-
import { ModalForm } from "../Modal.styled";
|
|
118
117
|
|
|
119
118
|
const meta: Meta<typeof Modal> = {
|
|
120
119
|
component: Modal,
|