@navikt/ds-react 8.10.4 → 8.10.6

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 (244) hide show
  1. package/cjs/data/data-grid/index.d.ts +2 -0
  2. package/cjs/data/data-grid/index.js +7 -0
  3. package/cjs/data/data-grid/index.js.map +1 -0
  4. package/cjs/data/data-grid/root/DataGridRoot.context.d.ts +11 -0
  5. package/cjs/data/data-grid/root/DataGridRoot.context.js +11 -0
  6. package/cjs/data/data-grid/root/DataGridRoot.context.js.map +1 -0
  7. package/cjs/data/data-grid/root/DataGridRoot.d.ts +38 -0
  8. package/cjs/data/data-grid/root/DataGridRoot.js +68 -0
  9. package/cjs/data/data-grid/root/DataGridRoot.js.map +1 -0
  10. package/cjs/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js +11 -13
  11. package/cjs/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js.map +1 -1
  12. package/cjs/data/drag-and-drop/root/DragAndDrop.context.d.ts +4 -2
  13. package/cjs/data/drag-and-drop/root/DragAndDrop.context.js.map +1 -1
  14. package/cjs/data/drag-and-drop/root/DragAndDropRoot.js +44 -46
  15. package/cjs/data/drag-and-drop/root/DragAndDropRoot.js.map +1 -1
  16. package/cjs/data/drag-and-drop/types.d.ts +0 -4
  17. package/cjs/data/stories/Data.test-data.d.ts +2 -5
  18. package/cjs/data/stories/Data.test-data.js +30 -39
  19. package/cjs/data/stories/Data.test-data.js.map +1 -1
  20. package/cjs/data/table/base-cell/DataTableBaseCell.d.ts +15 -15
  21. package/cjs/data/table/base-cell/DataTableBaseCell.js +4 -8
  22. package/cjs/data/table/base-cell/DataTableBaseCell.js.map +1 -1
  23. package/cjs/data/table/column-header/DataTableColumnHeader.d.ts +24 -6
  24. package/cjs/data/table/column-header/DataTableColumnHeader.js +22 -27
  25. package/cjs/data/table/column-header/DataTableColumnHeader.js.map +1 -1
  26. package/cjs/data/table/column-header/useTableColumnResize.d.ts +19 -29
  27. package/cjs/data/table/column-header/useTableColumnResize.js +24 -22
  28. package/cjs/data/table/column-header/useTableColumnResize.js.map +1 -1
  29. package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.d.ts +1 -1
  30. package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.js +2 -2
  31. package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.js.map +1 -1
  32. package/cjs/data/table/helpers/collectTableRowEntries.d.ts +9 -7
  33. package/cjs/data/table/helpers/collectTableRowEntries.js +18 -10
  34. package/cjs/data/table/helpers/collectTableRowEntries.js.map +1 -1
  35. package/cjs/data/table/helpers/selection/getMultipleSelectProps.d.ts +13 -11
  36. package/cjs/data/table/helpers/selection/getMultipleSelectProps.js +43 -53
  37. package/cjs/data/table/helpers/selection/getMultipleSelectProps.js.map +1 -1
  38. package/cjs/data/table/helpers/selection/getSingleSelectProps.d.ts +9 -8
  39. package/cjs/data/table/helpers/selection/getSingleSelectProps.js +23 -10
  40. package/cjs/data/table/helpers/selection/getSingleSelectProps.js.map +1 -1
  41. package/cjs/data/table/helpers/selection/selection.types.d.ts +19 -19
  42. package/cjs/data/table/helpers/selection/selection.utils.d.ts +21 -0
  43. package/cjs/data/table/helpers/selection/selection.utils.js +46 -0
  44. package/cjs/data/table/helpers/selection/selection.utils.js.map +1 -0
  45. package/cjs/data/table/helpers/table-focus.d.ts +0 -3
  46. package/cjs/data/table/helpers/table-focus.js +38 -8
  47. package/cjs/data/table/helpers/table-focus.js.map +1 -1
  48. package/cjs/data/table/hooks/useColumnOptions.d.ts +16 -5
  49. package/cjs/data/table/hooks/useColumnOptions.js +26 -8
  50. package/cjs/data/table/hooks/useColumnOptions.js.map +1 -1
  51. package/cjs/data/table/hooks/useGridCache.js +2 -2
  52. package/cjs/data/table/hooks/useGridCache.js.map +1 -1
  53. package/cjs/data/table/hooks/useTableDetailsPanel.d.ts +10 -13
  54. package/cjs/data/table/hooks/useTableDetailsPanel.js +7 -6
  55. package/cjs/data/table/hooks/useTableDetailsPanel.js.map +1 -1
  56. package/cjs/data/table/hooks/useTableItems.d.ts +31 -17
  57. package/cjs/data/table/hooks/useTableItems.js +10 -20
  58. package/cjs/data/table/hooks/useTableItems.js.map +1 -1
  59. package/cjs/data/table/hooks/useTableKeyboardNav.d.ts +1 -6
  60. package/cjs/data/table/hooks/useTableKeyboardNav.js +6 -5
  61. package/cjs/data/table/hooks/useTableKeyboardNav.js.map +1 -1
  62. package/cjs/data/table/hooks/useTableSelection.d.ts +6 -6
  63. package/cjs/data/table/hooks/useTableSelection.js +13 -13
  64. package/cjs/data/table/hooks/useTableSelection.js.map +1 -1
  65. package/cjs/data/table/hooks/useTableSort.d.ts +2 -2
  66. package/cjs/data/table/hooks/useTableSort.js +4 -5
  67. package/cjs/data/table/hooks/useTableSort.js.map +1 -1
  68. package/cjs/data/table/root/DataTable.types.d.ts +22 -13
  69. package/cjs/data/table/root/DataTableRoot.context.d.ts +13 -7
  70. package/cjs/data/table/root/DataTableRoot.context.js.map +1 -1
  71. package/cjs/data/table/root/DataTableRoot.d.ts +49 -72
  72. package/cjs/data/table/root/DataTableRoot.js +56 -72
  73. package/cjs/data/table/root/DataTableRoot.js.map +1 -1
  74. package/cjs/data/table/root/DataTableRoot.legacy.d.ts +2 -7
  75. package/cjs/data/table/root/DataTableRoot.legacy.js +17 -3
  76. package/cjs/data/table/root/DataTableRoot.legacy.js.map +1 -1
  77. package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.js +4 -4
  78. package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.js.map +1 -1
  79. package/cjs/data/table/tbody/DataTableTbody.js +4 -2
  80. package/cjs/data/table/tbody/DataTableTbody.js.map +1 -1
  81. package/cjs/data/table/tr/DataTableTr.d.ts +5 -3
  82. package/cjs/data/table/tr/DataTableTr.js +36 -23
  83. package/cjs/data/table/tr/DataTableTr.js.map +1 -1
  84. package/cjs/table/ColumnHeader.js +2 -1
  85. package/cjs/table/ColumnHeader.js.map +1 -1
  86. package/esm/data/data-grid/index.d.ts +2 -0
  87. package/esm/data/data-grid/index.js +3 -0
  88. package/esm/data/data-grid/index.js.map +1 -0
  89. package/esm/data/data-grid/root/DataGridRoot.context.d.ts +11 -0
  90. package/esm/data/data-grid/root/DataGridRoot.context.js +7 -0
  91. package/esm/data/data-grid/root/DataGridRoot.context.js.map +1 -0
  92. package/esm/data/data-grid/root/DataGridRoot.d.ts +38 -0
  93. package/esm/data/data-grid/root/DataGridRoot.js +32 -0
  94. package/esm/data/data-grid/root/DataGridRoot.js.map +1 -0
  95. package/esm/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js +11 -13
  96. package/esm/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js.map +1 -1
  97. package/esm/data/drag-and-drop/root/DragAndDrop.context.d.ts +4 -2
  98. package/esm/data/drag-and-drop/root/DragAndDrop.context.js.map +1 -1
  99. package/esm/data/drag-and-drop/root/DragAndDropRoot.js +44 -46
  100. package/esm/data/drag-and-drop/root/DragAndDropRoot.js.map +1 -1
  101. package/esm/data/drag-and-drop/types.d.ts +0 -4
  102. package/esm/data/stories/Data.test-data.d.ts +2 -5
  103. package/esm/data/stories/Data.test-data.js +30 -39
  104. package/esm/data/stories/Data.test-data.js.map +1 -1
  105. package/esm/data/table/base-cell/DataTableBaseCell.d.ts +15 -15
  106. package/esm/data/table/base-cell/DataTableBaseCell.js +4 -8
  107. package/esm/data/table/base-cell/DataTableBaseCell.js.map +1 -1
  108. package/esm/data/table/column-header/DataTableColumnHeader.d.ts +24 -6
  109. package/esm/data/table/column-header/DataTableColumnHeader.js +23 -28
  110. package/esm/data/table/column-header/DataTableColumnHeader.js.map +1 -1
  111. package/esm/data/table/column-header/useTableColumnResize.d.ts +19 -29
  112. package/esm/data/table/column-header/useTableColumnResize.js +24 -22
  113. package/esm/data/table/column-header/useTableColumnResize.js.map +1 -1
  114. package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.d.ts +1 -1
  115. package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.js +2 -2
  116. package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.js.map +1 -1
  117. package/esm/data/table/helpers/collectTableRowEntries.d.ts +9 -7
  118. package/esm/data/table/helpers/collectTableRowEntries.js +18 -10
  119. package/esm/data/table/helpers/collectTableRowEntries.js.map +1 -1
  120. package/esm/data/table/helpers/selection/getMultipleSelectProps.d.ts +13 -11
  121. package/esm/data/table/helpers/selection/getMultipleSelectProps.js +43 -53
  122. package/esm/data/table/helpers/selection/getMultipleSelectProps.js.map +1 -1
  123. package/esm/data/table/helpers/selection/getSingleSelectProps.d.ts +9 -8
  124. package/esm/data/table/helpers/selection/getSingleSelectProps.js +23 -10
  125. package/esm/data/table/helpers/selection/getSingleSelectProps.js.map +1 -1
  126. package/esm/data/table/helpers/selection/selection.types.d.ts +19 -19
  127. package/esm/data/table/helpers/selection/selection.utils.d.ts +21 -0
  128. package/esm/data/table/helpers/selection/selection.utils.js +43 -0
  129. package/esm/data/table/helpers/selection/selection.utils.js.map +1 -0
  130. package/esm/data/table/helpers/table-focus.d.ts +0 -3
  131. package/esm/data/table/helpers/table-focus.js +38 -8
  132. package/esm/data/table/helpers/table-focus.js.map +1 -1
  133. package/esm/data/table/hooks/useColumnOptions.d.ts +16 -5
  134. package/esm/data/table/hooks/useColumnOptions.js +26 -8
  135. package/esm/data/table/hooks/useColumnOptions.js.map +1 -1
  136. package/esm/data/table/hooks/useGridCache.js +2 -2
  137. package/esm/data/table/hooks/useGridCache.js.map +1 -1
  138. package/esm/data/table/hooks/useTableDetailsPanel.d.ts +10 -13
  139. package/esm/data/table/hooks/useTableDetailsPanel.js +7 -6
  140. package/esm/data/table/hooks/useTableDetailsPanel.js.map +1 -1
  141. package/esm/data/table/hooks/useTableItems.d.ts +31 -17
  142. package/esm/data/table/hooks/useTableItems.js +11 -18
  143. package/esm/data/table/hooks/useTableItems.js.map +1 -1
  144. package/esm/data/table/hooks/useTableKeyboardNav.d.ts +1 -6
  145. package/esm/data/table/hooks/useTableKeyboardNav.js +7 -6
  146. package/esm/data/table/hooks/useTableKeyboardNav.js.map +1 -1
  147. package/esm/data/table/hooks/useTableSelection.d.ts +6 -6
  148. package/esm/data/table/hooks/useTableSelection.js +13 -13
  149. package/esm/data/table/hooks/useTableSelection.js.map +1 -1
  150. package/esm/data/table/hooks/useTableSort.d.ts +2 -2
  151. package/esm/data/table/hooks/useTableSort.js +4 -5
  152. package/esm/data/table/hooks/useTableSort.js.map +1 -1
  153. package/esm/data/table/root/DataTable.types.d.ts +22 -13
  154. package/esm/data/table/root/DataTableRoot.context.d.ts +13 -7
  155. package/esm/data/table/root/DataTableRoot.context.js.map +1 -1
  156. package/esm/data/table/root/DataTableRoot.d.ts +49 -72
  157. package/esm/data/table/root/DataTableRoot.js +58 -74
  158. package/esm/data/table/root/DataTableRoot.js.map +1 -1
  159. package/esm/data/table/root/DataTableRoot.legacy.d.ts +2 -7
  160. package/esm/data/table/root/DataTableRoot.legacy.js +17 -3
  161. package/esm/data/table/root/DataTableRoot.legacy.js.map +1 -1
  162. package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.js +4 -4
  163. package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.js.map +1 -1
  164. package/esm/data/table/tbody/DataTableTbody.js +4 -2
  165. package/esm/data/table/tbody/DataTableTbody.js.map +1 -1
  166. package/esm/data/table/tr/DataTableTr.d.ts +5 -3
  167. package/esm/data/table/tr/DataTableTr.js +35 -23
  168. package/esm/data/table/tr/DataTableTr.js.map +1 -1
  169. package/esm/table/ColumnHeader.js +2 -1
  170. package/esm/table/ColumnHeader.js.map +1 -1
  171. package/package.json +8 -7
  172. package/src/data/data-grid/index.ts +3 -0
  173. package/src/data/data-grid/root/DataGridRoot.context.ts +16 -0
  174. package/src/data/data-grid/root/DataGridRoot.tsx +71 -0
  175. package/src/data/drag-and-drop/drag-handler/DragAndDropDragHandler.tsx +11 -17
  176. package/src/data/drag-and-drop/root/DragAndDrop.context.tsx +4 -2
  177. package/src/data/drag-and-drop/root/DragAndDropRoot.tsx +63 -52
  178. package/src/data/drag-and-drop/types.ts +0 -5
  179. package/src/data/stories/Data.test-data.tsx +52 -44
  180. package/src/data/table/agent-component-review.md +175 -0
  181. package/src/data/table/base-cell/DataTableBaseCell.tsx +31 -21
  182. package/src/data/table/column-header/DataTableColumnHeader.tsx +63 -58
  183. package/src/data/table/column-header/useTableColumnResize.ts +55 -71
  184. package/src/data/table/details-panel-row/DataTableDetailsPanelRow.tsx +7 -3
  185. package/src/data/table/helpers/collectTableRowEntries.ts +32 -19
  186. package/src/data/table/helpers/selection/getMultipleSelectProps.ts +65 -85
  187. package/src/data/table/helpers/selection/getSingleSelectProps.ts +35 -17
  188. package/src/data/table/helpers/selection/selection.types.ts +19 -19
  189. package/src/data/table/helpers/selection/selection.utils.test.ts +161 -0
  190. package/src/data/table/helpers/selection/selection.utils.ts +73 -0
  191. package/src/data/table/helpers/table-focus.ts +63 -9
  192. package/src/data/table/hooks/__tests__/useTableItems.test.ts +48 -8
  193. package/src/data/table/hooks/useColumnOptions.ts +48 -14
  194. package/src/data/table/hooks/useGridCache.ts +3 -2
  195. package/src/data/table/hooks/useTableDetailsPanel.tsx +25 -25
  196. package/src/data/table/hooks/useTableItems.ts +51 -42
  197. package/src/data/table/hooks/useTableKeyboardNav.ts +7 -15
  198. package/src/data/table/hooks/useTableSelection.ts +26 -31
  199. package/src/data/table/hooks/useTableSort.ts +10 -9
  200. package/src/data/table/root/DataTable.types.ts +30 -25
  201. package/src/data/table/root/DataTableRoot.context.ts +19 -7
  202. package/src/data/table/root/DataTableRoot.legacy.tsx +22 -14
  203. package/src/data/table/root/DataTableRoot.tsx +271 -320
  204. package/src/data/table/sub-row-toggle/DataTableSubRowToggle.tsx +5 -4
  205. package/src/data/table/tbody/DataTableTbody.tsx +6 -2
  206. package/src/data/table/tr/DataTableTr.tsx +98 -35
  207. package/src/table/ColumnHeader.tsx +2 -1
  208. package/cjs/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.d.ts +0 -22
  209. package/cjs/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.js +0 -35
  210. package/cjs/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.js.map +0 -1
  211. package/cjs/data/drag-and-drop-old/item/DataDragAndDropItem.d.ts +0 -27
  212. package/cjs/data/drag-and-drop-old/item/DataDragAndDropItem.js +0 -86
  213. package/cjs/data/drag-and-drop-old/item/DataDragAndDropItem.js.map +0 -1
  214. package/cjs/data/drag-and-drop-old/root/DataDragAndDrop.context.d.ts +0 -5
  215. package/cjs/data/drag-and-drop-old/root/DataDragAndDrop.context.js +0 -6
  216. package/cjs/data/drag-and-drop-old/root/DataDragAndDrop.context.js.map +0 -1
  217. package/cjs/data/drag-and-drop-old/root/DataDragAndDropRoot.d.ts +0 -24
  218. package/cjs/data/drag-and-drop-old/root/DataDragAndDropRoot.js +0 -108
  219. package/cjs/data/drag-and-drop-old/root/DataDragAndDropRoot.js.map +0 -1
  220. package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.d.ts +0 -46
  221. package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.js +0 -112
  222. package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.js.map +0 -1
  223. package/esm/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.d.ts +0 -22
  224. package/esm/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.js +0 -29
  225. package/esm/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.js.map +0 -1
  226. package/esm/data/drag-and-drop-old/item/DataDragAndDropItem.d.ts +0 -27
  227. package/esm/data/drag-and-drop-old/item/DataDragAndDropItem.js +0 -50
  228. package/esm/data/drag-and-drop-old/item/DataDragAndDropItem.js.map +0 -1
  229. package/esm/data/drag-and-drop-old/root/DataDragAndDrop.context.d.ts +0 -5
  230. package/esm/data/drag-and-drop-old/root/DataDragAndDrop.context.js +0 -3
  231. package/esm/data/drag-and-drop-old/root/DataDragAndDrop.context.js.map +0 -1
  232. package/esm/data/drag-and-drop-old/root/DataDragAndDropRoot.d.ts +0 -24
  233. package/esm/data/drag-and-drop-old/root/DataDragAndDropRoot.js +0 -68
  234. package/esm/data/drag-and-drop-old/root/DataDragAndDropRoot.js.map +0 -1
  235. package/esm/data/table/helpers/selection/SelectionSubtreeHelper.d.ts +0 -46
  236. package/esm/data/table/helpers/selection/SelectionSubtreeHelper.js +0 -109
  237. package/esm/data/table/helpers/selection/SelectionSubtreeHelper.js.map +0 -1
  238. package/src/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.tsx +0 -104
  239. package/src/data/drag-and-drop-old/item/DataDragAndDropItem.tsx +0 -74
  240. package/src/data/drag-and-drop-old/root/DataDragAndDrop.context.tsx +0 -11
  241. package/src/data/drag-and-drop-old/root/DataDragAndDropRoot.tsx +0 -96
  242. package/src/data/table/helpers/selection/SelectionSubtreeHelper.test.ts +0 -66
  243. package/src/data/table/helpers/selection/SelectionSubtreeHelper.ts +0 -162
  244. package/src/data/table/hooks/__tests__/useTableSelection.test.ts +0 -488
@@ -1,5 +1,6 @@
1
1
  import { renderHook } from "@testing-library/react";
2
2
  import { describe, expect, test } from "vitest";
3
+ import type { TableRowEntryId } from "../../root/DataTable.types";
3
4
  import { useTableItems } from "../useTableItems";
4
5
 
5
6
  type TestRow = {
@@ -45,6 +46,12 @@ const fallbackRows: FallbackTestRow[] = [
45
46
  },
46
47
  ];
47
48
 
49
+ const duplicatedRowObject: TestRow = {
50
+ id: "shared",
51
+ name: "Shared",
52
+ subRows: [{ id: "shared-child", name: "Child" }],
53
+ };
54
+
48
55
  const getSubRows = (row: TestRow) => row.subRows ?? [];
49
56
 
50
57
  const getVisibleIds = (rows: TestRow[]) => rows.map((row) => row.id);
@@ -59,16 +66,18 @@ describe("useTableItems", () => {
59
66
  );
60
67
 
61
68
  expect(getVisibleIds(result.current.items)).toEqual(["a", "b"]);
62
- expect(result.current.itemDetails.get(plainRows[0])).toMatchObject({
69
+ expect(result.current.itemDetails.get("a")).toMatchObject({
63
70
  id: "a",
71
+ rowData: plainRows[0],
64
72
  level: 0,
65
- parent: null,
73
+ parentId: null,
66
74
  children: [],
67
75
  });
68
- expect(result.current.itemDetails.get(plainRows[1])).toMatchObject({
76
+ expect(result.current.itemDetails.get("b")).toMatchObject({
69
77
  id: "b",
78
+ rowData: plainRows[1],
70
79
  level: 0,
71
- parent: null,
80
+ parentId: null,
72
81
  children: [],
73
82
  });
74
83
  });
@@ -102,14 +111,13 @@ describe("useTableItems", () => {
102
111
  expect(result.current.childRowIdsById.get("b")).toEqual(["b1"]);
103
112
  });
104
113
 
105
- test("uses the same fallback root id to reveal child rows when getRowId is omitted", () => {
114
+ test("uses unique fallback ids to reveal child rows when getRowId is omitted", () => {
106
115
  const { result } = renderHook(() =>
107
116
  useTableItems({
108
117
  items: fallbackRows,
109
- getRowId: (_, index) => index,
110
118
  subRows: {
111
119
  getRows: (row: any) => row.subRows ?? [],
112
- defaultExpandedRowIds: [0],
120
+ defaultExpandedRowIds: ["0"],
113
121
  },
114
122
  }),
115
123
  );
@@ -118,6 +126,7 @@ describe("useTableItems", () => {
118
126
  "Parent",
119
127
  "Child",
120
128
  ]);
129
+ expect(result.current.childRowIdsById.get("0")).toEqual(["0.0"]);
121
130
  });
122
131
 
123
132
  test("updates visible rows in depth-first order for controlled expanded ids", () => {
@@ -132,7 +141,7 @@ describe("useTableItems", () => {
132
141
  },
133
142
  }),
134
143
  {
135
- initialProps: { expandedIds: [] as (string | number)[] },
144
+ initialProps: { expandedIds: [] as TableRowEntryId[] },
136
145
  },
137
146
  );
138
147
 
@@ -149,4 +158,35 @@ describe("useTableItems", () => {
149
158
  "b1",
150
159
  ]);
151
160
  });
161
+
162
+ test("tracks duplicated row objects by row id instead of object identity", () => {
163
+ const { result } = renderHook(() =>
164
+ useTableItems({
165
+ items: [duplicatedRowObject, duplicatedRowObject],
166
+ subRows: {
167
+ getRows: getSubRows,
168
+ defaultExpandedRowIds: ["0"],
169
+ },
170
+ }),
171
+ );
172
+
173
+ expect(result.current.visibleRowIds).toEqual(["0", "0.0", "1"]);
174
+ expect(getVisibleIds(result.current.items)).toEqual([
175
+ "shared",
176
+ "shared-child",
177
+ "shared",
178
+ ]);
179
+ expect(result.current.itemDetails.get("0")).toMatchObject({
180
+ id: "0",
181
+ rowData: duplicatedRowObject,
182
+ parentId: null,
183
+ children: ["0.0"],
184
+ });
185
+ expect(result.current.itemDetails.get("1")).toMatchObject({
186
+ id: "1",
187
+ rowData: duplicatedRowObject,
188
+ parentId: null,
189
+ children: ["1.0"],
190
+ });
191
+ });
152
192
  });
@@ -3,38 +3,57 @@ import type {
3
3
  ColumnDefinition,
4
4
  ColumnDefinitions,
5
5
  } from "../root/DataTable.types";
6
- import type { SelectionProps } from "./useTableSelection";
6
+ import { ACTION_CELL_WIDTH } from "../tr/DataTableTr";
7
7
 
8
8
  type UseColumnOptions = {
9
9
  stickyColumns?: {
10
- first?: "1";
11
- last?: "1";
10
+ start?: "1";
11
+ end?: "1";
12
12
  };
13
- selectionMode: SelectionProps["selectionMode"];
13
+ hasSelection: boolean;
14
+ hasDetailsPanel: boolean;
15
+ layout: "fixed" | "auto";
16
+ };
17
+
18
+ type StickyStartState = {
19
+ selection: boolean;
20
+ expansion: boolean;
21
+ selectionOffset: number;
22
+ firstColumnOffset: number;
14
23
  };
15
24
 
16
25
  type UseColumnOptionsResult<T> = {
17
26
  columns: {
18
27
  isSticky: "start" | "end" | false;
28
+ isStickyLast?: boolean;
29
+ stickyLeftOffset?: number;
19
30
  colDef: ColumnDefinition<T>;
20
31
  }[];
21
- stickySelection: boolean;
32
+ stickyStart: StickyStartState;
33
+ totalColSpan: number;
22
34
  };
23
35
 
24
36
  function useColumnOptions<T>(
25
37
  columnDefinitions: ColumnDefinitions<T>,
26
38
  options: UseColumnOptions,
27
39
  ): UseColumnOptionsResult<T> {
28
- const { stickyColumns, selectionMode } = options;
40
+ const { stickyColumns, hasSelection, hasDetailsPanel, layout } = options;
41
+
42
+ const hasStickyStart = stickyColumns?.start === "1";
29
43
 
30
- const hasSelection = selectionMode !== "none";
44
+ const stickyExpansion = hasStickyStart && hasDetailsPanel;
45
+ const stickySelection = hasStickyStart && hasSelection;
46
+
47
+ const stickySelectionOffset = stickyExpansion ? ACTION_CELL_WIDTH : 0;
48
+ const stickyFirstColumnOffset =
49
+ (stickyExpansion ? ACTION_CELL_WIDTH : 0) +
50
+ (stickySelection ? ACTION_CELL_WIDTH : 0);
31
51
 
32
52
  const columns = useMemo(() => {
33
53
  return columnDefinitions.map((colDef, index) => {
34
- const isFirstSticky =
35
- stickyColumns?.first === "1" && index === 0 && !hasSelection;
54
+ const isFirstSticky = hasStickyStart && index === 0;
36
55
  const isLastSticky =
37
- stickyColumns?.last === "1" && index === columnDefinitions.length - 1;
56
+ stickyColumns?.end === "1" && index === columnDefinitions.length - 1;
38
57
 
39
58
  return {
40
59
  isSticky: isFirstSticky
@@ -42,21 +61,36 @@ function useColumnOptions<T>(
42
61
  : isLastSticky
43
62
  ? ("end" as const)
44
63
  : (false as const),
64
+ isStickyLast: isFirstSticky && !isLastSticky,
65
+ stickyLeftOffset: isFirstSticky ? stickyFirstColumnOffset : undefined,
45
66
  colDef,
46
67
  };
47
68
  });
48
69
  }, [
49
70
  columnDefinitions,
50
- hasSelection,
51
- stickyColumns?.first,
52
- stickyColumns?.last,
71
+ hasStickyStart,
72
+ stickyColumns,
73
+ stickyFirstColumnOffset,
53
74
  ]);
54
75
 
76
+ const totalColSpan =
77
+ columns.length +
78
+ (layout === "fixed" ? 1 : 0) +
79
+ (hasSelection ? 1 : 0) +
80
+ (hasDetailsPanel ? 1 : 0);
81
+
55
82
  return {
56
- stickySelection: selectionMode !== "none" && stickyColumns?.first === "1",
83
+ stickyStart: {
84
+ selection: stickySelection,
85
+ expansion: stickyExpansion,
86
+ selectionOffset: stickySelectionOffset,
87
+ firstColumnOffset: stickyFirstColumnOffset,
88
+ },
57
89
  columns,
90
+ totalColSpan,
58
91
  };
59
92
  }
60
93
 
61
94
  export { useColumnOptions };
95
+ export type { StickyStartState };
62
96
  export type { UseColumnOptionsResult };
@@ -14,7 +14,7 @@ function useGridCache(tableRef: HTMLTableElement | null, enabled: boolean) {
14
14
  });
15
15
 
16
16
  const [activeCell, setActiveCell] = useState<Element | null>(null);
17
- const activeCellRef = useValueAsRef(activeCell).current;
17
+ const activeCellRef = useValueAsRef(activeCell);
18
18
  const observerRef = useRef<MutationObserver | null>(null);
19
19
 
20
20
  useEffect(() => {
@@ -24,7 +24,8 @@ function useGridCache(tableRef: HTMLTableElement | null, enabled: boolean) {
24
24
 
25
25
  observerRef.current = new MutationObserver(() => {
26
26
  gridCacheRef.current.dirty = true;
27
- if (activeCellRef && !activeCellRef.isConnected) {
27
+
28
+ if (activeCellRef.current && !activeCellRef.current.isConnected) {
28
29
  setActiveCell(null);
29
30
  }
30
31
  });
@@ -1,14 +1,15 @@
1
1
  import React, { useCallback } from "react";
2
2
  import { createStrictContext } from "../../../utils/helpers";
3
3
  import { useControllableState } from "../../../utils/hooks";
4
- import { useTableItemsContext } from "./useTableItems";
4
+ import type { TableRowEntryId } from "../root/DataTable.types";
5
+ import { useDataTableContext } from "../root/DataTableRoot.context";
5
6
 
6
7
  type DetailsPanelProps<T> = {
7
8
  /**
8
- * Renders a details panel below the row when expanded.
9
+ * Function to get the content to show in the details panel for a given row.
9
10
  * When provided, an expand toggle column is added automatically.
10
11
  */
11
- getContent?: (rowData: T) => React.ReactNode;
12
+ getContent: (rowData: T) => React.ReactNode;
12
13
  /**
13
14
  * Determines whether a row can be expanded to show details panel content.
14
15
  * @default () => true
@@ -18,20 +19,16 @@ type DetailsPanelProps<T> = {
18
19
  * Controlled list of expanded row IDs.
19
20
  * Use with `onDetailsPanelChange` for controlled usage, or `defaultDetailsPanelRowIds` for uncontrolled.
20
21
  */
21
- expandedRowIds?: (string | number)[];
22
+ expandedRowIds?: TableRowEntryId[];
22
23
  /**
23
24
  * Initial list of expanded row IDs for uncontrolled usage.
24
25
  * @default []
25
26
  */
26
- defaultExpandedRowIds?: (string | number)[];
27
+ defaultExpandedRowIds?: TableRowEntryId[];
27
28
  /**
28
29
  * Called when the list of expanded row IDs changes.
29
- *
30
- *
31
- * TODO:
32
- * - Docs: This pattern is called "Master / Detail" in general terms
33
30
  */
34
- onExpandedRowIdsChange?: (ids: (string | number)[]) => void;
31
+ onExpandedRowIdsChange?: (ids: TableRowEntryId[]) => void; // TODO: Docs: This pattern is called "Master / Detail" in general terms
35
32
  /**
36
33
  * Returns the height (in px) or `"auto"` for a row's details panel.
37
34
  * When a number is returned, the panel scrolls within that fixed height.
@@ -46,9 +43,9 @@ type DetailsPanelProps<T> = {
46
43
  };
47
44
 
48
45
  type DataTableDetailsPanelContextT = {
49
- isExpanded: (id: string | number) => boolean;
50
- isDetailsPanelExpandable: (id: string | number) => boolean;
51
- toggleExpansion: (id: string | number) => void;
46
+ isExpanded: (id: TableRowEntryId) => boolean;
47
+ isDetailsPanelExpandable: (id: TableRowEntryId) => boolean;
48
+ toggleExpansion: (id: TableRowEntryId) => void;
52
49
  toggleAll: () => void;
53
50
  isAllExpanded: boolean;
54
51
  getDetailsPanelContent?: (row: unknown) => React.ReactNode;
@@ -68,7 +65,7 @@ const {
68
65
 
69
66
  function DataTableDetailsPanelProvider<T>({
70
67
  children,
71
- detailsPanel = {},
68
+ detailsPanel,
72
69
  }: { detailsPanel?: DetailsPanelProps<T> } & { children: React.ReactNode }) {
73
70
  const {
74
71
  expandedRowIds,
@@ -78,7 +75,7 @@ function DataTableDetailsPanelProvider<T>({
78
75
  isRowExpandable,
79
76
  getHeight,
80
77
  showExpandAll = false,
81
- } = detailsPanel;
78
+ } = detailsPanel || {};
82
79
 
83
80
  const [expandedIds, setExpandedIds] = useControllableState({
84
81
  value: expandedRowIds,
@@ -87,20 +84,23 @@ function DataTableDetailsPanelProvider<T>({
87
84
  });
88
85
 
89
86
  /* TODO: False is just fallback until auto and root is merged */
90
- const tableItemsContext = useTableItemsContext(false);
87
+ const tableContext = useDataTableContext(false);
91
88
 
92
- const { itemDetails } = tableItemsContext ?? {
93
- itemDetails: new Map(),
89
+ const { itemDetails } = tableContext?.tableItems ?? {
90
+ itemDetails: new Map<
91
+ TableRowEntryId,
92
+ { rowData: T; id: TableRowEntryId; level: number }
93
+ >(),
94
94
  };
95
95
 
96
96
  const expandableIds = React.useMemo(() => {
97
97
  if (!getContent) {
98
- return new Set<string | number>();
98
+ return new Set<TableRowEntryId>();
99
99
  }
100
100
 
101
- const ids = new Set<string | number>();
101
+ const ids = new Set<TableRowEntryId>();
102
102
 
103
- for (const [rowData, { id, level }] of itemDetails.entries()) {
103
+ for (const { rowData, id, level } of itemDetails.values()) {
104
104
  /* We only allow Master - Details pattern on top level rows */
105
105
  if (level > 0) {
106
106
  continue;
@@ -115,18 +115,18 @@ function DataTableDetailsPanelProvider<T>({
115
115
  }, [getContent, isRowExpandable, itemDetails]);
116
116
 
117
117
  const isDetailsPanelExpandableById = useCallback(
118
- (id: string | number) => expandableIds.has(id),
118
+ (id: TableRowEntryId) => expandableIds.has(id),
119
119
  [expandableIds],
120
120
  );
121
121
 
122
122
  const isExpanded = useCallback(
123
- (id: string | number) =>
123
+ (id: TableRowEntryId) =>
124
124
  isDetailsPanelExpandableById(id) && expandedIds.includes(id),
125
125
  [expandedIds, isDetailsPanelExpandableById],
126
126
  );
127
127
 
128
128
  const toggleExpansion = useCallback(
129
- (id: string | number) => {
129
+ (id: TableRowEntryId) => {
130
130
  if (!isDetailsPanelExpandableById(id)) {
131
131
  return;
132
132
  }
@@ -169,7 +169,7 @@ function DataTableDetailsPanelProvider<T>({
169
169
  );
170
170
  }
171
171
 
172
- function getDataTableDetailsPanelId(tableId: string, rowId: string | number) {
172
+ function getDataTableDetailsPanelId(tableId: string, rowId: TableRowEntryId) {
173
173
  return `${tableId}-expansion-${rowId}`;
174
174
  }
175
175
 
@@ -1,39 +1,55 @@
1
1
  import { useCallback, useMemo } from "react";
2
- import { createStrictContext } from "../../../utils/helpers";
3
2
  import { useControllableState } from "../../../utils/hooks";
4
3
  import {
5
4
  type ItemDetail,
6
- type TableRowEntryId,
7
5
  collectTableRowEntries,
8
6
  } from "../helpers/collectTableRowEntries";
7
+ import type { TableRowEntryId } from "../root/DataTable.types";
9
8
 
10
9
  type SubRowsProps<T> = {
11
- getRows?: (rowData: T) => T[];
12
- expandedRowIds?: (string | number)[];
13
- defaultExpandedRowIds?: (string | number)[];
10
+ /**
11
+ * Function to get sub-rows for a given row.
12
+ */
13
+ getRows: (rowData: T) => T[];
14
+ /**
15
+ * Controlled list of IDs of rows that should be expanded.
16
+ */
17
+ expandedRowIds?: TableRowEntryId[];
18
+ /**
19
+ * IDs of rows that should be initially expanded.
20
+ * Only used when `expandedRowIds` is not provided, i.e. when the expanded state is uncontrolled.
21
+ */
22
+ defaultExpandedRowIds?: TableRowEntryId[];
23
+ /**
24
+ * Called when the list of expanded row IDs changes.
25
+ */
26
+ onExpandedRowIdsChange?: (ids: TableRowEntryId[]) => void;
27
+ /**
28
+ * Function to get whether a row should be expandable.
29
+ * By default, all rows are expandable when `getRows` is provided.
30
+ */
14
31
  isRowExpandable?: (rowData: T) => boolean;
15
- onExpandedRowIdsChange?: (ids: (string | number)[]) => void;
16
32
  };
17
33
 
18
34
  type UseTableItemsArgs<T> = {
19
35
  items: T[];
20
- getRowId: (rowData: T, index: number) => TableRowEntryId;
36
+ getRowId?: (rowData: T) => TableRowEntryId;
21
37
  subRows?: SubRowsProps<T>;
22
38
  };
23
39
 
24
- type useTableItemsReturn<T> = {
40
+ type UseTableItemsReturn<T> = {
25
41
  items: T[];
26
- itemDetails: Map<T, ItemDetail<T>>;
42
+ itemDetails: Map<TableRowEntryId, ItemDetail<T>>;
27
43
  /** Row ids for the rows currently rendered in the table body. */
28
44
  visibleRowIds: TableRowEntryId[];
29
45
  /** Direct child ids for each row, used to traverse selection groups lazily. */
30
46
  childRowIdsById: Map<TableRowEntryId, TableRowEntryId[]>;
31
- onExpandedRowIdsChange: (id: string | number) => void;
32
- isSubRowExpanded: (id: string | number) => boolean;
47
+ onExpandedRowIdsChange: (id: TableRowEntryId) => void;
48
+ isSubRowExpanded: (id: TableRowEntryId) => boolean;
33
49
  };
34
50
 
35
- function useTableItems<T>(args: UseTableItemsArgs<T>): useTableItemsReturn<T> {
36
- const { items, subRows = {}, getRowId } = args;
51
+ function useTableItems<T>(args: UseTableItemsArgs<T>): UseTableItemsReturn<T> {
52
+ const { items, subRows, getRowId } = args;
37
53
 
38
54
  const {
39
55
  expandedRowIds,
@@ -41,7 +57,7 @@ function useTableItems<T>(args: UseTableItemsArgs<T>): useTableItemsReturn<T> {
41
57
  getRows,
42
58
  onExpandedRowIdsChange,
43
59
  isRowExpandable,
44
- } = subRows;
60
+ } = subRows || {};
45
61
 
46
62
  const [nestedSubRowsExpandedIds, setNestedSubRowsExpandedIds] =
47
63
  useControllableState({
@@ -57,32 +73,35 @@ function useTableItems<T>(args: UseTableItemsArgs<T>): useTableItemsReturn<T> {
57
73
 
58
74
  const { itemDetails, visibleItems, visibleRowIds, childRowIdsById } =
59
75
  useMemo(() => {
60
- const { itemDetails: rowEntriesMap, childRowIdsById: _childRowIdsById } =
61
- collectTableRowEntries({
62
- items,
63
- getRowId,
64
- getRows,
65
- isRowExpandable,
66
- });
76
+ const {
77
+ itemDetails: rowEntriesMap,
78
+ rootRowIds,
79
+ childRowIdsById: _childRowIdsById,
80
+ } = collectTableRowEntries({
81
+ items,
82
+ getRowId,
83
+ getRows,
84
+ isRowExpandable,
85
+ });
67
86
 
68
87
  const localVisibleItems: T[] = [];
69
88
  const localVisibleRowIds: TableRowEntryId[] = [];
70
89
 
71
- const addVisibleRows = (rowData: T): TableRowEntryId[] => {
72
- const details = rowEntriesMap.get(rowData);
90
+ const addVisibleRows = (rowId: TableRowEntryId): TableRowEntryId[] => {
91
+ const details = rowEntriesMap.get(rowId);
73
92
 
74
93
  if (!details) {
75
94
  return [];
76
95
  }
77
96
 
78
- localVisibleItems.push(rowData);
97
+ localVisibleItems.push(details.rowData);
79
98
  localVisibleRowIds.push(details.id);
80
99
 
81
100
  const visibleDescendantRowIds: TableRowEntryId[] = [];
82
101
 
83
102
  if (expandedIdsSet.has(details.id)) {
84
- for (const childRow of details.children) {
85
- const childVisibleRowIds = addVisibleRows(childRow);
103
+ for (const childRowId of details.children) {
104
+ const childVisibleRowIds = addVisibleRows(childRowId);
86
105
  visibleDescendantRowIds.push(...childVisibleRowIds);
87
106
  }
88
107
  }
@@ -90,8 +109,8 @@ function useTableItems<T>(args: UseTableItemsArgs<T>): useTableItemsReturn<T> {
90
109
  return [details.id, ...visibleDescendantRowIds];
91
110
  };
92
111
 
93
- for (const rowData of items) {
94
- addVisibleRows(rowData);
112
+ for (const rowId of rootRowIds) {
113
+ addVisibleRows(rowId);
95
114
  }
96
115
 
97
116
  return {
@@ -103,7 +122,7 @@ function useTableItems<T>(args: UseTableItemsArgs<T>): useTableItemsReturn<T> {
103
122
  }, [getRows, items, getRowId, isRowExpandable, expandedIdsSet]);
104
123
 
105
124
  const handleExpandedSubRowIdChange = useCallback(
106
- (id: string | number) => {
125
+ (id: TableRowEntryId) => {
107
126
  setNestedSubRowsExpandedIds((prev) =>
108
127
  prev.includes(id)
109
128
  ? prev.filter((expandedId) => expandedId !== id)
@@ -119,19 +138,9 @@ function useTableItems<T>(args: UseTableItemsArgs<T>): useTableItemsReturn<T> {
119
138
  visibleRowIds,
120
139
  childRowIdsById,
121
140
  onExpandedRowIdsChange: handleExpandedSubRowIdChange,
122
- isSubRowExpanded: (id: string | number) => expandedIdsSet.has(id),
141
+ isSubRowExpanded: (id: TableRowEntryId) => expandedIdsSet.has(id),
123
142
  };
124
143
  }
125
144
 
126
- const { Provider: TableItemsProvider, useContext: useTableItemsContext } =
127
- /* TODO: Can we type this better? */
128
- createStrictContext<
129
- Omit<useTableItemsReturn<any>, "visibleRowIds" | "childRowIdsById">
130
- >({
131
- name: "TableItemsContext",
132
- errorMessage:
133
- "useTableItemsContext must be used within a TableItemsProvider",
134
- });
135
-
136
- export { useTableItems, TableItemsProvider, useTableItemsContext };
137
- export type { ItemDetail, SubRowsProps };
145
+ export { useTableItems };
146
+ export type { ItemDetail, SubRowsProps, UseTableItemsReturn };
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import { useEventCallback } from "../../../utils/hooks";
3
3
  import { focusInitialTableTarget } from "../helpers/table-cell";
4
- import { focusCellAndUpdateTabIndex } from "../helpers/table-focus";
4
+ import { focusCell, focusCellAndUpdateTabIndex } from "../helpers/table-focus";
5
5
  import {
6
6
  findFirstCell,
7
7
  findFirstCellInRow,
@@ -18,17 +18,9 @@ import { useGridCache } from "./useGridCache";
18
18
 
19
19
  type UseTableKeyboardNavOptions = {
20
20
  enabled: boolean;
21
- /**
22
- * Custom callback to determine if navigation should be blocked.
23
- * Called before default blocking logic.
24
- */
25
- shouldBlockNavigation?: (event: KeyboardEvent) => boolean;
26
21
  };
27
22
 
28
- function useTableKeyboardNav({
29
- enabled,
30
- shouldBlockNavigation: customBlockFn,
31
- }: UseTableKeyboardNavOptions) {
23
+ function useTableKeyboardNav({ enabled }: UseTableKeyboardNavOptions) {
32
24
  const [tableRef, setTableRef] = useState<HTMLTableElement | null>(null);
33
25
  const { getTableGrid, activeCell, setActiveCell } = useGridCache(
34
26
  tableRef,
@@ -110,10 +102,6 @@ function useTableKeyboardNav({
110
102
  * Checks if navigation should be blocked based on current focus context.
111
103
  */
112
104
  const handleTableKeyDown = useEventCallback((event: KeyboardEvent): void => {
113
- if (customBlockFn?.(event)) {
114
- return;
115
- }
116
-
117
105
  const action = getNavigationAction(event);
118
106
  if (!action) {
119
107
  return;
@@ -137,6 +125,10 @@ function useTableKeyboardNav({
137
125
  const target = event.target as Element | null;
138
126
 
139
127
  if (tableRef && target === tableRef) {
128
+ if (activeCell) {
129
+ focusCell(activeCell);
130
+ return;
131
+ }
140
132
  focusInitialTableTarget(tableRef);
141
133
  return;
142
134
  }
@@ -173,7 +165,7 @@ function useTableKeyboardNav({
173
165
 
174
166
  return {
175
167
  /* Table should only have tabIndex until the focus is moved inside and is enabled */
176
- tabIndex: enabled ? (activeCell ? undefined : 0) : undefined,
168
+ tabIndex: enabled ? 0 : undefined,
177
169
  setTableRef,
178
170
  };
179
171
  }