@agregio-solutions/design-system 1.102.5 → 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.
Files changed (51) hide show
  1. package/dist/design-system.cjs +544 -629
  2. package/dist/design-system.js +7772 -27711
  3. package/dist/packages/components/Button/doc.md +33 -2
  4. package/dist/packages/components/Combobox/Combobox.d.ts +2 -2
  5. package/dist/packages/components/Combobox/doc.md +0 -4
  6. package/dist/packages/components/DataTable/DataTable.d.ts +15 -6
  7. package/dist/packages/components/DataTable/DataTableRow/DataTableRow.d.ts +1 -6
  8. package/dist/packages/components/DataTable/doc.md +495 -389
  9. package/dist/packages/components/DatePicker/DatePicker.d.ts +2 -2
  10. package/dist/packages/components/DatePicker/doc.md +0 -1
  11. package/dist/packages/components/DateRangePicker/DateRangePicker.d.ts +2 -2
  12. package/dist/packages/components/DateRangePicker/doc.md +0 -1
  13. package/dist/packages/components/Drawer/Drawer.d.ts +7 -24
  14. package/dist/packages/components/Drawer/doc.md +136 -106
  15. package/dist/packages/components/Dropdown/Dropdown.d.ts +1 -7
  16. package/dist/packages/components/Dropdown/doc.md +2 -29
  17. package/dist/packages/components/FileUpload/doc.md +0 -2
  18. package/dist/packages/components/Filter/Filter.d.ts +24 -10
  19. package/dist/packages/components/Filter/doc.md +31 -1
  20. package/dist/packages/components/Modal/doc.md +1 -2
  21. package/dist/packages/components/Navigation/Navigation.d.ts +2 -1
  22. package/dist/packages/components/Navigation/doc.md +14 -9
  23. package/dist/packages/components/NavigationItem/NavigationItem.d.ts +4 -0
  24. package/dist/packages/components/NavigationItem/doc.md +15 -3
  25. package/dist/packages/components/PageLayout/doc.md +190 -149
  26. package/dist/packages/components/Select/Select.d.ts +1 -1
  27. package/dist/packages/components/Select/doc.md +0 -9
  28. package/dist/packages/components/Slider/Slider.d.ts +3 -9
  29. package/dist/packages/components/Slider/doc.md +1 -4
  30. package/dist/packages/components/Tabs/Tab/Tab.d.ts +0 -5
  31. package/dist/packages/components/TextInput/TextInput.d.ts +2 -2
  32. package/dist/packages/components/TextInput/doc.md +0 -22
  33. package/dist/packages/components/TimeField/TimeField.d.ts +2 -2
  34. package/dist/packages/components/TimeField/doc.md +0 -1
  35. package/dist/packages/components/TimePicker/TimePicker.d.ts +2 -2
  36. package/dist/packages/components/Timeline/Timeline.styled.d.ts +15 -20
  37. package/dist/packages/components/Timeline/components/BlockLineContainer/BlockLineContainer.styled.d.ts +23 -27
  38. package/dist/packages/components/Timeline/components/GroupLineContainer/GroupLineContainer.styled.d.ts +3 -4
  39. package/dist/packages/components/Timeline/components/TimelineRange/TimelineRange.styled.d.ts +12 -16
  40. package/dist/packages/components/Timeline/doc.md +38 -1
  41. package/dist/packages/components/YearMonthPicker/YearMonthPicker.d.ts +2 -2
  42. package/dist/packages/components/YearMonthPicker/doc.md +0 -1
  43. package/dist/packages/ds-design-tokens/index.d.ts +1 -2
  44. package/dist/packages/ds-design-tokens/src/tokens.style.d.ts +138 -135
  45. package/dist/packages/index.d.ts +3 -4
  46. package/dist/packages/internal-components/FormGroupWrapper/FormGroupWrapper.d.ts +0 -12
  47. package/dist/public_docs/index.md +4 -1
  48. package/dist/public_docs/migration-guide-v2.md +444 -0
  49. package/package.json +6 -8
  50. package/dist/packages/ds-design-tokens/src/fontSize.style.d.ts +0 -60
  51. package/dist/packages/mui/mui-theme.d.ts +0 -3
@@ -20,13 +20,23 @@ import {
20
20
  ColumnFiltersState,
21
21
  ColumnSort,
22
22
  SortingState,
23
+ columnFilteringFeature,
23
24
  createColumnHelper,
24
- getCoreRowModel,
25
- getExpandedRowModel,
26
- getFilteredRowModel,
27
- getPaginationRowModel,
28
- getSortedRowModel,
29
- useReactTable,
25
+ createExpandedRowModel,
26
+ createFilteredRowModel,
27
+ createPaginatedRowModel,
28
+ createSortedRowModel,
29
+ filterFn_arrIncludesSome,
30
+ filterFn_equals,
31
+ filterFn_includesString,
32
+ globalFilteringFeature,
33
+ rowExpandingFeature,
34
+ rowPaginationFeature,
35
+ rowSortingFeature,
36
+ sortFn_alphanumeric,
37
+ sortFn_text,
38
+ tableFeatures,
39
+ useTable,
30
40
  } from "@tanstack/react-table";
31
41
  import DataTableHeader from "@packages/components/DataTable/DataTableHeader/DataTableHeader";
32
42
  import { useMemo, useState } from "react";
@@ -46,6 +56,44 @@ import { someTime } from "@internal/test-utils-storybook/test-utils-storybook";
46
56
  import DataTableRoot from "./DataTableRoot/DataTableRoot";
47
57
  import DataTableRow from "./DataTableRow/DataTableRow";
48
58
 
59
+ // Since react-table v9, every feature a table uses has to be registered through
60
+ // `tableFeatures()`. Declare the set once, outside of the component, and derive
61
+ // the types of the column helpers and column metas from it with `typeof`.
62
+ const clientFeatures = tableFeatures({
63
+ columnFilteringFeature,
64
+ globalFilteringFeature,
65
+ rowSortingFeature,
66
+ rowPaginationFeature,
67
+ // Register only the built-ins the columns use: the full `filterFns` / `sortFns`
68
+ // registries are deprecated because they pull every built-in into the bundle.
69
+ filterFns: {
70
+ arrIncludesSome: filterFn_arrIncludesSome,
71
+ equals: filterFn_equals,
72
+ includesString: filterFn_includesString,
73
+ },
74
+ sortFns: { alphanumeric: sortFn_alphanumeric, text: sortFn_text },
75
+ filteredRowModel: createFilteredRowModel(),
76
+ sortedRowModel: createSortedRowModel(),
77
+ paginatedRowModel: createPaginatedRowModel(),
78
+ });
79
+
80
+ // A back-end managed table computes its rows server-side: register the features
81
+ // for their state and APIs, but none of their row models.
82
+ const serverFeatures = tableFeatures({
83
+ columnFilteringFeature,
84
+ rowSortingFeature,
85
+ rowPaginationFeature,
86
+ });
87
+
88
+ const expandingFeatures = tableFeatures({
89
+ rowSortingFeature,
90
+ rowExpandingFeature,
91
+ sortedRowModel: createSortedRowModel(),
92
+ expandedRowModel: createExpandedRowModel(),
93
+ });
94
+
95
+ const coreOnlyFeatures = tableFeatures({});
96
+
49
97
  const meta: Meta<typeof DataTable> = {
50
98
  component: DataTable,
51
99
  parameters: {
@@ -130,178 +178,193 @@ export const WithReactTableFrontEndManaged: StoryObj<typeof DataTable> = {
130
178
  // Here starts the interesting stuff: columns definitions.
131
179
  // All the features of the filters comes from those columns definitions
132
180
  // Please read carefully each column, they all serve as a different example of how to use the filters
133
- const columnHelper = useMemo(() => createColumnHelper<User>(), []);
181
+ const columnHelper = useMemo(
182
+ () => createColumnHelper<typeof clientFeatures, User>(),
183
+ [],
184
+ );
134
185
 
135
186
  const columns = useMemo(
136
- () => [
137
- columnHelper.accessor("firstname", {
138
- // Here, because there is no "FilterInDrawer" component, there will be nothing in the filter drawer related to the firstname
139
- // But the firstname can still be filtered by the global filter (search input)
140
- header: ({ header }) => (
141
- <DataTableHeader
142
- onSortClick={header.column.getToggleSortingHandler()}
143
- sortDirection={header.column.getIsSorted() || "none"}
144
- text="Firstname"
145
- />
146
- ),
147
- cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
148
- }),
149
- // Here is an example using a "FilterInDrawer" component that render a radio group to filter the day
150
- // Please note the "filterFn" is "arrIncludes" for this king of value (Array<string>)
151
- // Also note the usage of `satisfies TypeTableColumnWithFilter<User>` to help you type check the column definition (because meta is a custom property)
152
- // Finally, you will note that the Chips are auto-generated, if you do not want to use them you can set the `skipFilterChips` to true
153
- columnHelper.accessor("day", {
154
- id: "Day",
155
- header: () => <DataTableHeader text="Day" />,
156
- cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
157
- filterFn: "equals",
158
- meta: {
159
- FilterInDrawer: ({ column }) => (
160
- <div>
161
- <b>Filter by day:</b>
162
-
163
- {DAYS.map((day) => (
164
- <Radio
165
- label={day}
166
- key={day}
167
- checked={column.getFilterValue() === day}
168
- onChange={() => column.setFilterValue(day)}
169
- />
170
- ))}
171
- </div>
187
+ () =>
188
+ columnHelper.columns([
189
+ columnHelper.accessor("firstname", {
190
+ // Here, because there is no "FilterInDrawer" component, there will be nothing in the filter drawer related to the firstname
191
+ // But the firstname can still be filtered by the global filter (search input)
192
+ header: ({ header }) => (
193
+ <DataTableHeader
194
+ onSortClick={header.column.getToggleSortingHandler()}
195
+ sortDirection={header.column.getIsSorted() || "none"}
196
+ text="Firstname"
197
+ />
172
198
  ),
173
- } satisfies TypeTableColumnWithFilter<User>,
174
- }),
175
- // Here is another example using a "FilterInDrawer" component that render a checkbox group to filter the food
176
- // Please note the "filterFn" is "arrIncludesSome" -> we want to include all entries that match at least one food
177
- // Also note the usage of `satisfies TypeTableColumnWithFilter<User>` to help you type check the column definition (because meta is a custom property)
178
- // Finally, you will note that the Chips are auto-generated, if you do not want to use them you can set the `skipFilterChips` to true
179
- columnHelper.accessor("foods", {
180
- id: "Foods",
181
- header: () => <DataTableHeader text="Foods" />,
182
- cell: (props) => (
183
- <DataTableCell>{props.getValue()?.join(", ")}</DataTableCell>
184
- ),
185
- filterFn: "arrIncludesSome",
186
- meta: {
187
- FilterInDrawer: ({ column }) => (
188
- <div style={{ marginTop: 8 }}>
189
- <b>Filter by food (at least one):</b>
190
- <CheckboxGroup
191
- value={(column.getFilterValue() as any) || []}
192
- onChange={column.setFilterValue}
193
- aria-label="Filter by food (at least one)"
199
+ cell: (props) => (
200
+ <DataTableCell>{props.getValue()}</DataTableCell>
201
+ ),
202
+ }),
203
+ // Here is an example using a "FilterInDrawer" component that render a radio group to filter the day
204
+ // Please note the "filterFn" is "arrIncludes" for this king of value (Array<string>)
205
+ // Also note the usage of `satisfies TypeTableColumnWithFilter<typeof clientFeatures, User>` to help you type check the column definition (because meta is a custom property)
206
+ // Finally, you will note that the Chips are auto-generated, if you do not want to use them you can set the `skipFilterChips` to true
207
+ columnHelper.accessor("day", {
208
+ id: "Day",
209
+ header: () => <DataTableHeader text="Day" />,
210
+ cell: (props) => (
211
+ <DataTableCell>{props.getValue()}</DataTableCell>
212
+ ),
213
+ filterFn: "equals",
214
+ meta: {
215
+ FilterInDrawer: ({ column }) => (
216
+ <div>
217
+ <b>Filter by day:</b>
218
+
219
+ {DAYS.map((day) => (
220
+ <Radio
221
+ label={day}
222
+ key={day}
223
+ checked={column.getFilterValue() === day}
224
+ onChange={() => column.setFilterValue(day)}
225
+ />
226
+ ))}
227
+ </div>
228
+ ),
229
+ } satisfies TypeTableColumnWithFilter<
230
+ typeof clientFeatures,
231
+ User
232
+ >,
233
+ }),
234
+ // Here is another example using a "FilterInDrawer" component that render a checkbox group to filter the food
235
+ // Please note the "filterFn" is "arrIncludesSome" -> we want to include all entries that match at least one food
236
+ // Also note the usage of `satisfies TypeTableColumnWithFilter<typeof clientFeatures, User>` to help you type check the column definition (because meta is a custom property)
237
+ // Finally, you will note that the Chips are auto-generated, if you do not want to use them you can set the `skipFilterChips` to true
238
+ columnHelper.accessor("foods", {
239
+ id: "Foods",
240
+ header: () => <DataTableHeader text="Foods" />,
241
+ cell: (props) => (
242
+ <DataTableCell>{props.getValue()?.join(", ")}</DataTableCell>
243
+ ),
244
+ filterFn: "arrIncludesSome",
245
+ meta: {
246
+ FilterInDrawer: ({ column }) => (
247
+ <div style={{ marginTop: 8 }}>
248
+ <b>Filter by food (at least one):</b>
249
+ <CheckboxGroup
250
+ value={(column.getFilterValue() as any) || []}
251
+ onChange={(value) => column.setFilterValue(value)}
252
+ aria-label="Filter by food (at least one)"
253
+ >
254
+ {ALL_FOODS.map((food) => (
255
+ <div key={food}>
256
+ <Checkbox label={food} value={food} />
257
+ </div>
258
+ ))}
259
+ </CheckboxGroup>
260
+ </div>
261
+ ),
262
+ } satisfies TypeTableColumnWithFilter<
263
+ typeof clientFeatures,
264
+ User
265
+ >,
266
+ }),
267
+ // Here is another example using a "FilterInDrawer" component that render a select to filter the organization
268
+ // Please note the "filterFn" is a custom function that will filter the data based on the organization id
269
+ // Also note the usage of `satisfies TypeTableColumnWithFilter<typeof clientFeatures, User>` to help you type check the column definition (because meta is a custom property)
270
+ // And most important here, we have a custom meta property `filterChips` that will generate a chip to display the selected organization
271
+ // The Filter can not handle automatic chips when the data is a complex object, so you have to handle that manually
272
+ columnHelper.accessor("organization", {
273
+ id: "Organization",
274
+ header: () => <DataTableHeader text="Organization" />,
275
+ cell: (props) => (
276
+ <DataTableCell>{props.getValue()?.name}</DataTableCell>
277
+ ),
278
+ filterFn: (row, _, filterValue) => {
279
+ if (!filterValue) return true;
280
+ return row.original.organization?.id === filterValue;
281
+ },
282
+ meta: {
283
+ filterChips: ({ column }) => {
284
+ const value = column.getFilterValue();
285
+ const organization = ORGANIZATIONS.find(
286
+ (org) => org.id === value,
287
+ );
288
+ if (!organization) return [];
289
+ return [
290
+ {
291
+ value: `Organization: ${organization.name}`,
292
+ onClose: () => column.setFilterValue(null),
293
+ },
294
+ ];
295
+ },
296
+ FilterInDrawer: ({ column }) => (
297
+ <Select
298
+ id="organization-select"
299
+ label="Filter by organization:"
300
+ mode="single"
301
+ value={(column.getFilterValue() as any) || null}
302
+ onChange={(value) => column.setFilterValue(value)}
303
+ fullWidth
304
+ wrapperProps={{ style: { marginBottom: 8 } }}
194
305
  >
195
- {ALL_FOODS.map((food) => (
196
- <div key={food}>
197
- <Checkbox label={food} value={food} />
198
- </div>
306
+ {ORGANIZATIONS.map((organization) => (
307
+ <SelectItem
308
+ text={organization.name}
309
+ id={organization.id}
310
+ key={organization.id}
311
+ />
199
312
  ))}
200
- </CheckboxGroup>
201
- </div>
313
+ </Select>
314
+ ),
315
+ } satisfies TypeTableColumnWithFilter<
316
+ typeof clientFeatures,
317
+ User
318
+ >,
319
+ }),
320
+ // Here is an example that uses a filter outside of the drawer.
321
+ // See the "extraLeftContent" prop to see how to connect it to react-table filtering features
322
+ columnHelper.accessor("isActive", {
323
+ header: () => <DataTableHeader text="Active" />,
324
+ cell: (props) => (
325
+ <DataTableCell>{props.getValue() ? "✅" : "❌"}</DataTableCell>
202
326
  ),
203
- } satisfies TypeTableColumnWithFilter<User>,
204
- }),
205
- // Here is another example using a "FilterInDrawer" component that render a select to filter the organization
206
- // Please note the "filterFn" is a custom function that will filter the data based on the organization id
207
- // Also note the usage of `satisfies TypeTableColumnWithFilter<User>` to help you type check the column definition (because meta is a custom property)
208
- // And most important here, we have a custom meta property `filterChips` that will generate a chip to display the selected organization
209
- // The Filter can not handle automatic chips when the data is a complex object, so you have to handle that manually
210
- columnHelper.accessor("organization", {
211
- id: "Organization",
212
- header: () => <DataTableHeader text="Organization" />,
213
- cell: (props) => (
214
- <DataTableCell>{props.getValue()?.name}</DataTableCell>
215
- ),
216
- filterFn: (row, _, filterValue) => {
217
- if (!filterValue) return true;
218
- return row.original.organization?.id === filterValue;
219
- },
220
- meta: {
221
- filterChips: ({ column }) => {
222
- const value = column.getFilterValue();
223
- const organization = ORGANIZATIONS.find(
224
- (org) => org.id === value,
225
- );
226
- if (!organization) return [];
227
- return [
228
- {
229
- value: `Organization: ${organization.name}`,
230
- onClose: () => column.setFilterValue(null),
231
- },
232
- ];
327
+ filterFn: (row, _, filterValue) => {
328
+ if (!filterValue) return true;
329
+ return !!row.original.isActive;
233
330
  },
234
- FilterInDrawer: ({ column }) => (
235
- <Select
236
- id="organization-select"
237
- label="Filter by organization:"
238
- mode="single"
239
- value={(column.getFilterValue() as any) || null}
240
- onChange={column.setFilterValue}
241
- fullWidth
242
- wrapperProps={{ style: { marginBottom: 8 } }}
243
- >
244
- {ORGANIZATIONS.map((organization) => (
245
- <SelectItem
246
- text={organization.name}
247
- id={organization.id}
248
- key={organization.id}
249
- />
250
- ))}
251
- </Select>
331
+ }),
332
+ // Here is an example of an empty column, for example to display an actions bar
333
+ // You can note that the header is empty, the text is optional
334
+ columnHelper.display({
335
+ id: "actions",
336
+ header: () => <DataTableHeader />,
337
+ cell: () => (
338
+ <DataTableCell>
339
+ <Button mode="secondary" iconLeft="edit" />
340
+ </DataTableCell>
252
341
  ),
253
- } satisfies TypeTableColumnWithFilter<User>,
254
- }),
255
- // Here is an example that uses a filter outside of the drawer.
256
- // See the "extraLeftContent" prop to see how to connect it to react-table filtering features
257
- columnHelper.accessor("isActive", {
258
- header: () => <DataTableHeader text="Active" />,
259
- cell: (props) => (
260
- <DataTableCell>{props.getValue() ? "✅" : "❌"}</DataTableCell>
261
- ),
262
- filterFn: (row, _, filterValue) => {
263
- if (!filterValue) return true;
264
- return !!row.original.isActive;
265
- },
266
- }),
267
- // Here is an example of an empty column, for example to display an actions bar
268
- // You can note that the header is empty, the text is optional
269
- columnHelper.display({
270
- id: "actions",
271
- header: () => <DataTableHeader />,
272
- cell: () => (
273
- <DataTableCell>
274
- <Button mode="secondary" iconLeft="edit" />
275
- </DataTableCell>
276
- ),
277
- }),
278
- ],
342
+ }),
343
+ ]),
279
344
  [ALL_FOODS, DAYS, ORGANIZATIONS, columnHelper],
280
345
  );
281
346
 
282
347
  // And here we define the table as usual
283
- const table = useReactTable({
348
+ const table = useTable({
349
+ features: clientFeatures,
284
350
  data,
285
351
  columns,
286
352
  initialState: {
287
353
  globalFilter: "", // Add this to show the global filter (aka the Search bar)
288
354
  pagination: {
355
+ pageIndex: 0,
289
356
  pageSize: 12, // Add this to customize the page size
290
357
  },
291
358
  },
292
- getCoreRowModel: getCoreRowModel(),
293
- getSortedRowModel: getSortedRowModel(),
294
- getFilteredRowModel: getFilteredRowModel(),
295
- getPaginationRowModel: getPaginationRowModel(),
296
359
  });
297
360
 
298
361
  // Get the pagination state
299
- const pagination = table.getState().pagination;
362
+ const pagination = table.state.pagination;
300
363
 
301
364
  // Render the table using the base DataTable component
302
365
  return (
303
366
  <>
304
- <Filter<User>
367
+ <Filter
305
368
  onDownloadClick={() => alert("Download")}
306
369
  onSettingsClick={() => alert("Settings")}
307
370
  table={table}
@@ -436,93 +499,104 @@ export const WithReactTableBackEndManaged: StoryObj<typeof DataTable> = {
436
499
  }),
437
500
  });
438
501
 
439
- // You will need to create a column helper
502
+ // You will need to create a column helper, typed with the registered features
440
503
  // ⚠ This should be memoized!
441
- const columnHelper = useMemo(() => createColumnHelper<User>(), []);
504
+ const columnHelper = useMemo(
505
+ () => createColumnHelper<typeof serverFeatures, User>(),
506
+ [],
507
+ );
442
508
 
443
509
  // Then, you can define the columns
444
510
  // ⚠ This should be memoized!
445
511
  const columns = useMemo(
446
- () => [
447
- // Example of a custom column for initials (no data model, so no filters, etc)
448
- columnHelper.display({
449
- id: "initials",
450
- header: () => <DataTableHeader text="Initials" />,
451
- cell: (props) => (
452
- <DataTableCell>
453
- {/*Here is an example of how you can access the data model values directly (if needed) */}
454
- {props.row.original.firstname[0]}
455
- {props.row.original.lastname[0]}
456
- </DataTableCell>
457
- ),
458
- }),
459
- // Example of a column with sorting (accessor means it has data model)
460
- columnHelper.accessor("firstname", {
461
- header: ({ header }) => (
462
- <DataTableHeader
463
- onSortClick={header.column.getToggleSortingHandler()}
464
- sortDirection={header.column.getIsSorted() || "none"}
465
- text="Firstname"
466
- />
467
- ),
468
- cell: (props) => (
469
- <DataTableCell iconLeft="star_outline">
470
- {/* because the accessor key ("firstname") is a data model object key, we can access it directly with getValue() */}
471
- {props.getValue()}
472
- </DataTableCell>
473
- ),
474
- }),
475
- columnHelper.accessor("lastname", {
476
- header: ({ header }) => (
477
- <DataTableHeader
478
- onSortClick={header.column.getToggleSortingHandler()}
479
- sortDirection={header.column.getIsSorted() || "none"}
480
- text="Lastname"
481
- />
482
- ),
483
- cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
484
- }),
485
- columnHelper.accessor("email", {
486
- header: ({ header }) => (
487
- <DataTableHeader
488
- onSortClick={header.column.getToggleSortingHandler()}
489
- sortDirection={header.column.getIsSorted() || "none"}
490
- text="Email"
491
- iconLeft="send"
492
- />
493
- ),
494
- cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
495
- meta: {
496
- FilterInDrawer: ({ column }) => (
497
- <Select
498
- id="email-select"
499
- label="Filter by email:"
500
- mode="multiple"
501
- selectedKeys={(column.getFilterValue() as any) || []}
502
- onSelectionChange={column.setFilterValue}
503
- fullWidth
504
- wrapperProps={{ style: { marginBottom: 8 } }}
505
- >
506
- {arrayOfLength(10).map((index) => (
507
- <SelectItem
508
- text={`john-doe${index + 1}@example.com`}
509
- id={`john-doe${index + 1}@example.com`}
510
- key={`john-doe${index + 1}@example.com`}
511
- />
512
- ))}
513
- </Select>
512
+ () =>
513
+ columnHelper.columns([
514
+ // Example of a custom column for initials (no data model, so no filters, etc)
515
+ columnHelper.display({
516
+ id: "initials",
517
+ header: () => <DataTableHeader text="Initials" />,
518
+ cell: (props) => (
519
+ <DataTableCell>
520
+ {/*Here is an example of how you can access the data model values directly (if needed) */}
521
+ {props.row.original.firstname[0]}
522
+ {props.row.original.lastname[0]}
523
+ </DataTableCell>
514
524
  ),
515
- } satisfies TypeTableColumnWithFilter<User>,
516
- }),
517
- ],
525
+ }),
526
+ // Example of a column with sorting (accessor means it has data model)
527
+ columnHelper.accessor("firstname", {
528
+ header: ({ header }) => (
529
+ <DataTableHeader
530
+ onSortClick={header.column.getToggleSortingHandler()}
531
+ sortDirection={header.column.getIsSorted() || "none"}
532
+ text="Firstname"
533
+ />
534
+ ),
535
+ cell: (props) => (
536
+ <DataTableCell iconLeft="star_outline">
537
+ {/* because the accessor key ("firstname") is a data model object key, we can access it directly with getValue() */}
538
+ {props.getValue()}
539
+ </DataTableCell>
540
+ ),
541
+ }),
542
+ columnHelper.accessor("lastname", {
543
+ header: ({ header }) => (
544
+ <DataTableHeader
545
+ onSortClick={header.column.getToggleSortingHandler()}
546
+ sortDirection={header.column.getIsSorted() || "none"}
547
+ text="Lastname"
548
+ />
549
+ ),
550
+ cell: (props) => (
551
+ <DataTableCell>{props.getValue()}</DataTableCell>
552
+ ),
553
+ }),
554
+ columnHelper.accessor("email", {
555
+ header: ({ header }) => (
556
+ <DataTableHeader
557
+ onSortClick={header.column.getToggleSortingHandler()}
558
+ sortDirection={header.column.getIsSorted() || "none"}
559
+ text="Email"
560
+ iconLeft="send"
561
+ />
562
+ ),
563
+ cell: (props) => (
564
+ <DataTableCell>{props.getValue()}</DataTableCell>
565
+ ),
566
+ meta: {
567
+ FilterInDrawer: ({ column }) => (
568
+ <Select
569
+ id="email-select"
570
+ label="Filter by email:"
571
+ mode="multiple"
572
+ selectedKeys={(column.getFilterValue() as any) || []}
573
+ onSelectionChange={(keys) => column.setFilterValue(keys)}
574
+ fullWidth
575
+ wrapperProps={{ style: { marginBottom: 8 } }}
576
+ >
577
+ {arrayOfLength(10).map((index) => (
578
+ <SelectItem
579
+ text={`john-doe${index + 1}@example.com`}
580
+ id={`john-doe${index + 1}@example.com`}
581
+ key={`john-doe${index + 1}@example.com`}
582
+ />
583
+ ))}
584
+ </Select>
585
+ ),
586
+ } satisfies TypeTableColumnWithFilter<
587
+ typeof serverFeatures,
588
+ User
589
+ >,
590
+ }),
591
+ ]),
518
592
  [columnHelper],
519
593
  );
520
594
 
521
595
  // Instantiate the table with @tanstack/react-table
522
- const table = useReactTable({
596
+ const table = useTable({
597
+ features: serverFeatures,
523
598
  data: data?.data || [],
524
599
  columns,
525
- getCoreRowModel: getCoreRowModel(), // from @tanstack/react-table
526
600
  manualPagination: true,
527
601
  manualSorting: true,
528
602
  manualFiltering: true,
@@ -540,7 +614,7 @@ export const WithReactTableBackEndManaged: StoryObj<typeof DataTable> = {
540
614
  // Render the table using the base DataTable component
541
615
  return (
542
616
  <>
543
- <Filter<User> table={table} />
617
+ <Filter table={table} />
544
618
  <DataTable table={table} pagination={pagination} />
545
619
  </>
546
620
  );
@@ -553,38 +627,39 @@ export const WithReactTableBackEndManaged: StoryObj<typeof DataTable> = {
553
627
  export const Empty: StoryObj<typeof DataTable> = {
554
628
  render: () => {
555
629
  const ParentComponent = () => {
556
- const columnHelper = useMemo(() => createColumnHelper<User>(), []);
630
+ const columnHelper = useMemo(
631
+ () => createColumnHelper<typeof clientFeatures, User>(),
632
+ [],
633
+ );
557
634
 
558
635
  const data = useMemo(() => [], []);
559
636
 
560
637
  const columns = useMemo(
561
- () => [
562
- columnHelper.display({
563
- id: "example",
564
- header: () => <DataTableHeader text="Example" />,
565
- cell: () => <DataTableCell>Example</DataTableCell>,
566
- }),
567
- ],
638
+ () =>
639
+ columnHelper.columns([
640
+ columnHelper.display({
641
+ id: "example",
642
+ header: () => <DataTableHeader text="Example" />,
643
+ cell: () => <DataTableCell>Example</DataTableCell>,
644
+ }),
645
+ ]),
568
646
  [columnHelper],
569
647
  );
570
648
 
571
- const table = useReactTable({
649
+ const table = useTable({
650
+ features: clientFeatures,
572
651
  data,
573
652
  columns,
574
653
  initialState: {
575
654
  globalFilter: "",
576
655
  },
577
- getCoreRowModel: getCoreRowModel(),
578
- getSortedRowModel: getSortedRowModel(),
579
- getFilteredRowModel: getFilteredRowModel(),
580
- getPaginationRowModel: getPaginationRowModel(),
581
656
  });
582
657
 
583
- const pagination = table.getState().pagination;
658
+ const pagination = table.state.pagination;
584
659
 
585
660
  return (
586
661
  <>
587
- <Filter<User>
662
+ <Filter
588
663
  onDownloadClick={() => alert("Download")}
589
664
  onSettingsClick={() => alert("Settings")}
590
665
  table={table}
@@ -611,77 +686,78 @@ export const WithSubColumns: StoryObj<typeof DataTable> = {
611
686
  }));
612
687
 
613
688
  const ParentComponent = () => {
614
- const columnHelper = useMemo(() => createColumnHelper<User>(), []);
689
+ const columnHelper = useMemo(
690
+ () => createColumnHelper<typeof clientFeatures, User>(),
691
+ [],
692
+ );
615
693
 
616
694
  const columns = useMemo(
617
- () => [
618
- columnHelper.group({
619
- id: "name",
620
- header: (group) => (
621
- <DataTableHeader
622
- text="Name group"
623
- colSpan={group.header.colSpan}
624
- />
625
- ),
626
- columns: [
627
- columnHelper.accessor("firstname", {
628
- id: "Firstname",
629
- header: () => <DataTableHeader text="Firstname" />,
630
- cell: (props) => (
631
- <DataTableCell>{props.getValue()}</DataTableCell>
632
- ),
633
- }),
634
- columnHelper.accessor("lastname", {
635
- id: "Lastname",
636
- header: () => <DataTableHeader text="Lastname" />,
637
- cell: (props) => (
638
- <DataTableCell>{props.getValue()}</DataTableCell>
639
- ),
640
- }),
641
- ],
642
- }),
643
- columnHelper.group({
644
- id: "other",
645
- header: (group) => (
646
- <DataTableHeader
647
- text="Other group"
648
- colSpan={group.header.colSpan}
649
- border="left"
650
- />
651
- ),
652
- columns: [
653
- columnHelper.accessor("email", {
654
- id: "Email",
655
- header: () => <DataTableHeader border="left" text="Email" />,
656
- cell: (props) => (
657
- <DataTableCell border="left">
658
- {props.getValue()}
659
- </DataTableCell>
660
- ),
661
- }),
662
- ],
663
- }),
664
- ],
695
+ () =>
696
+ columnHelper.columns([
697
+ columnHelper.group({
698
+ id: "name",
699
+ header: (group) => (
700
+ <DataTableHeader
701
+ text="Name group"
702
+ colSpan={group.header.colSpan}
703
+ />
704
+ ),
705
+ columns: columnHelper.columns([
706
+ columnHelper.accessor("firstname", {
707
+ id: "Firstname",
708
+ header: () => <DataTableHeader text="Firstname" />,
709
+ cell: (props) => (
710
+ <DataTableCell>{props.getValue()}</DataTableCell>
711
+ ),
712
+ }),
713
+ columnHelper.accessor("lastname", {
714
+ id: "Lastname",
715
+ header: () => <DataTableHeader text="Lastname" />,
716
+ cell: (props) => (
717
+ <DataTableCell>{props.getValue()}</DataTableCell>
718
+ ),
719
+ }),
720
+ ]),
721
+ }),
722
+ columnHelper.group({
723
+ id: "other",
724
+ header: (group) => (
725
+ <DataTableHeader
726
+ text="Other group"
727
+ colSpan={group.header.colSpan}
728
+ border="left"
729
+ />
730
+ ),
731
+ columns: columnHelper.columns([
732
+ columnHelper.accessor("email", {
733
+ id: "Email",
734
+ header: () => <DataTableHeader border="left" text="Email" />,
735
+ cell: (props) => (
736
+ <DataTableCell border="left">
737
+ {props.getValue()}
738
+ </DataTableCell>
739
+ ),
740
+ }),
741
+ ]),
742
+ }),
743
+ ]),
665
744
  [columnHelper],
666
745
  );
667
746
 
668
- const table = useReactTable({
747
+ const table = useTable({
748
+ features: clientFeatures,
669
749
  data,
670
750
  columns,
671
751
  initialState: {
672
752
  globalFilter: "",
673
753
  },
674
- getCoreRowModel: getCoreRowModel(),
675
- getSortedRowModel: getSortedRowModel(),
676
- getFilteredRowModel: getFilteredRowModel(),
677
- getPaginationRowModel: getPaginationRowModel(),
678
754
  });
679
755
 
680
- const pagination = table.getState().pagination;
756
+ const pagination = table.state.pagination;
681
757
 
682
758
  return (
683
759
  <>
684
- <Filter<User> table={table} />
760
+ <Filter table={table} />
685
761
  <DataTable table={table} pagination={pagination} />
686
762
  </>
687
763
  );
@@ -693,41 +769,46 @@ export const WithSubColumns: StoryObj<typeof DataTable> = {
693
769
 
694
770
  export const WithContentOverflow: StoryObj<typeof DataTable> = {
695
771
  render: () => {
696
- const data: Array<any> = arrayOfLength(300).map((index) => ({
772
+ type OverflowingUser = { firstname: string };
773
+
774
+ const data: Array<OverflowingUser> = arrayOfLength(300).map((index) => ({
697
775
  firstname: `Firstnameverylongthatwilloverflowonthescreensqhjdhqskdhkqshdkhqkdshhqsdhkqhshqhdskqjshdjhqsjkhdkqhskjdhqjkshdqkshdkhqsdkjqhdsjkhqsdk ${index + 1}`,
698
776
  }));
699
777
 
700
778
  const ParentComponent = () => {
701
- const columnHelper = useMemo(() => createColumnHelper<any>(), []);
779
+ const columnHelper = useMemo(
780
+ () => createColumnHelper<typeof clientFeatures, OverflowingUser>(),
781
+ [],
782
+ );
702
783
 
703
784
  const columns = useMemo(
704
- () => [
705
- columnHelper.accessor("firstname", {
706
- id: "Firstname",
707
- header: () => <DataTableHeader text="Firstname" />,
708
- cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
709
- }),
710
- ],
785
+ () =>
786
+ columnHelper.columns([
787
+ columnHelper.accessor("firstname", {
788
+ id: "Firstname",
789
+ header: () => <DataTableHeader text="Firstname" />,
790
+ cell: (props) => (
791
+ <DataTableCell>{props.getValue()}</DataTableCell>
792
+ ),
793
+ }),
794
+ ]),
711
795
  [columnHelper],
712
796
  );
713
797
 
714
- const table = useReactTable({
798
+ const table = useTable({
799
+ features: clientFeatures,
715
800
  data,
716
801
  columns,
717
802
  initialState: {
718
803
  globalFilter: "",
719
804
  },
720
- getCoreRowModel: getCoreRowModel(),
721
- getSortedRowModel: getSortedRowModel(),
722
- getFilteredRowModel: getFilteredRowModel(),
723
- getPaginationRowModel: getPaginationRowModel(),
724
805
  });
725
806
 
726
- const pagination = table.getState().pagination;
807
+ const pagination = table.state.pagination;
727
808
 
728
809
  return (
729
810
  <>
730
- <Filter<User> table={table} />
811
+ <Filter table={table} />
731
812
  <DataTable table={table} pagination={pagination} />
732
813
  </>
733
814
  );
@@ -749,7 +830,10 @@ export const WithContentOverflow: StoryObj<typeof DataTable> = {
749
830
  export const CustomExpandUi: StoryObj<typeof DataTable> = {
750
831
  render: () => {
751
832
  const ParentComponent = () => {
752
- const columnHelper = useMemo(() => createColumnHelper<User>(), []);
833
+ const columnHelper = useMemo(
834
+ () => createColumnHelper<typeof expandingFeatures, User>(),
835
+ [],
836
+ );
753
837
 
754
838
  // Fake data, should come from an API typically
755
839
  const data = useMemo(
@@ -761,35 +845,38 @@ export const CustomExpandUi: StoryObj<typeof DataTable> = {
761
845
  );
762
846
 
763
847
  const columns = useMemo(
764
- () => [
765
- columnHelper.accessor("firstname", {
766
- header: () => <DataTableHeader text="Firstname" />,
767
- cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
768
- }),
769
- columnHelper.display({
770
- id: "expander",
771
- header: () => <DataTableHeader text="" />,
772
- cell: ({ row }) => (
773
- <DataTableCell style={{ width: 72 }}>
774
- <Button
775
- mode="tertiary"
776
- iconLeft={row.getIsExpanded() ? "expand_less" : "expand_more"} // Show a different icon when the row is expanded
777
- onClick={row.getToggleExpandedHandler()} // Here you plug your button with react-table expanded handler
778
- />
779
- </DataTableCell>
780
- ),
781
- }),
782
- ],
848
+ () =>
849
+ columnHelper.columns([
850
+ columnHelper.accessor("firstname", {
851
+ header: () => <DataTableHeader text="Firstname" />,
852
+ cell: (props) => (
853
+ <DataTableCell>{props.getValue()}</DataTableCell>
854
+ ),
855
+ }),
856
+ columnHelper.display({
857
+ id: "expander",
858
+ header: () => <DataTableHeader text="" />,
859
+ cell: ({ row }) => (
860
+ <DataTableCell style={{ width: 72 }}>
861
+ <Button
862
+ mode="tertiary"
863
+ iconLeft={
864
+ row.getIsExpanded() ? "expand_less" : "expand_more"
865
+ } // Show a different icon when the row is expanded
866
+ onClick={row.getToggleExpandedHandler()} // Here you plug your button with react-table expanded handler
867
+ />
868
+ </DataTableCell>
869
+ ),
870
+ }),
871
+ ]),
783
872
  [columnHelper],
784
873
  );
785
874
 
786
- const table = useReactTable({
875
+ const table = useTable({
876
+ features: expandingFeatures,
787
877
  data,
788
878
  columns,
789
879
  getRowCanExpand: () => true, // tell the table that all rows can be expanded
790
- getCoreRowModel: getCoreRowModel(),
791
- getSortedRowModel: getSortedRowModel(),
792
- getExpandedRowModel: getExpandedRowModel(), // from @tanstack/react-table
793
880
  });
794
881
 
795
882
  return (
@@ -817,7 +904,8 @@ export const ExpandingSubRows: StoryObj<typeof DataTable> = {
817
904
 
818
905
  const ParentComponent = () => {
819
906
  const columnHelper = useMemo(
820
- () => createColumnHelper<UserWithAffiliates>(),
907
+ () =>
908
+ createColumnHelper<typeof expandingFeatures, UserWithAffiliates>(),
821
909
  [],
822
910
  );
823
911
 
@@ -863,45 +951,48 @@ export const ExpandingSubRows: StoryObj<typeof DataTable> = {
863
951
  );
864
952
 
865
953
  const columns = useMemo(
866
- () => [
867
- columnHelper.accessor("firstname", {
868
- header: () => <DataTableHeader text="Firstname" />,
869
- cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
870
- }),
871
- columnHelper.accessor("lastname", {
872
- id: "Lastname",
873
- header: () => <DataTableHeader text="Lastname" />,
874
- cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
875
- }),
876
- columnHelper.display({
877
- id: "expander",
878
- header: () => <DataTableHeader text="" />,
879
- cell: ({ row }) => (
880
- <DataTableCell style={{ width: 72 }}>
881
- {row.getCanExpand() && (
882
- <Button
883
- mode="tertiary"
884
- iconLeft={
885
- // Show a different icon when the row is expanded
886
- row.getIsExpanded() ? "expand_less" : "expand_more"
887
- }
888
- onClick={row.getToggleExpandedHandler()} // Here you plug your button with react-table expanded handler
889
- />
890
- )}
891
- </DataTableCell>
892
- ),
893
- }),
894
- ],
954
+ () =>
955
+ columnHelper.columns([
956
+ columnHelper.accessor("firstname", {
957
+ header: () => <DataTableHeader text="Firstname" />,
958
+ cell: (props) => (
959
+ <DataTableCell>{props.getValue()}</DataTableCell>
960
+ ),
961
+ }),
962
+ columnHelper.accessor("lastname", {
963
+ id: "Lastname",
964
+ header: () => <DataTableHeader text="Lastname" />,
965
+ cell: (props) => (
966
+ <DataTableCell>{props.getValue()}</DataTableCell>
967
+ ),
968
+ }),
969
+ columnHelper.display({
970
+ id: "expander",
971
+ header: () => <DataTableHeader text="" />,
972
+ cell: ({ row }) => (
973
+ <DataTableCell style={{ width: 72 }}>
974
+ {row.getCanExpand() && (
975
+ <Button
976
+ mode="tertiary"
977
+ iconLeft={
978
+ // Show a different icon when the row is expanded
979
+ row.getIsExpanded() ? "expand_less" : "expand_more"
980
+ }
981
+ onClick={row.getToggleExpandedHandler()} // Here you plug your button with react-table expanded handler
982
+ />
983
+ )}
984
+ </DataTableCell>
985
+ ),
986
+ }),
987
+ ]),
895
988
  [columnHelper],
896
989
  );
897
990
 
898
- const table = useReactTable({
991
+ const table = useTable({
992
+ features: expandingFeatures,
899
993
  data,
900
994
  columns,
901
995
  getSubRows: (row) => row.affiliates,
902
- getCoreRowModel: getCoreRowModel(),
903
- getSortedRowModel: getSortedRowModel(),
904
- getExpandedRowModel: getExpandedRowModel(), // from @tanstack/react-table
905
996
  });
906
997
 
907
998
  return <DataTable table={table} />;
@@ -955,23 +1046,33 @@ export const Sizes: StoryObj<typeof DataTable> = {
955
1046
  [],
956
1047
  );
957
1048
 
958
- const columnHelper = useMemo(() => createColumnHelper<User>(), []);
1049
+ const columnHelper = useMemo(
1050
+ () => createColumnHelper<typeof coreOnlyFeatures, User>(),
1051
+ [],
1052
+ );
959
1053
 
960
1054
  const columns = useMemo(
961
- () => [
962
- columnHelper.accessor("firstname", {
963
- header: () => <DataTableHeader text="Firstname" />,
964
- cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
965
- }),
966
- columnHelper.accessor("lastname", {
967
- header: () => <DataTableHeader text="Lastname" />,
968
- cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
969
- }),
970
- columnHelper.accessor("email", {
971
- header: () => <DataTableHeader text="Email" />,
972
- cell: (props) => <DataTableCell>{props.getValue()}</DataTableCell>,
973
- }),
974
- ],
1055
+ () =>
1056
+ columnHelper.columns([
1057
+ columnHelper.accessor("firstname", {
1058
+ header: () => <DataTableHeader text="Firstname" />,
1059
+ cell: (props) => (
1060
+ <DataTableCell>{props.getValue()}</DataTableCell>
1061
+ ),
1062
+ }),
1063
+ columnHelper.accessor("lastname", {
1064
+ header: () => <DataTableHeader text="Lastname" />,
1065
+ cell: (props) => (
1066
+ <DataTableCell>{props.getValue()}</DataTableCell>
1067
+ ),
1068
+ }),
1069
+ columnHelper.accessor("email", {
1070
+ header: () => <DataTableHeader text="Email" />,
1071
+ cell: (props) => (
1072
+ <DataTableCell>{props.getValue()}</DataTableCell>
1073
+ ),
1074
+ }),
1075
+ ]),
975
1076
  [columnHelper],
976
1077
  );
977
1078
 
@@ -980,10 +1081,10 @@ export const Sizes: StoryObj<typeof DataTable> = {
980
1081
  }: {
981
1082
  size: "small" | "default" | "large";
982
1083
  }) => {
983
- const table = useReactTable({
1084
+ const table = useTable({
1085
+ features: coreOnlyFeatures,
984
1086
  data,
985
1087
  columns,
986
- getCoreRowModel: getCoreRowModel(),
987
1088
  });
988
1089
 
989
1090
  return (
@@ -1068,6 +1169,11 @@ Should it be from a too intensive load for the database, a payload so big that t
1068
1169
 
1069
1170
  Below are two examples of how to integrate the `DataTable` component with `react-table` in both cases.
1070
1171
 
1172
+ Since `react-table` v9, a table only exposes the APIs of the features it registers
1173
+ through `tableFeatures()`. Declare that set once outside of your component, then type
1174
+ your column helper with it (`createColumnHelper<typeof features, TData>()`) and pass it
1175
+ to `useTable`. Both examples below show the pattern.
1176
+
1071
1177
  ### Front-end managed
1072
1178
 
1073
1179
  When the data is small, it is better to handle the data manipulation on the front-end.