@agregio-solutions/design-system 2.2.0 → 2.3.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 +327 -327
- package/dist/design-system.js +905 -888
- package/dist/packages/components/DataTable/DataTable.d.ts +10 -1
- package/dist/packages/components/DataTable/DataTableRoot/DataTableRoot.d.ts +4 -0
- package/dist/packages/components/DataTable/DataTableSkeletonRows/DataTableSkeletonRows.d.ts +11 -0
- package/dist/packages/components/DataTable/doc.md +112 -11
- package/package.json +1 -1
|
@@ -41,6 +41,15 @@ type Props<TFeatures extends TableFeatures, TData extends RowData> = {
|
|
|
41
41
|
* @default "default".
|
|
42
42
|
*/
|
|
43
43
|
size?: "small" | "default" | "large";
|
|
44
|
+
/**
|
|
45
|
+
* Whether the table is loading its data for the first time.
|
|
46
|
+
* Renders skeleton rows under the real header, as many as the page size
|
|
47
|
+
* (`pagination.pageSize`, else `table.state.pagination.pageSize`, else 10).
|
|
48
|
+
* Map it to react-query `isLoading` (first load, no data yet), not `isFetching`:
|
|
49
|
+
* on refetch, keep the current rows displayed (e.g. with `placeholderData: keepPreviousData`).
|
|
50
|
+
* @default false
|
|
51
|
+
*/
|
|
52
|
+
isLoading?: boolean;
|
|
44
53
|
};
|
|
45
|
-
export default function DataTable<TFeatures extends TableFeatures, TData extends RowData>({ table, pagination, renderSubComponent, itemsPerPageOptions, size, ...divProps }: Props<TFeatures, TData>): React.JSX.Element;
|
|
54
|
+
export default function DataTable<TFeatures extends TableFeatures, TData extends RowData>({ table, pagination, renderSubComponent, itemsPerPageOptions, size, isLoading, ...divProps }: Props<TFeatures, TData>): React.JSX.Element;
|
|
46
55
|
export {};
|
|
@@ -8,6 +8,10 @@ export type Props = {
|
|
|
8
8
|
* The inline style for the element.
|
|
9
9
|
*/
|
|
10
10
|
style?: React.CSSProperties;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the table content is loading.
|
|
13
|
+
*/
|
|
14
|
+
"aria-busy"?: boolean;
|
|
11
15
|
children: React.ReactNode;
|
|
12
16
|
};
|
|
13
17
|
export default function DataTableRoot({ children, ...otherProps }: Props): React.JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Props = {
|
|
2
|
+
/**
|
|
3
|
+
* The number of skeleton rows to render.
|
|
4
|
+
*/
|
|
5
|
+
rowCount: number;
|
|
6
|
+
/**
|
|
7
|
+
* The number of skeleton cells to render in each row.
|
|
8
|
+
*/
|
|
9
|
+
columnCount: number;
|
|
10
|
+
};
|
|
11
|
+
export default function DataTableSkeletonRows({ rowCount, columnCount, }: Props): import('react').JSX.Element;
|
|
@@ -52,7 +52,7 @@ import Select from "../Select/Select";
|
|
|
52
52
|
import SelectItem from "../SelectItem/SelectItem";
|
|
53
53
|
import Button from "../Button/Button";
|
|
54
54
|
import Switch from "../Switch/Switch";
|
|
55
|
-
import { within } from "storybook/test";
|
|
55
|
+
import { expect, within } from "storybook/test";
|
|
56
56
|
import { someTime } from "@internal/test-utils-storybook/test-utils-storybook";
|
|
57
57
|
import DataTableRoot from "./DataTableRoot/DataTableRoot";
|
|
58
58
|
import DataTableRow from "./DataTableRow/DataTableRow";
|
|
@@ -161,9 +161,13 @@ export const WithReactTableFrontEndManaged: StoryObj<typeof DataTable> = {
|
|
|
161
161
|
[],
|
|
162
162
|
);
|
|
163
163
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
// Fetch the whole dataset once: filtering, sorting and pagination happen client-side
|
|
165
|
+
// `isLoading` is only true on the first load, while there is no data yet
|
|
166
|
+
const { data, isLoading } = useQuery({
|
|
167
|
+
queryKey: ["all-users"],
|
|
168
|
+
queryFn: async (): Promise<Array<User>> => {
|
|
169
|
+
await someTime(100); // Your API call goes here
|
|
170
|
+
return arrayOfLength(300).map((index) => ({
|
|
167
171
|
firstname: `Firstname ${index + 1}`,
|
|
168
172
|
lastname: `Lastname ${index + 1}`,
|
|
169
173
|
email: `john-doe${index + 1}@example.com`,
|
|
@@ -172,9 +176,13 @@ export const WithReactTableFrontEndManaged: StoryObj<typeof DataTable> = {
|
|
|
172
176
|
day: DAYS[index % DAYS.length],
|
|
173
177
|
organization: ORGANIZATIONS[index % ORGANIZATIONS.length],
|
|
174
178
|
isActive: index % 2 === 0,
|
|
175
|
-
}))
|
|
176
|
-
|
|
177
|
-
);
|
|
179
|
+
}));
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// The table data needs a stable reference: an inline `data ?? []` would create a new array on every render while loading
|
|
184
|
+
// ⚠ This should be memoized!
|
|
185
|
+
const users = useMemo(() => data ?? [], [data]);
|
|
178
186
|
|
|
179
187
|
// Here starts the interesting stuff: columns definitions.
|
|
180
188
|
// All the features of the filters comes from those columns definitions
|
|
@@ -347,7 +355,7 @@ export const WithReactTableFrontEndManaged: StoryObj<typeof DataTable> = {
|
|
|
347
355
|
// And here we define the table as usual
|
|
348
356
|
const table = useTable({
|
|
349
357
|
features: clientFeatures,
|
|
350
|
-
data,
|
|
358
|
+
data: users,
|
|
351
359
|
columns,
|
|
352
360
|
initialState: {
|
|
353
361
|
globalFilter: "", // Add this to show the global filter (aka the Search bar)
|
|
@@ -400,6 +408,7 @@ export const WithReactTableFrontEndManaged: StoryObj<typeof DataTable> = {
|
|
|
400
408
|
table={table}
|
|
401
409
|
pagination={pagination}
|
|
402
410
|
itemsPerPageOptions={[12, 24, 36, 48, 60]} // Add this to customize the page size options available in the select
|
|
411
|
+
isLoading={isLoading}
|
|
403
412
|
/>
|
|
404
413
|
</>
|
|
405
414
|
);
|
|
@@ -407,6 +416,10 @@ export const WithReactTableFrontEndManaged: StoryObj<typeof DataTable> = {
|
|
|
407
416
|
|
|
408
417
|
return <ParentComponent />;
|
|
409
418
|
},
|
|
419
|
+
play: async ({ canvasElement }) => {
|
|
420
|
+
const canvas = within(canvasElement);
|
|
421
|
+
await canvas.findByText("Firstname 1");
|
|
422
|
+
},
|
|
410
423
|
};
|
|
411
424
|
|
|
412
425
|
type User = {
|
|
@@ -485,7 +498,8 @@ export const WithReactTableBackEndManaged: StoryObj<typeof DataTable> = {
|
|
|
485
498
|
);
|
|
486
499
|
|
|
487
500
|
// Fetch data using the table state (filters, pagination, sorting, etc.)
|
|
488
|
-
|
|
501
|
+
// `isLoading` is only true on the first load: on refetch, `keepPreviousData` keeps the current rows displayed
|
|
502
|
+
const { data, isLoading } = useQuery({
|
|
489
503
|
placeholderData: keepPreviousData, // from @tanstack/react-query, recommended to use it in this scenario
|
|
490
504
|
queryKey: ["users", pagination, sorting, columnFilters],
|
|
491
505
|
queryFn: () =>
|
|
@@ -499,6 +513,10 @@ export const WithReactTableBackEndManaged: StoryObj<typeof DataTable> = {
|
|
|
499
513
|
}),
|
|
500
514
|
});
|
|
501
515
|
|
|
516
|
+
// The table data needs a stable reference: an inline `data?.data ?? []` would create a new array on every render while loading
|
|
517
|
+
// ⚠ This should be memoized!
|
|
518
|
+
const users = useMemo(() => data?.data ?? [], [data]);
|
|
519
|
+
|
|
502
520
|
// You will need to create a column helper, typed with the registered features
|
|
503
521
|
// ⚠ This should be memoized!
|
|
504
522
|
const columnHelper = useMemo(
|
|
@@ -595,7 +613,7 @@ export const WithReactTableBackEndManaged: StoryObj<typeof DataTable> = {
|
|
|
595
613
|
// Instantiate the table with @tanstack/react-table
|
|
596
614
|
const table = useTable({
|
|
597
615
|
features: serverFeatures,
|
|
598
|
-
data:
|
|
616
|
+
data: users,
|
|
599
617
|
columns,
|
|
600
618
|
manualPagination: true,
|
|
601
619
|
manualSorting: true,
|
|
@@ -615,7 +633,11 @@ export const WithReactTableBackEndManaged: StoryObj<typeof DataTable> = {
|
|
|
615
633
|
return (
|
|
616
634
|
<>
|
|
617
635
|
<Filter table={table} />
|
|
618
|
-
<DataTable
|
|
636
|
+
<DataTable
|
|
637
|
+
table={table}
|
|
638
|
+
pagination={pagination}
|
|
639
|
+
isLoading={isLoading}
|
|
640
|
+
/>
|
|
619
641
|
</>
|
|
620
642
|
);
|
|
621
643
|
}
|
|
@@ -677,6 +699,85 @@ export const Empty: StoryObj<typeof DataTable> = {
|
|
|
677
699
|
},
|
|
678
700
|
};
|
|
679
701
|
|
|
702
|
+
export const Loading: StoryObj<typeof DataTable> = {
|
|
703
|
+
render: () => {
|
|
704
|
+
const ParentComponent = () => {
|
|
705
|
+
const columnHelper = useMemo(
|
|
706
|
+
() => createColumnHelper<typeof clientFeatures, User>(),
|
|
707
|
+
[],
|
|
708
|
+
);
|
|
709
|
+
|
|
710
|
+
const data = useMemo(() => [], []);
|
|
711
|
+
|
|
712
|
+
const columns = useMemo(
|
|
713
|
+
() =>
|
|
714
|
+
columnHelper.columns([
|
|
715
|
+
columnHelper.accessor("firstname", {
|
|
716
|
+
header: ({ header }) => (
|
|
717
|
+
<DataTableHeader
|
|
718
|
+
onSortClick={header.column.getToggleSortingHandler()}
|
|
719
|
+
sortDirection={header.column.getIsSorted() || "none"}
|
|
720
|
+
text="Firstname"
|
|
721
|
+
/>
|
|
722
|
+
),
|
|
723
|
+
cell: (props) => (
|
|
724
|
+
<DataTableCell>{props.getValue()}</DataTableCell>
|
|
725
|
+
),
|
|
726
|
+
}),
|
|
727
|
+
columnHelper.accessor("lastname", {
|
|
728
|
+
header: () => <DataTableHeader text="Lastname" />,
|
|
729
|
+
cell: (props) => (
|
|
730
|
+
<DataTableCell>{props.getValue()}</DataTableCell>
|
|
731
|
+
),
|
|
732
|
+
}),
|
|
733
|
+
columnHelper.accessor("email", {
|
|
734
|
+
header: () => <DataTableHeader text="Email" />,
|
|
735
|
+
cell: (props) => (
|
|
736
|
+
<DataTableCell>{props.getValue()}</DataTableCell>
|
|
737
|
+
),
|
|
738
|
+
}),
|
|
739
|
+
]),
|
|
740
|
+
[columnHelper],
|
|
741
|
+
);
|
|
742
|
+
|
|
743
|
+
const table = useTable({
|
|
744
|
+
features: clientFeatures,
|
|
745
|
+
data,
|
|
746
|
+
columns,
|
|
747
|
+
initialState: {
|
|
748
|
+
pagination: { pageIndex: 0, pageSize: 10 },
|
|
749
|
+
},
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
const pagination = table.state.pagination;
|
|
753
|
+
|
|
754
|
+
return <DataTable table={table} pagination={pagination} isLoading />;
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
return <ParentComponent />;
|
|
758
|
+
},
|
|
759
|
+
play: async ({ canvasElement }) => {
|
|
760
|
+
const canvas = within(canvasElement);
|
|
761
|
+
|
|
762
|
+
await expect(canvas.getByRole("table")).toHaveAttribute(
|
|
763
|
+
"aria-busy",
|
|
764
|
+
"true",
|
|
765
|
+
);
|
|
766
|
+
await expect(canvas.getAllByRole("columnheader")).toHaveLength(3);
|
|
767
|
+
// 1 skeleton row per item of the page (pageSize: 10)
|
|
768
|
+
await expect(
|
|
769
|
+
canvasElement.querySelectorAll(
|
|
770
|
+
'[data-design-system-component="DataTableSkeletonRows"]',
|
|
771
|
+
),
|
|
772
|
+
).toHaveLength(10);
|
|
773
|
+
// Skeleton rows are hidden from assistive technologies: only the header row is exposed
|
|
774
|
+
await expect(canvas.getAllByRole("row")).toHaveLength(1);
|
|
775
|
+
await expect(
|
|
776
|
+
canvas.queryByText("Sorry, there is currently no data to display."),
|
|
777
|
+
).not.toBeInTheDocument();
|
|
778
|
+
},
|
|
779
|
+
};
|
|
780
|
+
|
|
680
781
|
export const WithSubColumns: StoryObj<typeof DataTable> = {
|
|
681
782
|
render: () => {
|
|
682
783
|
const data: Array<User> = arrayOfLength(300).map((index) => ({
|
package/package.json
CHANGED