@navikt/ds-react 8.10.3 → 8.10.4

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 (159) hide show
  1. package/cjs/action-menu/ActionMenu.js +1 -1
  2. package/cjs/action-menu/ActionMenu.js.map +1 -1
  3. package/cjs/data/stories/Data.test-data.d.ts +24 -0
  4. package/cjs/data/stories/Data.test-data.js +1616 -0
  5. package/cjs/data/stories/Data.test-data.js.map +1 -0
  6. package/cjs/data/table/column-header/DataTableColumnHeader.d.ts +4 -1
  7. package/cjs/data/table/column-header/DataTableColumnHeader.js +2 -2
  8. package/cjs/data/table/column-header/DataTableColumnHeader.js.map +1 -1
  9. package/cjs/data/table/column-header/useTableColumnResize.d.ts +21 -18
  10. package/cjs/data/table/column-header/useTableColumnResize.js +7 -25
  11. package/cjs/data/table/column-header/useTableColumnResize.js.map +1 -1
  12. package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.d.ts +6 -0
  13. package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.js +32 -0
  14. package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.js.map +1 -0
  15. package/cjs/data/table/helpers/collectTableRowEntries.d.ts +4 -4
  16. package/cjs/data/table/helpers/collectTableRowEntries.js +6 -7
  17. package/cjs/data/table/helpers/collectTableRowEntries.js.map +1 -1
  18. package/cjs/data/table/hooks/useColumnOptions.js +18 -5
  19. package/cjs/data/table/hooks/useColumnOptions.js.map +1 -1
  20. package/cjs/data/table/hooks/useTableDetailsPanel.d.ts +62 -0
  21. package/cjs/data/table/hooks/{useTableExpansion.js → useTableDetailsPanel.js} +20 -19
  22. package/cjs/data/table/hooks/useTableDetailsPanel.js.map +1 -0
  23. package/cjs/data/table/hooks/useTableItems.d.ts +13 -16
  24. package/cjs/data/table/hooks/useTableItems.js +9 -8
  25. package/cjs/data/table/hooks/useTableItems.js.map +1 -1
  26. package/cjs/data/table/hooks/useTableSelection.d.ts +4 -2
  27. package/cjs/data/table/hooks/useTableSelection.js +6 -1
  28. package/cjs/data/table/hooks/useTableSelection.js.map +1 -1
  29. package/cjs/data/table/index.d.ts +1 -2
  30. package/cjs/data/table/index.js +22 -12
  31. package/cjs/data/table/index.js.map +1 -1
  32. package/cjs/data/table/root/DataTable.types.d.ts +7 -6
  33. package/cjs/data/table/root/DataTableRoot.context.d.ts +5 -1
  34. package/cjs/data/table/root/DataTableRoot.context.js.map +1 -1
  35. package/cjs/data/table/root/DataTableRoot.d.ts +79 -115
  36. package/cjs/data/table/root/DataTableRoot.js +167 -38
  37. package/cjs/data/table/root/DataTableRoot.js.map +1 -1
  38. package/cjs/data/table/root/DataTableRoot.legacy.d.ts +177 -0
  39. package/cjs/data/table/root/DataTableRoot.legacy.js +104 -0
  40. package/cjs/data/table/root/DataTableRoot.legacy.js.map +1 -0
  41. package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.d.ts +6 -0
  42. package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.js +21 -0
  43. package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.js.map +1 -0
  44. package/cjs/data/table/tr/DataTableTr.js +11 -11
  45. package/cjs/data/table/tr/DataTableTr.js.map +1 -1
  46. package/cjs/utils/components/dismissablelayer/DismissableLayer.js +1 -1
  47. package/cjs/utils/components/dismissablelayer/DismissableLayer.js.map +1 -1
  48. package/cjs/utils/components/floating/Floating.d.ts +16 -1
  49. package/cjs/utils/components/floating/Floating.js +50 -13
  50. package/cjs/utils/components/floating/Floating.js.map +1 -1
  51. package/cjs/utils/components/floating-menu/Menu.js +1 -1
  52. package/cjs/utils/components/floating-menu/Menu.js.map +1 -1
  53. package/cjs/utils/helpers/create-strict-context.js +1 -1
  54. package/cjs/utils/helpers/create-strict-context.js.map +1 -1
  55. package/cjs/utils/hooks/useControllableState.d.ts +5 -5
  56. package/cjs/utils/hooks/useControllableState.js.map +1 -1
  57. package/cjs/utils/hooks/useValueAsRef.js +1 -1
  58. package/cjs/utils/hooks/useValueAsRef.js.map +1 -1
  59. package/cjs/utils-external/hooks/useId.js +1 -1
  60. package/cjs/utils-external/hooks/useId.js.map +1 -1
  61. package/esm/action-menu/ActionMenu.js +1 -1
  62. package/esm/action-menu/ActionMenu.js.map +1 -1
  63. package/esm/data/stories/Data.test-data.d.ts +24 -0
  64. package/esm/data/stories/Data.test-data.js +1607 -0
  65. package/esm/data/stories/Data.test-data.js.map +1 -0
  66. package/esm/data/table/column-header/DataTableColumnHeader.d.ts +4 -1
  67. package/esm/data/table/column-header/DataTableColumnHeader.js +2 -2
  68. package/esm/data/table/column-header/DataTableColumnHeader.js.map +1 -1
  69. package/esm/data/table/column-header/useTableColumnResize.d.ts +21 -18
  70. package/esm/data/table/column-header/useTableColumnResize.js +7 -25
  71. package/esm/data/table/column-header/useTableColumnResize.js.map +1 -1
  72. package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.d.ts +6 -0
  73. package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.js +27 -0
  74. package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.js.map +1 -0
  75. package/esm/data/table/helpers/collectTableRowEntries.d.ts +4 -4
  76. package/esm/data/table/helpers/collectTableRowEntries.js +6 -7
  77. package/esm/data/table/helpers/collectTableRowEntries.js.map +1 -1
  78. package/esm/data/table/hooks/useColumnOptions.js +18 -5
  79. package/esm/data/table/hooks/useColumnOptions.js.map +1 -1
  80. package/esm/data/table/hooks/useTableDetailsPanel.d.ts +62 -0
  81. package/esm/data/table/hooks/{useTableExpansion.js → useTableDetailsPanel.js} +17 -16
  82. package/esm/data/table/hooks/useTableDetailsPanel.js.map +1 -0
  83. package/esm/data/table/hooks/useTableItems.d.ts +13 -16
  84. package/esm/data/table/hooks/useTableItems.js +9 -8
  85. package/esm/data/table/hooks/useTableItems.js.map +1 -1
  86. package/esm/data/table/hooks/useTableSelection.d.ts +4 -2
  87. package/esm/data/table/hooks/useTableSelection.js +6 -1
  88. package/esm/data/table/hooks/useTableSelection.js.map +1 -1
  89. package/esm/data/table/index.d.ts +1 -2
  90. package/esm/data/table/index.js +21 -1
  91. package/esm/data/table/index.js.map +1 -1
  92. package/esm/data/table/root/DataTable.types.d.ts +7 -6
  93. package/esm/data/table/root/DataTableRoot.context.d.ts +5 -1
  94. package/esm/data/table/root/DataTableRoot.context.js.map +1 -1
  95. package/esm/data/table/root/DataTableRoot.d.ts +79 -115
  96. package/esm/data/table/root/DataTableRoot.js +174 -36
  97. package/esm/data/table/root/DataTableRoot.js.map +1 -1
  98. package/esm/data/table/root/DataTableRoot.legacy.d.ts +177 -0
  99. package/esm/data/table/root/DataTableRoot.legacy.js +59 -0
  100. package/esm/data/table/root/DataTableRoot.legacy.js.map +1 -0
  101. package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.d.ts +6 -0
  102. package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.js +16 -0
  103. package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.js.map +1 -0
  104. package/esm/data/table/tr/DataTableTr.js +11 -11
  105. package/esm/data/table/tr/DataTableTr.js.map +1 -1
  106. package/esm/utils/components/dismissablelayer/DismissableLayer.js +1 -1
  107. package/esm/utils/components/dismissablelayer/DismissableLayer.js.map +1 -1
  108. package/esm/utils/components/floating/Floating.d.ts +16 -1
  109. package/esm/utils/components/floating/Floating.js +48 -13
  110. package/esm/utils/components/floating/Floating.js.map +1 -1
  111. package/esm/utils/components/floating-menu/Menu.js +2 -2
  112. package/esm/utils/components/floating-menu/Menu.js.map +1 -1
  113. package/esm/utils/helpers/create-strict-context.js +1 -1
  114. package/esm/utils/helpers/create-strict-context.js.map +1 -1
  115. package/esm/utils/hooks/useControllableState.d.ts +5 -5
  116. package/esm/utils/hooks/useControllableState.js.map +1 -1
  117. package/esm/utils/hooks/useValueAsRef.js +1 -1
  118. package/esm/utils/hooks/useValueAsRef.js.map +1 -1
  119. package/esm/utils-external/hooks/useId.js +1 -1
  120. package/esm/utils-external/hooks/useId.js.map +1 -1
  121. package/package.json +3 -3
  122. package/src/action-menu/ActionMenu.tsx +1 -1
  123. package/src/data/stories/Data.test-data.tsx +1703 -0
  124. package/src/data/table/column-header/DataTableColumnHeader.tsx +6 -6
  125. package/src/data/table/column-header/useTableColumnResize.ts +29 -44
  126. package/src/data/table/details-panel-row/DataTableDetailsPanelRow.tsx +53 -0
  127. package/src/data/table/helpers/collectTableRowEntries.ts +10 -18
  128. package/src/data/table/hooks/__tests__/useTableItems.test.ts +14 -7
  129. package/src/data/table/hooks/__tests__/useTableSelection.test.ts +57 -44
  130. package/src/data/table/hooks/useColumnOptions.ts +19 -5
  131. package/src/data/table/hooks/{useTableExpansion.tsx → useTableDetailsPanel.tsx} +81 -45
  132. package/src/data/table/hooks/useTableItems.ts +27 -36
  133. package/src/data/table/hooks/useTableSelection.ts +17 -6
  134. package/src/data/table/index.tsx +5 -3
  135. package/src/data/table/root/DataTable.types.ts +20 -6
  136. package/src/data/table/root/DataTableRoot.context.ts +5 -1
  137. package/src/data/table/root/DataTableRoot.legacy.tsx +297 -0
  138. package/src/data/table/root/DataTableRoot.tsx +482 -217
  139. package/src/data/table/sub-row-toggle/DataTableSubRowToggle.tsx +39 -0
  140. package/src/data/table/tr/DataTableTr.tsx +14 -13
  141. package/src/utils/components/dismissablelayer/DismissableLayer.tsx +1 -1
  142. package/src/utils/components/floating/Floating.tsx +56 -13
  143. package/src/utils/components/floating-menu/Menu.tsx +4 -1
  144. package/src/utils/helpers/create-strict-context.tsx +1 -1
  145. package/src/utils/hooks/useControllableState.ts +11 -8
  146. package/src/utils/hooks/useValueAsRef.ts +1 -1
  147. package/src/utils-external/hooks/useId.ts +1 -1
  148. package/cjs/data/table/hooks/useTableExpansion.d.ts +0 -27
  149. package/cjs/data/table/hooks/useTableExpansion.js.map +0 -1
  150. package/cjs/data/table/root/DataTableAuto.d.ts +0 -182
  151. package/cjs/data/table/root/DataTableAuto.js +0 -206
  152. package/cjs/data/table/root/DataTableAuto.js.map +0 -1
  153. package/esm/data/table/hooks/useTableExpansion.d.ts +0 -27
  154. package/esm/data/table/hooks/useTableExpansion.js.map +0 -1
  155. package/esm/data/table/root/DataTableAuto.d.ts +0 -182
  156. package/esm/data/table/root/DataTableAuto.js +0 -170
  157. package/esm/data/table/root/DataTableAuto.js.map +0 -1
  158. package/src/data/table/root/DataTableAuto.test.tsx +0 -244
  159. package/src/data/table/root/DataTableAuto.tsx +0 -612
@@ -17,11 +17,10 @@ import { type ResizeProps, useTableColumnResize } from "./useTableColumnResize";
17
17
 
18
18
  interface DataTableColumnHeaderProps
19
19
  extends ResizeProps, DataTableBaseCellProps {
20
- resizeHandler?: (
21
- event:
22
- | React.MouseEvent<HTMLButtonElement>
23
- | React.TouchEvent<HTMLButtonElement>,
24
- ) => void;
20
+ /**
21
+ * Accessible name of the column.
22
+ */
23
+ label: string;
25
24
  /**
26
25
  * Makes the column header sortable. The entire header cell content becomes
27
26
  * a clickable button when true.
@@ -59,6 +58,7 @@ const DataTableColumnHeader = forwardRef<
59
58
  {
60
59
  className,
61
60
  children,
61
+ label,
62
62
  sortable = false,
63
63
  sortDirection = "none",
64
64
  onSortClick,
@@ -144,7 +144,7 @@ const DataTableColumnHeader = forwardRef<
144
144
  aria-label={
145
145
  resizeResult.isResizingWithKeyboard
146
146
  ? "Bruk pil venstre/høyre"
147
- : "Endre bredde"
147
+ : `Endre bredde ${label}`
148
148
  } // TODO Translate
149
149
  data-active={resizeResult.isResizingWithKeyboard}
150
150
  data-disable-keyboard-nav={resizeResult.isResizingWithKeyboard}
@@ -8,9 +8,8 @@ import {
8
8
  import { useControllableState } from "../../../utils/hooks";
9
9
  import { useDataTableContext } from "../root/DataTableRoot.context";
10
10
 
11
- type ColumnWidth = number | string;
12
-
13
11
  type ResizeProps = {
12
+ // If/when we add support for composition, consider mentioning that resizing only works on first row in thead.
14
13
  /**
15
14
  * Whether the column should be resizable by the user.
16
15
  *
@@ -18,16 +17,6 @@ type ResizeProps = {
18
17
  * @default true
19
18
  */
20
19
  resizable?: boolean;
21
- /**
22
- * Controlled width of the column.
23
- *
24
- * Should only be used to fully control column width state. Otherwise, use `defaultWidth` and let the component handle resizing.
25
- */
26
- width?: ColumnWidth;
27
- /**
28
- * Initial width of the column. Only used when `width` is not set and `resizable` is true.
29
- */
30
- defaultWidth?: ColumnWidth;
31
20
  /**
32
21
  * Whether the column should automatically resize to fit its content. **Runs only once.**
33
22
  *
@@ -40,21 +29,36 @@ type ResizeProps = {
40
29
  */
41
30
  autoWidth?: boolean;
42
31
  /**
43
- * Minimum width of the column.
32
+ * Minimum width of the column when resizing. Only used when `resizable` or `autoWidth` is enabled.
33
+ * @default 40
34
+ */
35
+ minWidth?: number;
36
+ /**
37
+ * Maximum width of the column when resizing. Only used when `resizable` or `autoWidth` is enabled.
38
+ */
39
+ maxWidth?: number;
40
+ // TODO: Consider "allowing" %-width on last column, if we find a solution to the overflow issue (width becomes 0px).
41
+ /**
42
+ * Controlled width of the column. Does not respect `minWidth` and `maxWidth`.
44
43
  *
45
- * Should be used in conjunction with `width` or `defaultWidth` to set limits when resizing.
44
+ * Should only be used to fully control column width state. Otherwise, use `defaultWidth` and let the component handle resizing.
45
+ *
46
+ * **NB:** Percentage as initial width does not work well with resizing.
46
47
  */
47
- minWidth?: ColumnWidth;
48
+ width?: number | string;
48
49
  /**
49
- * Maximum width of the column.
50
+ * Initial width of the column. Only used when `width` is not set and `resizable` is true.
51
+ * Does not respect `minWidth` and `maxWidth`.
50
52
  *
51
- * Should be used in conjunction with `width` or `defaultWidth` to set limits when resizing.
53
+ * **NB:** Percentage as initial width does not work well with resizing.
54
+ * @default 140px
52
55
  */
53
- maxWidth?: ColumnWidth;
56
+ defaultWidth?: number | string;
54
57
  /**
55
58
  * Called when the column width changes.
59
+ * @param width New width in pixels.
56
60
  */
57
- onWidthChange?: (width: ColumnWidth) => void;
61
+ onWidthChange?: (width: number) => void;
58
62
  /**
59
63
  * Forwarded styles
60
64
  */
@@ -133,10 +137,7 @@ function useTableColumnResize(
133
137
 
134
138
  const setClampedWidth = useCallback(
135
139
  (newWidth: number) => {
136
- const min = parseWidth(minWidth) ?? 0;
137
- const max = parseWidth(maxWidth) ?? Infinity;
138
- const clamped = Math.min(Math.max(newWidth, min), max);
139
- setWidth(clamped);
140
+ setWidth(Math.min(Math.max(newWidth, minWidth), maxWidth));
140
141
  },
141
142
  [minWidth, maxWidth, setWidth],
142
143
  );
@@ -212,14 +213,11 @@ function useTableColumnResize(
212
213
  const currentWidth = thRef.current?.offsetWidth ?? 0;
213
214
  const newWidth = startWidth + (clientX - startX);
214
215
 
215
- const min = parseWidth(minWidth) ?? 0;
216
- const max = parseWidth(maxWidth) ?? Infinity;
217
-
218
- if (newWidth > max) {
216
+ if (newWidth > maxWidth) {
219
217
  setWidth(newWidth < currentWidth ? newWidth : currentWidth);
220
218
  return;
221
219
  }
222
- if (newWidth < min) {
220
+ if (newWidth < minWidth) {
223
221
  setWidth(newWidth > currentWidth ? newWidth : currentWidth);
224
222
  return;
225
223
  }
@@ -315,20 +313,6 @@ function useTableColumnResize(
315
313
  };
316
314
  }
317
315
 
318
- function parseWidth(width: ColumnWidth | undefined): number | undefined {
319
- if (width == null) {
320
- return undefined;
321
- }
322
- if (typeof width === "number") {
323
- return width;
324
- }
325
- if (typeof width === "string") {
326
- const parsed = parseInt(width, 10);
327
- return Number.isNaN(parsed) ? undefined : parsed;
328
- }
329
- return undefined;
330
- }
331
-
332
316
  function getAutoColumnWidth(
333
317
  thRef: React.RefObject<HTMLTableCellElement | null>,
334
318
  ) {
@@ -341,7 +325,9 @@ function getAutoColumnWidth(
341
325
  }
342
326
 
343
327
  // Find needed width for header cell
344
- const contentWidth = thContent.scrollWidth;
328
+ const range = document.createRange();
329
+ range.selectNodeContents(thContent);
330
+ const contentWidth = range.getBoundingClientRect().width;
345
331
  const paddingElStyle = window.getComputedStyle(thPaddingEl);
346
332
  const thInlinePadding =
347
333
  parseInt(paddingElStyle.paddingLeft, 10) +
@@ -357,7 +343,6 @@ function getAutoColumnWidth(
357
343
  }
358
344
 
359
345
  // Find needed width for each cell in column in tbody and tfoot
360
- const range = document.createRange();
361
346
  let skipRows = 0;
362
347
  for (const row of rows) {
363
348
  // Skip rows where the cell in this column is covered by a rowspan from a previous row
@@ -0,0 +1,53 @@
1
+ import React from "react";
2
+ import {
3
+ getDataTableDetailsPanelId,
4
+ useDataTableDetailsPanel,
5
+ } from "../hooks/useTableDetailsPanel";
6
+ import { useDataTableContext } from "../root/DataTableRoot.context";
7
+
8
+ function DataTableDetailsPanelRow<T>({
9
+ rowId,
10
+ rowData,
11
+ }: {
12
+ rowId: string | number;
13
+ rowData: T;
14
+ }) {
15
+ const { tableId, fullWidthColSpan } = useDataTableContext();
16
+ const {
17
+ enableDetailsPanel,
18
+ isExpanded,
19
+ getDetailsPanelContent,
20
+ getDetailsPanelHeight,
21
+ } = useDataTableDetailsPanel();
22
+
23
+ if (!enableDetailsPanel) {
24
+ return null;
25
+ }
26
+
27
+ if (!isExpanded(rowId)) {
28
+ return null;
29
+ }
30
+
31
+ const content = getDetailsPanelContent?.(rowData);
32
+ const expansionId = getDataTableDetailsPanelId(tableId, rowId);
33
+
34
+ if (!content) {
35
+ return null;
36
+ }
37
+
38
+ const panelHeight = getDetailsPanelHeight?.(rowData);
39
+
40
+ const style: React.CSSProperties = panelHeight
41
+ ? { height: panelHeight, overflow: "auto" }
42
+ : { height: "auto" };
43
+
44
+ return (
45
+ <tr className="aksel-data-table__details-panel-row">
46
+ <td id={expansionId} colSpan={fullWidthColSpan}>
47
+ <div style={style}>{content}</div>
48
+ </td>
49
+ </tr>
50
+ );
51
+ }
52
+
53
+ export { DataTableDetailsPanelRow };
@@ -2,9 +2,9 @@ type TableRowEntryId = string | number;
2
2
 
3
3
  type CollectTableRowEntriesArgs<T> = {
4
4
  items: T[];
5
- getRowId?: (rowData: T, index: number) => TableRowEntryId;
6
- getSubRows?: (rowData: T) => T[];
7
- isSubRowExpandable?: (rowData: T) => boolean;
5
+ getRowId: (rowData: T, index: number) => TableRowEntryId;
6
+ getRows?: (rowData: T) => T[];
7
+ isRowExpandable?: (rowData: T) => boolean;
8
8
  };
9
9
 
10
10
  interface ItemDetail<T> {
@@ -26,8 +26,8 @@ type CollectTableRowEntriesReturn<T> = {
26
26
  function collectTableRowEntries<T>({
27
27
  items,
28
28
  getRowId,
29
- getSubRows,
30
- isSubRowExpandable,
29
+ getRows,
30
+ isRowExpandable,
31
31
  }: CollectTableRowEntriesArgs<T>): CollectTableRowEntriesReturn<T> {
32
32
  const itemDetailsMap = new Map<T, ItemDetail<T>>();
33
33
  const childRowIdsById = new Map<TableRowEntryId, TableRowEntryId[]>();
@@ -37,13 +37,11 @@ function collectTableRowEntries<T>({
37
37
  rowIndex: number,
38
38
  level: number,
39
39
  parent: T | null,
40
- parentId?: TableRowEntryId,
41
40
  ): TableRowEntryId => {
42
- const rowId =
43
- getRowId?.(rowData, rowIndex) ??
44
- (parentId == null ? rowIndex : `${parentId}-${rowIndex}`);
45
- const isRowExpandable = isSubRowExpandable?.(rowData) ?? true;
46
- const children = (isRowExpandable ? getSubRows?.(rowData) : []) ?? [];
41
+ const rowId = getRowId(rowData, rowIndex);
42
+
43
+ const children =
44
+ ((isRowExpandable?.(rowData) ?? true) ? getRows?.(rowData) : []) ?? [];
47
45
 
48
46
  itemDetailsMap.set(rowData, {
49
47
  id: rowId,
@@ -56,13 +54,7 @@ function collectTableRowEntries<T>({
56
54
 
57
55
  for (let childIndex = 0; childIndex < children.length; childIndex++) {
58
56
  const childRow = children[childIndex];
59
- const childRowId = traverseRow(
60
- childRow,
61
- childIndex,
62
- level + 1,
63
- rowData,
64
- rowId,
65
- );
57
+ const childRowId = traverseRow(childRow, childIndex, level + 1, rowData);
66
58
  childRowIds.push(childRowId);
67
59
  }
68
60
 
@@ -78,8 +78,10 @@ describe("useTableItems", () => {
78
78
  useTableItems({
79
79
  items: nestedRows,
80
80
  getRowId: (row) => row.id,
81
- getSubRows,
82
- defaultExpandedSubRowIds: ["a"],
81
+ subRows: {
82
+ getRows: getSubRows,
83
+ defaultExpandedRowIds: ["a"],
84
+ },
83
85
  }),
84
86
  );
85
87
 
@@ -91,7 +93,7 @@ describe("useTableItems", () => {
91
93
  useTableItems({
92
94
  items: nestedRows,
93
95
  getRowId: (row) => row.id,
94
- getSubRows,
96
+ subRows: { getRows: getSubRows },
95
97
  }),
96
98
  );
97
99
 
@@ -104,8 +106,11 @@ describe("useTableItems", () => {
104
106
  const { result } = renderHook(() =>
105
107
  useTableItems({
106
108
  items: fallbackRows,
107
- getSubRows: (row) => row.subRows ?? [],
108
- defaultExpandedSubRowIds: [0],
109
+ getRowId: (_, index) => index,
110
+ subRows: {
111
+ getRows: (row: any) => row.subRows ?? [],
112
+ defaultExpandedRowIds: [0],
113
+ },
109
114
  }),
110
115
  );
111
116
 
@@ -121,8 +126,10 @@ describe("useTableItems", () => {
121
126
  useTableItems({
122
127
  items: nestedRows,
123
128
  getRowId: (row) => row.id,
124
- getSubRows,
125
- expandedSubRowIds: expandedIds,
129
+ subRows: {
130
+ getRows: getSubRows,
131
+ expandedRowIds: expandedIds,
132
+ },
126
133
  }),
127
134
  {
128
135
  initialProps: { expandedIds: [] as (string | number)[] },
@@ -42,7 +42,7 @@ describe("useTableSelection", () => {
42
42
  test("returns empty selectedKeys and no prop getters", () => {
43
43
  const { result } = renderHook(() =>
44
44
  useTableSelection({
45
- selectionMode: "none",
45
+ selection: { selectionMode: "none" },
46
46
  visibleRowIds,
47
47
  }),
48
48
  );
@@ -56,7 +56,7 @@ describe("useTableSelection", () => {
56
56
  test("returns getRowRadioProps", () => {
57
57
  const { result } = renderHook(() =>
58
58
  useTableSelection({
59
- selectionMode: "single",
59
+ selection: { selectionMode: "single" },
60
60
  visibleRowIds,
61
61
  }),
62
62
  );
@@ -69,9 +69,8 @@ describe("useTableSelection", () => {
69
69
  const onChange = vi.fn();
70
70
  const { result } = renderHook(() =>
71
71
  useTableSelection({
72
- selectionMode: "single",
72
+ selection: { selectionMode: "single", onSelectionChange: onChange },
73
73
  visibleRowIds,
74
- onSelectionChange: onChange,
75
74
  }),
76
75
  );
77
76
 
@@ -88,9 +87,8 @@ describe("useTableSelection", () => {
88
87
  test("toggling the same row keeps it selected", () => {
89
88
  const { result } = renderHook(() =>
90
89
  useTableSelection({
91
- selectionMode: "single",
90
+ selection: { selectionMode: "single", defaultSelectedKeys: ["a"] },
92
91
  visibleRowIds,
93
- defaultSelectedKeys: ["a"],
94
92
  }),
95
93
  );
96
94
 
@@ -106,9 +104,8 @@ describe("useTableSelection", () => {
106
104
  test("selecting a new row replaces the previous", () => {
107
105
  const { result } = renderHook(() =>
108
106
  useTableSelection({
109
- selectionMode: "single",
107
+ selection: { selectionMode: "single", defaultSelectedKeys: ["a"] },
110
108
  visibleRowIds,
111
- defaultSelectedKeys: ["a"],
112
109
  }),
113
110
  );
114
111
 
@@ -124,9 +121,8 @@ describe("useTableSelection", () => {
124
121
  test("disabled rows have disabled prop", () => {
125
122
  const { result } = renderHook(() =>
126
123
  useTableSelection({
127
- selectionMode: "single",
124
+ selection: { selectionMode: "single", disabledSelectionKeys: ["b"] },
128
125
  visibleRowIds,
129
- disabledSelectionKeys: ["b"],
130
126
  }),
131
127
  );
132
128
 
@@ -138,9 +134,8 @@ describe("useTableSelection", () => {
138
134
  const { result, rerender } = renderHook(
139
135
  ({ selectedKeys }) =>
140
136
  useTableSelection({
141
- selectionMode: "single",
137
+ selection: { selectionMode: "single", selectedKeys },
142
138
  visibleRowIds,
143
- selectedKeys,
144
139
  }),
145
140
  { initialProps: { selectedKeys: ["a"] as (string | number)[] } },
146
141
  );
@@ -156,7 +151,7 @@ describe("useTableSelection", () => {
156
151
  test("returns getTheadCheckboxProps and getRowCheckboxProps", () => {
157
152
  const { result } = renderHook(() =>
158
153
  useTableSelection({
159
- selectionMode: "multiple",
154
+ selection: { selectionMode: "multiple" },
160
155
  visibleRowIds,
161
156
  }),
162
157
  );
@@ -169,7 +164,7 @@ describe("useTableSelection", () => {
169
164
  test("selecting individual rows", () => {
170
165
  const { result } = renderHook(() =>
171
166
  useTableSelection({
172
- selectionMode: "multiple",
167
+ selection: { selectionMode: "multiple" },
173
168
  visibleRowIds,
174
169
  }),
175
170
  );
@@ -194,9 +189,11 @@ describe("useTableSelection", () => {
194
189
  test("deselecting a row", () => {
195
190
  const { result } = renderHook(() =>
196
191
  useTableSelection({
197
- selectionMode: "multiple",
192
+ selection: {
193
+ selectionMode: "multiple",
194
+ defaultSelectedKeys: ["a", "b"],
195
+ },
198
196
  visibleRowIds,
199
- defaultSelectedKeys: ["a", "b"],
200
197
  }),
201
198
  );
202
199
 
@@ -212,7 +209,7 @@ describe("useTableSelection", () => {
212
209
  test("select all via thead checkbox", () => {
213
210
  const { result } = renderHook(() =>
214
211
  useTableSelection({
215
- selectionMode: "multiple",
212
+ selection: { selectionMode: "multiple" },
216
213
  visibleRowIds,
217
214
  }),
218
215
  );
@@ -229,7 +226,7 @@ describe("useTableSelection", () => {
229
226
  test("select all via thead includes hidden descendants for visible parents", () => {
230
227
  const { result } = renderHook(() =>
231
228
  useTableSelection({
232
- selectionMode: "multiple",
229
+ selection: { selectionMode: "multiple" },
233
230
  visibleRowIds: ["a"],
234
231
  childRowIdsById,
235
232
  }),
@@ -247,9 +244,11 @@ describe("useTableSelection", () => {
247
244
  test("deselect all when all are selected", () => {
248
245
  const { result } = renderHook(() =>
249
246
  useTableSelection({
250
- selectionMode: "multiple",
247
+ selection: {
248
+ selectionMode: "multiple",
249
+ defaultSelectedKeys: ["a", "b", "c"],
250
+ },
251
251
  visibleRowIds,
252
- defaultSelectedKeys: ["a", "b", "c"],
253
252
  }),
254
253
  );
255
254
 
@@ -265,10 +264,12 @@ describe("useTableSelection", () => {
265
264
  test("deselect all clears hidden descendants for visible parents but preserves unrelated keys", () => {
266
265
  const { result } = renderHook(() =>
267
266
  useTableSelection({
268
- selectionMode: "multiple",
267
+ selection: {
268
+ selectionMode: "multiple",
269
+ defaultSelectedKeys: ["a", "a1", "a2", "a2a", "external"],
270
+ },
269
271
  visibleRowIds: ["a"],
270
272
  childRowIdsById,
271
- defaultSelectedKeys: ["a", "a1", "a2", "a2a", "external"],
272
273
  }),
273
274
  );
274
275
 
@@ -284,9 +285,11 @@ describe("useTableSelection", () => {
284
285
  test("select all skips disabled keys", () => {
285
286
  const { result } = renderHook(() =>
286
287
  useTableSelection({
287
- selectionMode: "multiple",
288
+ selection: {
289
+ selectionMode: "multiple",
290
+ disabledSelectionKeys: ["b"],
291
+ },
288
292
  visibleRowIds,
289
- disabledSelectionKeys: ["b"],
290
293
  }),
291
294
  );
292
295
 
@@ -302,10 +305,12 @@ describe("useTableSelection", () => {
302
305
  test("deselect all preserves disabled-but-selected rows", () => {
303
306
  const { result } = renderHook(() =>
304
307
  useTableSelection({
305
- selectionMode: "multiple",
308
+ selection: {
309
+ selectionMode: "multiple",
310
+ defaultSelectedKeys: ["a", "b", "c"],
311
+ disabledSelectionKeys: ["b"],
312
+ },
306
313
  visibleRowIds,
307
- defaultSelectedKeys: ["a", "b", "c"],
308
- disabledSelectionKeys: ["b"],
309
314
  }),
310
315
  );
311
316
 
@@ -321,9 +326,8 @@ describe("useTableSelection", () => {
321
326
  test("thead checkbox shows indeterminate when partially selected", () => {
322
327
  const { result } = renderHook(() =>
323
328
  useTableSelection({
324
- selectionMode: "multiple",
329
+ selection: { selectionMode: "multiple", defaultSelectedKeys: ["a"] },
325
330
  visibleRowIds,
326
- defaultSelectedKeys: ["a"],
327
331
  }),
328
332
  );
329
333
 
@@ -335,9 +339,11 @@ describe("useTableSelection", () => {
335
339
  test("thead checkbox shows checked when all selected", () => {
336
340
  const { result } = renderHook(() =>
337
341
  useTableSelection({
338
- selectionMode: "multiple",
342
+ selection: {
343
+ selectionMode: "multiple",
344
+ defaultSelectedKeys: ["a", "b", "c"],
345
+ },
339
346
  visibleRowIds,
340
- defaultSelectedKeys: ["a", "b", "c"],
341
347
  }),
342
348
  );
343
349
 
@@ -349,10 +355,12 @@ describe("useTableSelection", () => {
349
355
  test("thead checkbox shows checked when all selectable rows are selected", () => {
350
356
  const { result } = renderHook(() =>
351
357
  useTableSelection({
352
- selectionMode: "multiple",
358
+ selection: {
359
+ selectionMode: "multiple",
360
+ defaultSelectedKeys: ["a", "c"],
361
+ disabledSelectionKeys: ["b"],
362
+ },
353
363
  visibleRowIds,
354
- defaultSelectedKeys: ["a", "c"],
355
- disabledSelectionKeys: ["b"],
356
364
  }),
357
365
  );
358
366
 
@@ -364,9 +372,11 @@ describe("useTableSelection", () => {
364
372
  test("deselecting one row when all rows are selected", () => {
365
373
  const { result } = renderHook(() =>
366
374
  useTableSelection({
367
- selectionMode: "multiple",
375
+ selection: {
376
+ selectionMode: "multiple",
377
+ defaultSelectedKeys: ["a", "b", "c"],
378
+ },
368
379
  visibleRowIds,
369
- defaultSelectedKeys: ["a", "b", "c"],
370
380
  }),
371
381
  );
372
382
 
@@ -382,9 +392,11 @@ describe("useTableSelection", () => {
382
392
  test("disabled rows have disabled prop", () => {
383
393
  const { result } = renderHook(() =>
384
394
  useTableSelection({
385
- selectionMode: "multiple",
395
+ selection: {
396
+ selectionMode: "multiple",
397
+ disabledSelectionKeys: ["b"],
398
+ },
386
399
  visibleRowIds,
387
- disabledSelectionKeys: ["b"],
388
400
  }),
389
401
  );
390
402
 
@@ -395,9 +407,11 @@ describe("useTableSelection", () => {
395
407
  test("thead checkbox disabled when all rows disabled", () => {
396
408
  const { result } = renderHook(() =>
397
409
  useTableSelection({
398
- selectionMode: "multiple",
410
+ selection: {
411
+ selectionMode: "multiple",
412
+ disabledSelectionKeys: ["a", "b", "c"],
413
+ },
399
414
  visibleRowIds,
400
- disabledSelectionKeys: ["a", "b", "c"],
401
415
  }),
402
416
  );
403
417
 
@@ -407,10 +421,9 @@ describe("useTableSelection", () => {
407
421
  test("parent rows show indeterminate when visible descendants are partially selected", () => {
408
422
  const { result } = renderHook(() =>
409
423
  useTableSelection({
410
- selectionMode: "multiple",
424
+ selection: { selectionMode: "multiple", defaultSelectedKeys: ["a1"] },
411
425
  visibleRowIds: ["a", "a1", "a2"],
412
426
  childRowIdsById,
413
- defaultSelectedKeys: ["a1"],
414
427
  }),
415
428
  );
416
429
 
@@ -423,7 +436,7 @@ describe("useTableSelection", () => {
423
436
  test("toggling a parent row selects and deselects its descendants", () => {
424
437
  const { result } = renderHook(() =>
425
438
  useTableSelection({
426
- selectionMode: "multiple",
439
+ selection: { selectionMode: "multiple" },
427
440
  visibleRowIds: ["a", "a1", "a2"],
428
441
  childRowIdsById,
429
442
  }),
@@ -449,7 +462,7 @@ describe("useTableSelection", () => {
449
462
  test("toggling a collapsed parent selects and deselects hidden descendants", () => {
450
463
  const { result } = renderHook(() =>
451
464
  useTableSelection({
452
- selectionMode: "multiple",
465
+ selection: { selectionMode: "multiple" },
453
466
  visibleRowIds: ["a"],
454
467
  childRowIdsById,
455
468
  }),
@@ -1,3 +1,4 @@
1
+ import { useMemo } from "react";
1
2
  import type {
2
3
  ColumnDefinition,
3
4
  ColumnDefinitions,
@@ -28,19 +29,32 @@ function useColumnOptions<T>(
28
29
 
29
30
  const hasSelection = selectionMode !== "none";
30
31
 
31
- return {
32
- stickySelection: selectionMode !== "none" && stickyColumns?.first === "1",
33
- columns: columnDefinitions.map((colDef, index) => {
32
+ const columns = useMemo(() => {
33
+ return columnDefinitions.map((colDef, index) => {
34
34
  const isFirstSticky =
35
35
  stickyColumns?.first === "1" && index === 0 && !hasSelection;
36
36
  const isLastSticky =
37
37
  stickyColumns?.last === "1" && index === columnDefinitions.length - 1;
38
38
 
39
39
  return {
40
- isSticky: isFirstSticky ? "start" : isLastSticky ? "end" : false,
40
+ isSticky: isFirstSticky
41
+ ? ("start" as const)
42
+ : isLastSticky
43
+ ? ("end" as const)
44
+ : (false as const),
41
45
  colDef,
42
46
  };
43
- }),
47
+ });
48
+ }, [
49
+ columnDefinitions,
50
+ hasSelection,
51
+ stickyColumns?.first,
52
+ stickyColumns?.last,
53
+ ]);
54
+
55
+ return {
56
+ stickySelection: selectionMode !== "none" && stickyColumns?.first === "1",
57
+ columns,
44
58
  };
45
59
  }
46
60