@drivenets/design-system 0.12.1 → 0.14.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 (122) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/index.cjs +1989 -735
  3. package/dist/index.d.cts +1378 -998
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +1378 -998
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1978 -735
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +43 -41
  11. package/src/components/ds-button-v3/ds-button-v3.figma.ts +99 -0
  12. package/src/components/ds-button-v3/ds-button-v3.module.scss +22 -22
  13. package/src/components/ds-card/figma/ds-card-content.figma.ts +15 -0
  14. package/src/components/ds-card/figma/ds-card-footer.figma.ts +15 -0
  15. package/src/components/ds-card/figma/ds-card-header.figma.ts +15 -0
  16. package/src/components/ds-card/figma/ds-card.figma.ts +72 -0
  17. package/src/components/ds-date-input/ds-date-input.utils.ts +2 -2
  18. package/src/components/ds-date-picker/ds-date-picker.figma.ts +19 -0
  19. package/src/components/ds-drawer/ds-drawer.tsx +2 -0
  20. package/src/components/ds-form-control/ds-form-control.figma.ts +76 -0
  21. package/src/components/ds-form-control/ds-form-control.tsx +14 -11
  22. package/src/components/ds-form-control/ds-form-control.types.ts +4 -0
  23. package/src/components/ds-grid/ds-grid.tsx +1 -2
  24. package/src/components/ds-icon/figma/ds-icon-material.figma.batch.ts +10 -0
  25. package/src/components/ds-icon/figma/ds-icon-special.figma.batch.ts +18 -0
  26. package/src/components/ds-icon/figma/ds-icon.figma.batch.json +2873 -0
  27. package/src/components/ds-modal/ds-modal.tsx +3 -0
  28. package/src/components/ds-modal/figma/ds-modal-content.figma.ts +18 -0
  29. package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +19 -0
  30. package/src/components/ds-modal/figma/ds-modal-header.figma.ts +21 -0
  31. package/src/components/ds-modal/figma/ds-modal.figma.ts +36 -0
  32. package/src/components/ds-popover/ds-popover.module.scss +58 -16
  33. package/src/components/ds-popover/ds-popover.tsx +148 -24
  34. package/src/components/ds-popover/ds-popover.types.ts +95 -12
  35. package/src/components/ds-popover/index.ts +1 -1
  36. package/src/components/ds-select/ds-select.figma.ts +53 -0
  37. package/src/components/ds-split-button/ds-split-button.figma.ts +95 -0
  38. package/src/components/ds-status-badge-v2/ds-status-badge-v2.figma.ts +41 -0
  39. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.module.scss +21 -2
  40. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +22 -14
  41. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.types.ts +1 -6
  42. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +2 -2
  43. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.module.scss +2 -0
  44. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.tsx +42 -11
  45. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +31 -0
  46. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +48 -0
  47. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.types.ts +12 -0
  48. package/src/components/ds-table/components/ds-table-group-header-cell/index.ts +2 -0
  49. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +28 -4
  50. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +65 -28
  51. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +26 -0
  52. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +3 -0
  53. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +23 -0
  54. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +4 -1
  55. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.types.ts +1 -1
  56. package/src/components/ds-table/components/edit/cell-editors/cell-editors.module.scss +3 -0
  57. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-checkbox.tsx +27 -0
  58. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-date.tsx +50 -0
  59. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-number.tsx +51 -0
  60. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-select.tsx +36 -0
  61. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-text.tsx +50 -0
  62. package/src/components/ds-table/components/edit/cell-editors/index.ts +14 -0
  63. package/src/components/ds-table/components/edit/cell-editors/table-edit-form-control.tsx +31 -0
  64. package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.module.scss +28 -0
  65. package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx +39 -0
  66. package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.types.ts +7 -0
  67. package/src/components/ds-table/components/edit/ds-table-editable-cell/index.ts +2 -0
  68. package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.module.scss +48 -0
  69. package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.tsx +76 -0
  70. package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.types.ts +5 -0
  71. package/src/components/ds-table/components/edit/ds-table-editing-cell/index.ts +2 -0
  72. package/src/components/ds-table/context/ds-table-context.ts +83 -1
  73. package/src/components/ds-table/ds-table-column.types.ts +12 -0
  74. package/src/components/ds-table/ds-table.module.scss +100 -10
  75. package/src/components/ds-table/ds-table.tsx +38 -21
  76. package/src/components/ds-table/ds-table.types.ts +107 -0
  77. package/src/components/ds-table/figma/ds-table-expandable.figma.ts +17 -0
  78. package/src/components/ds-table/figma/ds-table.figma.ts +16 -0
  79. package/src/components/ds-table/filters/adapters/create-checkbox-filter-adapter.tsx +24 -40
  80. package/src/components/ds-table/filters/adapters/create-dual-range-filter-adapter.tsx +10 -31
  81. package/src/components/ds-table/filters/adapters/create-filter-adapter.ts +6 -98
  82. package/src/components/ds-table/filters/components/checkbox-filter/checkbox-filter.module.scss +36 -0
  83. package/src/components/ds-table/filters/components/checkbox-filter/checkbox-filter.tsx +92 -13
  84. package/src/components/ds-table/filters/components/checkbox-filter/index.ts +1 -1
  85. package/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.module.scss +42 -0
  86. package/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.tsx +89 -0
  87. package/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.types.ts +25 -0
  88. package/src/components/ds-table/filters/components/column-filter-popover/index.ts +5 -0
  89. package/src/components/ds-table/filters/hooks/use-table-filters.ts +113 -78
  90. package/src/components/ds-table/filters/index.ts +2 -44
  91. package/src/components/ds-table/filters/types/column-meta.augmentation.ts +19 -0
  92. package/src/components/ds-table/filters/types/filter-adapter.types.ts +48 -81
  93. package/src/components/ds-table/grouping/hooks/use-column-groups.ts +73 -0
  94. package/src/components/ds-table/grouping/index.ts +5 -0
  95. package/src/components/ds-table/grouping/types/column-group-meta.augmentation.ts +26 -0
  96. package/src/components/ds-table/grouping/types/column-group.types.ts +35 -0
  97. package/src/components/ds-table/grouping/utils/column-group.ts +119 -0
  98. package/src/components/ds-table/hooks/use-cell-editor.ts +81 -0
  99. package/src/components/ds-table/index.ts +32 -0
  100. package/src/components/ds-table/styles/_variables.scss +1 -0
  101. package/src/components/ds-table/utils/column-size.ts +9 -8
  102. package/src/components/ds-tag/ds-tag.module.scss +22 -1
  103. package/src/components/ds-tag/ds-tag.tsx +18 -5
  104. package/src/components/ds-tag/ds-tag.types.ts +22 -8
  105. package/src/components/ds-tag/ds-tag.unit.test.ts +32 -0
  106. package/src/components/ds-tag-filter/ds-tag-filter.tsx +14 -22
  107. package/src/components/ds-text-input/ds-text-input.figma.ts +60 -0
  108. package/src/components/ds-time-picker/ds-time-picker.figma.ts +18 -0
  109. package/src/components/ds-workspace-layout/ds-workspace-layout.context.ts +19 -0
  110. package/src/components/ds-workspace-layout/ds-workspace-layout.module.scss +166 -0
  111. package/src/components/ds-workspace-layout/ds-workspace-layout.tsx +135 -0
  112. package/src/components/ds-workspace-layout/ds-workspace-layout.types.ts +52 -0
  113. package/src/components/ds-workspace-layout/index.ts +11 -0
  114. package/src/index.ts +1 -1
  115. package/src/styles/_root_new.scss +2 -0
  116. package/src/styles/_scrollbars.scss +36 -22
  117. package/src/utils/restore-document-body-modal-styles.ts +44 -0
  118. package/src/utils/use-ark-dialog-body-lock-cleanup.ts +36 -0
  119. package/src/components/ds-workspace/ds-workspace.module.scss +0 -55
  120. package/src/components/ds-workspace/ds-workspace.tsx +0 -37
  121. package/src/components/ds-workspace/ds-workspace.types.ts +0 -17
  122. package/src/components/ds-workspace/index.ts +0 -8
@@ -1,5 +1,5 @@
1
1
  @use '../../styles/typography';
2
- @use '../../styles/utilities' as utils;
2
+ @use '../../styles/scrollbars' as scrollbars;
3
3
  @use './styles/variables' as vars;
4
4
 
5
5
  :root {
@@ -9,9 +9,10 @@
9
9
  --ds-table-header-height: 45px;
10
10
  }
11
11
 
12
- $bulk-actions-height: 60px;
12
+ $bulk-actions-reserved-space: 80px;
13
13
 
14
14
  .container {
15
+ position: relative;
15
16
  border-radius: vars.$border-radius;
16
17
  border-width: vars.$border-width;
17
18
  border-style: solid;
@@ -21,7 +22,51 @@ $bulk-actions-height: 60px;
21
22
 
22
23
  .dataTableContainer {
23
24
  max-height: 100%;
24
- overflow: auto;
25
+ overflow-x: auto;
26
+ overflow-y: hidden;
27
+ display: flex;
28
+ flex-direction: column;
29
+
30
+ @include scrollbars.scrollbar-on-hover;
31
+
32
+ > table {
33
+ display: flex;
34
+ flex-direction: column;
35
+ flex: 1;
36
+ min-height: 0;
37
+ width: max-content;
38
+ min-width: 100%;
39
+ }
40
+
41
+ thead {
42
+ display: block;
43
+ flex-shrink: 0;
44
+ overflow-y: scroll;
45
+ scrollbar-gutter: stable;
46
+
47
+ @include scrollbars.scrollbar-on-hover;
48
+ }
49
+
50
+ tbody {
51
+ display: block;
52
+ overflow-y: auto;
53
+ flex: 1;
54
+ min-height: 0;
55
+ scrollbar-gutter: stable;
56
+
57
+ @include scrollbars.scrollbar-on-hover;
58
+ }
59
+
60
+ thead tr,
61
+ tbody tr {
62
+ display: flex;
63
+ }
64
+
65
+ thead th,
66
+ tbody td {
67
+ display: flex;
68
+ align-items: center;
69
+ }
25
70
  }
26
71
 
27
72
  .table {
@@ -40,21 +85,66 @@ $bulk-actions-height: 60px;
40
85
  width: 100%;
41
86
  }
42
87
 
88
+ .bulkActionsVisible {
89
+ tbody {
90
+ padding-bottom: $bulk-actions-reserved-space;
91
+ }
92
+ }
93
+
43
94
  .emptyState {
95
+ // colSpan in markup isn't honored by flex row layout, so the cell stretches
96
+ // across the row; stretch the inner content so it occupies full table width
97
+ flex: 1;
98
+ min-width: 0;
44
99
  height: vars.$empty-state-height;
100
+ justify-content: center;
45
101
  text-align: center;
46
- }
47
102
 
48
- .virtualized {
49
- display: grid;
103
+ > * {
104
+ flex: 1;
105
+ }
50
106
  }
51
107
 
52
108
  .virtualizedContainer {
53
109
  position: relative;
54
110
  height: vars.$virtualized-height;
55
- overflow: auto;
56
- }
111
+ overflow-x: auto;
112
+ overflow-y: hidden;
113
+ display: flex;
114
+ flex-direction: column;
57
115
 
58
- .bulkActionsVisible {
59
- padding-bottom: $bulk-actions-height;
116
+ @include scrollbars.scrollbar-on-hover;
117
+
118
+ > table {
119
+ display: flex;
120
+ flex-direction: column;
121
+ flex: 1;
122
+ min-height: 0;
123
+ width: max-content;
124
+ min-width: 100%;
125
+ }
126
+
127
+ thead {
128
+ display: block;
129
+ flex-shrink: 0;
130
+ overflow-y: scroll;
131
+ scrollbar-gutter: stable;
132
+
133
+ @include scrollbars.scrollbar-on-hover;
134
+ }
135
+
136
+ tbody {
137
+ flex: 1;
138
+ min-height: 0;
139
+ scrollbar-gutter: stable;
140
+ }
141
+
142
+ thead tr {
143
+ display: flex;
144
+ }
145
+
146
+ thead th {
147
+ display: flex;
148
+ align-items: center;
149
+ }
60
150
  }
@@ -23,8 +23,9 @@ import { DsTableRowExpandableCell } from './components/ds-table-row-expandable-c
23
23
  import { DsTableRowSelectableCell } from './components/ds-table-row-selectable-cell';
24
24
  import { DsTableHeaderSelectableCell } from './components/ds-table-header-selectable-cell';
25
25
  import { useDragAndDrop } from './hooks/use-drag-and-drop';
26
- import { type DsTableContextType, DsTableContext } from './context/ds-table-context';
26
+ import { type DsTableContextType, DsTableContext, useEditingState } from './context/ds-table-context';
27
27
  import { DsTableBodyVirtualized } from './components/ds-table-body-virtualized';
28
+ import { useColumnGroups } from './grouping';
28
29
  import {
29
30
  EMPTY_TABLE_STATE_TEXT,
30
31
  EXPANDER_COLUMN_ID,
@@ -75,8 +76,13 @@ const DsTable = <TData extends { id: string }, TValue>({
75
76
  onColumnFiltersChange,
76
77
  columnVisibility: externalColumnVisibility,
77
78
  onColumnVisibilityChange,
79
+ collapsedColumnGroups: externalCollapsedColumnGroups,
80
+ onCollapsedColumnGroupsChange,
81
+ locale,
78
82
  activeRowId,
79
83
  infiniteScroll,
84
+ onCellEdit,
85
+ onCellValidate,
80
86
  }: DsDataTableProps<TData, TValue>) => {
81
87
  const [data, setData] = React.useState(tableData);
82
88
  const [sorting, setSorting] = React.useState<SortingState>([]);
@@ -107,12 +113,12 @@ const DsTable = <TData extends { id: string }, TValue>({
107
113
  }
108
114
  };
109
115
 
110
- const columnVisibility = externalColumnVisibility ?? internalColumnVisibility;
116
+ const baseColumnVisibility = externalColumnVisibility ?? internalColumnVisibility;
111
117
  const handleColumnVisibilityChange = (
112
118
  updaterOrValue: VisibilityState | ((old: VisibilityState) => VisibilityState),
113
119
  ) => {
114
120
  const newVisibility =
115
- typeof updaterOrValue === 'function' ? updaterOrValue(columnVisibility) : updaterOrValue;
121
+ typeof updaterOrValue === 'function' ? updaterOrValue(baseColumnVisibility) : updaterOrValue;
116
122
 
117
123
  if (onColumnVisibilityChange) {
118
124
  onColumnVisibilityChange(newVisibility);
@@ -121,6 +127,17 @@ const DsTable = <TData extends { id: string }, TValue>({
121
127
  }
122
128
  };
123
129
 
130
+ const { collapsedColumnGroups, toggleColumnGroup, collapsedVisibility } = useColumnGroups({
131
+ columns: columnsProp,
132
+ collapsedColumnGroups: externalCollapsedColumnGroups,
133
+ onCollapsedColumnGroupsChange,
134
+ });
135
+
136
+ const columnVisibility = useMemo(
137
+ () => ({ ...baseColumnVisibility, ...collapsedVisibility }),
138
+ [baseColumnVisibility, collapsedVisibility],
139
+ );
140
+
124
141
  const handleSortingChange = (updaterOrValue: SortingState | ((old: SortingState) => SortingState)) => {
125
142
  const newSorting = typeof updaterOrValue === 'function' ? updaterOrValue(sorting) : updaterOrValue;
126
143
  setSorting(newSorting);
@@ -267,6 +284,8 @@ const DsTable = <TData extends { id: string }, TValue>({
267
284
 
268
285
  const isBulkActionsVisible = selectable && actions.length > 0 && selectedRows.length > 0;
269
286
 
287
+ const editingState = useEditingState<TData, TValue>(onCellEdit, onCellValidate);
288
+
270
289
  const contextValue: DsTableContextType<TData, TValue> = {
271
290
  stickyHeader,
272
291
  bordered,
@@ -283,6 +302,11 @@ const DsTable = <TData extends { id: string }, TValue>({
283
302
  renderExpandedRow,
284
303
  virtualized,
285
304
  activeRowId,
305
+ collapsedColumnGroups,
306
+ onToggleColumnGroup: toggleColumnGroup,
307
+ locale,
308
+ onCellEdit,
309
+ ...editingState,
286
310
  };
287
311
 
288
312
  return (
@@ -298,18 +322,11 @@ const DsTable = <TData extends { id: string }, TValue>({
298
322
  )}
299
323
  >
300
324
  <DragWrapper>
301
- <Table
302
- className={classnames(
303
- fullWidth && styles.fullWidth,
304
- !bordered && styles.tableNoBorder,
305
- virtualized && styles.virtualized,
306
- )}
307
- >
325
+ <Table className={classnames(fullWidth && styles.fullWidth, !bordered && styles.tableNoBorder)}>
308
326
  <DsTableHeader table={table} />
309
327
  {virtualized ? (
310
328
  <DsTableBodyVirtualized
311
329
  table={table}
312
- tableContainerRef={tableContainerRef}
313
330
  emptyState={emptyState}
314
331
  estimateSize={virtualizedOptions?.estimateSize || ROW_SIZE_HEIGHT_MAP[rowSize]}
315
332
  overscan={virtualizedOptions?.overscan}
@@ -330,17 +347,17 @@ const DsTable = <TData extends { id: string }, TValue>({
330
347
  )}
331
348
  </Table>
332
349
  </DragWrapper>
350
+ {selectable && actions.length > 0 && (
351
+ <DsTableBulkActions
352
+ numSelectedRows={selectedRows.length}
353
+ actions={actions.map((action) => ({
354
+ ...action,
355
+ onClick: () => action.onClick(selectedRows),
356
+ }))}
357
+ onClearSelection={table.resetRowSelection}
358
+ />
359
+ )}
333
360
  </div>
334
- {selectable && actions.length > 0 && (
335
- <DsTableBulkActions
336
- numSelectedRows={selectedRows.length}
337
- actions={actions.map((action) => ({
338
- ...action,
339
- onClick: () => action.onClick(selectedRows),
340
- }))}
341
- onClearSelection={table.resetRowSelection}
342
- />
343
- )}
344
361
  </DsTableContext.Provider>
345
362
  );
346
363
  };
@@ -1,4 +1,5 @@
1
1
  import type React from 'react';
2
+ import './ds-table-column.types';
2
3
  import type {
3
4
  ColumnDef,
4
5
  ColumnFiltersState,
@@ -15,6 +16,31 @@ import type { InfiniteScrollConfig, ScrollParams } from './components/ds-table-b
15
16
  */
16
17
  export type DsTableRowSize = 'small' | 'medium' | 'large';
17
18
 
19
+ /**
20
+ * Overridable, user-facing strings used by the table (e.g. accessible labels for
21
+ * the column-group collapse toggle). Pass a partial object via the `locale` prop
22
+ * to localize.
23
+ */
24
+ export interface DsTableLocale {
25
+ /**
26
+ * Accessible label for the toggle that collapses a column group.
27
+ */
28
+ collapseColumnGroup: string;
29
+
30
+ /**
31
+ * Accessible label for the toggle that expands a column group.
32
+ */
33
+ expandColumnGroup: string;
34
+ }
35
+
36
+ /**
37
+ * Default English strings for {@link DsTableLocale}.
38
+ */
39
+ export const defaultDsTableLocale: DsTableLocale = Object.freeze({
40
+ collapseColumnGroup: 'Collapse column group',
41
+ expandColumnGroup: 'Expand column group',
42
+ });
43
+
18
44
  /**
19
45
  * API for programmatically controlling a DsTable component.
20
46
  * This interface provides methods to interact with the table's selection, sorting, filtering, and pagination features.
@@ -440,4 +466,85 @@ export interface DsDataTableProps<TData, TValue> {
440
466
  * Callback when column visibility changes
441
467
  */
442
468
  onColumnVisibilityChange?: (visibility: VisibilityState) => void;
469
+
470
+ /**
471
+ * Controlled list of collapsed column-group ids. A group is collapsible when its
472
+ * column def sets `meta.group.collapsible`; collapsing hides every leaf column in
473
+ * the group except those marked `meta.keepVisibleWhenCollapsed`. Omit for
474
+ * uncontrolled behavior (seeded from `meta.group.defaultCollapsed`).
475
+ *
476
+ * @example
477
+ * ```tsx
478
+ * const [collapsed, setCollapsed] = useState<string[]>(['equipment']);
479
+ *
480
+ * <DsTable
481
+ * collapsedColumnGroups={collapsed}
482
+ * onCollapsedColumnGroupsChange={setCollapsed}
483
+ * />
484
+ * ```
485
+ */
486
+ collapsedColumnGroups?: string[];
487
+
488
+ /**
489
+ * Callback fired with the next list of collapsed group ids when a group's
490
+ * collapse toggle is clicked.
491
+ */
492
+ onCollapsedColumnGroupsChange?: (collapsedGroupIds: string[]) => void;
493
+
494
+ /**
495
+ * Overrides for user-facing strings (e.g. column-group toggle labels). Merged
496
+ * over the built-in English defaults.
497
+ */
498
+ locale?: Partial<DsTableLocale>;
499
+
500
+ /**
501
+ * Callback fired when an editable cell commits a new value via one of the
502
+ * `DsTableEditCell*` wrappers or the `useCellEditor` hook. The parent is
503
+ * expected to update the row data in response.
504
+ *
505
+ * Commits occur on confirm (check button or Enter). Opening another cell
506
+ * discards the previous draft without calling this callback.
507
+ *
508
+ * @param row - The row data of the cell that was edited
509
+ * @param columnId - The id of the column whose cell was edited
510
+ * @param value - The new committed value
511
+ *
512
+ * @example
513
+ * ```tsx
514
+ * const [people, setPeople] = useState(initialPeople);
515
+ *
516
+ * <DsTable
517
+ * data={people}
518
+ * columns={columns}
519
+ * onCellEdit={(row, columnId, value) => {
520
+ * setPeople((rows) =>
521
+ * rows.map((r) => (r.id === row.id ? { ...r, [columnId]: value } : r)),
522
+ * );
523
+ * }}
524
+ * />
525
+ * ```
526
+ */
527
+ onCellEdit?: (row: TData, columnId: string, value: TValue) => void;
528
+
529
+ /**
530
+ * Validates a cell value before it is committed. Return `null` to allow the
531
+ * commit (which fires `onCellEdit`), or an error message string to reject it
532
+ * and keep the cell in edit mode. Synchronous only.
533
+ *
534
+ *
535
+ * @param row - The row data of the cell being edited
536
+ * @param columnId - The id of the column whose cell is being edited
537
+ * @param value - The candidate value to validate
538
+ *
539
+ * @example
540
+ * ```tsx
541
+ * <DsTable
542
+ * data={people}
543
+ * columns={columns}
544
+ * onCellValidate={(row, columnId, value) => validateField(columnId)(value, row)}
545
+ * onCellEdit={...}
546
+ * />
547
+ * ```
548
+ */
549
+ onCellValidate?: (row: TData, columnId: string, value: TValue) => string | null;
443
550
  }
@@ -0,0 +1,17 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=36535-44213
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-table
3
+ // component=DsTable
4
+ //
5
+ // `DAP_Table Expandable_v03` is the expandable table on the "Table New (Data grid)"
6
+ // page. It maps to the same `DsTable` code component as the simple table, with the
7
+ // `expandable` prop enabled and a `renderExpandedRow` callback for the nested content.
8
+ // The Simple vs Expandable split exists only because of a Figma restriction (a
9
+ // component can't contain an instance of itself); in code it's one `DsTable`.
10
+ import figma from 'figma';
11
+
12
+ export default {
13
+ example: figma.code`<DsTable columns={columns} data={data} expandable renderExpandedRow={(row) => null} />`,
14
+ imports: ["import { DsTable } from '@drivenets/design-system'"],
15
+ id: 'ds-table-expandable',
16
+ metadata: { nestable: false },
17
+ };
@@ -0,0 +1,16 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=36354-1965
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-table
3
+ // component=DsTable
4
+ //
5
+ // `DAP_Table Simple_v03` is the non-expandable table on the "Table New (Data grid)"
6
+ // page. Figma models the table as separate components (header/row/cell),
7
+ // while code exposes a single declarative `DsTable` driven by `columns`/`data`,
8
+ // so the snippet is a representative usage that references those consts.
9
+ import figma from 'figma';
10
+
11
+ export default {
12
+ example: figma.code`<DsTable columns={columns} data={data} />`,
13
+ imports: ["import { DsTable } from '@drivenets/design-system'"],
14
+ id: 'ds-table',
15
+ metadata: { nestable: false },
16
+ };
@@ -1,54 +1,36 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { Row } from '@tanstack/react-table';
3
3
  import type { FilterAdapter } from '../types/filter-adapter.types';
4
- import { type CheckboxFilterItem, CheckboxFilter } from '../components/checkbox-filter';
4
+ import {
5
+ type CheckboxFilterItem,
6
+ type CheckboxFilterLocale,
7
+ CheckboxFilter,
8
+ } from '../components/checkbox-filter';
5
9
  import { createFilterAdapter } from './create-filter-adapter';
6
10
 
7
11
  export interface CheckboxFilterAdapterConfig<TData, TValue> {
8
- /**
9
- * Unique identifier (should match column accessorKey)
10
- */
12
+ /** Unique id, should match the column `accessorKey`. */
11
13
  id: string;
12
-
13
- /**
14
- * Display label for filter navigation
15
- */
16
14
  label: string;
17
-
18
- /**
19
- * Available items to select from
20
- */
15
+ /** Selectable items. */
21
16
  items: CheckboxFilterItem<TValue>[];
22
-
23
- /**
24
- * Optional custom renderer for each item
25
- */
17
+ /** Custom row renderer; defaults to `item.label`. */
26
18
  renderer?: (item: CheckboxFilterItem<TValue>) => ReactNode;
27
-
28
- /**
29
- * Optional custom chip label generator
30
- * @default (item) => `${label}: ${item.label}`
31
- */
19
+ /** @default (item) => `${label}: ${item.label}` */
32
20
  chipLabelTemplate?: (item: CheckboxFilterItem<TValue>) => string;
33
-
34
- /**
35
- * Optional custom cell renderer for table column
36
- */
37
21
  cellRenderer?: (value: TValue) => ReactNode;
38
-
39
- /**
40
- * How to extract the value from a row for comparison
41
- * @default (row) => row.getValue(id)
42
- */
22
+ /** @default (row) => row.getValue(id) */
43
23
  getRowValue?: (row: Row<TData>) => TValue;
24
+ /** Show a search input above the list. @default false */
25
+ searchable?: boolean;
26
+ /** Show an "All" toggle that selects / clears every (visible) item. @default false */
27
+ selectAll?: boolean;
28
+ locale?: CheckboxFilterLocale;
44
29
  }
45
30
 
46
31
  /**
47
- * Factory function to create a checkbox filter adapter
48
- *
49
- * Handles multi-select checkbox filtering with inclusion model:
50
- * - Empty selection = show all data (no filter active)
51
- * - Selected items = show only those items
32
+ * Multi-select inclusion filter: empty selection shows every row, selected
33
+ * items show only those rows.
52
34
  */
53
35
  export function createCheckboxFilterAdapter<TData, TValue = string>(
54
36
  config: CheckboxFilterAdapterConfig<TData, TValue>,
@@ -61,20 +43,21 @@ export function createCheckboxFilterAdapter<TData, TValue = string>(
61
43
  chipLabelTemplate = (item) => `${label}: ${item.label}`,
62
44
  cellRenderer,
63
45
  getRowValue = (row) => row.getValue(id),
46
+ searchable = false,
47
+ selectAll = false,
48
+ locale,
64
49
  } = config;
65
50
 
66
51
  return createFilterAdapter<TData, CheckboxFilterItem<TValue>[], TValue>({
67
52
  id,
68
53
  label,
69
- initialValue: [], // Start with nothing selected (show all)
54
+ initialValue: [],
70
55
 
71
56
  filterFn: (row, columnId, filterValue) => {
72
- // Empty selection = no filter = show all
73
57
  if (filterValue.length === 0) {
74
58
  return true;
75
59
  }
76
60
 
77
- // Otherwise, show only selected items
78
61
  const rowValue = getRowValue(row);
79
62
  return filterValue.some((item) => item.value === rowValue);
80
63
  },
@@ -82,7 +65,6 @@ export function createCheckboxFilterAdapter<TData, TValue = string>(
82
65
  cellRenderer,
83
66
 
84
67
  toChips: (selectedItems) => {
85
- // Generate chips for all selected items
86
68
  return selectedItems.map((item) => ({
87
69
  id: `${id}_${String(item.value)}`,
88
70
  label: chipLabelTemplate(item),
@@ -98,7 +80,6 @@ export function createCheckboxFilterAdapter<TData, TValue = string>(
98
80
  },
99
81
 
100
82
  getActiveFiltersCount: (selectedItems) => {
101
- // Count = number of selected items (0 means none selected)
102
83
  return selectedItems.length;
103
84
  },
104
85
 
@@ -109,6 +90,9 @@ export function createCheckboxFilterAdapter<TData, TValue = string>(
109
90
  renderer={renderer}
110
91
  selectedItems={value}
111
92
  onSelectionChange={onChange}
93
+ searchable={searchable}
94
+ selectAll={selectAll}
95
+ locale={locale}
112
96
  />
113
97
  );
114
98
  },
@@ -1,5 +1,5 @@
1
1
  import type { Row } from '@tanstack/react-table';
2
- import type { ChipItem } from '../../../ds-chip-group';
2
+ import type { TagFilterItem } from '../../../ds-tag-filter';
3
3
  import type { FilterAdapter } from '../types/filter-adapter.types';
4
4
  import { RangeFilter } from '../components/range-filter';
5
5
  import { createFilterAdapter } from './create-filter-adapter';
@@ -14,39 +14,18 @@ export interface DualRangeFilterValue {
14
14
  }
15
15
 
16
16
  export interface DualRangeFilterAdapterConfig<TData> {
17
- /**
18
- * Unique identifier (should match column accessorKey)
19
- */
17
+ /** Unique id, should match the column `accessorKey`. */
20
18
  id: string;
21
-
22
- /**
23
- * Display label for filter navigation
24
- */
25
19
  label: string;
26
-
27
- /**
28
- * Field configurations: { fieldKey: displayLabel }
29
- * Example: { running: 'Running', completed: 'Completed' }
30
- */
20
+ /** Field key → display label, e.g. `{ running: 'Running' }`. */
31
21
  fields: Record<string, string>;
32
-
33
- /**
34
- * Optional formatter for chip display
35
- * @default (num) => num.toLocaleString('en-US')
36
- */
22
+ /** @default (num) => num.toLocaleString('en-US') */
37
23
  formatNumber?: (num: number) => string;
38
-
39
- /**
40
- * How to extract the value object from a row
41
- * @default (row) => row.getValue(id)
42
- */
24
+ /** @default (row) => row.getValue(id) */
43
25
  getRowValue?: (row: Row<TData>) => Record<string, number>;
44
26
  }
45
27
 
46
- /**
47
- * Factory function to create a dual-range filter adapter
48
- * Handles filtering on multiple numeric ranges within a single column
49
- */
28
+ /** Filter over multiple numeric ranges within one column (AND across fields). */
50
29
  export function createDualRangeFilterAdapter<TData>(
51
30
  config: DualRangeFilterAdapterConfig<TData>,
52
31
  ): FilterAdapter<TData, DualRangeFilterValue, Record<string, number>> {
@@ -71,7 +50,6 @@ export function createDualRangeFilterAdapter<TData>(
71
50
  filterFn: (row, columnId, filterValue) => {
72
51
  const rowValue = getRowValue(row);
73
52
 
74
- // Check each field's range
75
53
  for (const [fieldKey, range] of Object.entries(filterValue)) {
76
54
  const hasFilter = range.from !== undefined || range.to !== undefined;
77
55
  const fieldValue = rowValue[fieldKey];
@@ -80,8 +58,9 @@ export function createDualRangeFilterAdapter<TData>(
80
58
  const matchesFrom = range.from === undefined || fieldValue >= range.from;
81
59
  const matchesTo = range.to === undefined || fieldValue <= range.to;
82
60
 
61
+ // AND across fields: every range with a bound must match.
83
62
  if (!matchesFrom || !matchesTo) {
84
- return false; // AND logic: all ranges must match
63
+ return false;
85
64
  }
86
65
  }
87
66
  }
@@ -89,10 +68,10 @@ export function createDualRangeFilterAdapter<TData>(
89
68
  return true;
90
69
  },
91
70
 
92
- cellRenderer: undefined, // Let the column definition handle rendering
71
+ cellRenderer: undefined,
93
72
 
94
73
  toChips: (value) => {
95
- const chips: ChipItem[] = [];
74
+ const chips: TagFilterItem[] = [];
96
75
 
97
76
  Object.entries(value).forEach(([fieldKey, range]) => {
98
77
  const hasFilter = range.from !== undefined || range.to !== undefined;