@navikt/ds-react 8.10.2 → 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.
- package/cjs/action-menu/ActionMenu.js +1 -1
- package/cjs/action-menu/ActionMenu.js.map +1 -1
- package/cjs/data/stories/Data.test-data.d.ts +24 -0
- package/cjs/data/stories/Data.test-data.js +1616 -0
- package/cjs/data/stories/Data.test-data.js.map +1 -0
- package/cjs/data/table/column-header/DataTableColumnHeader.d.ts +4 -1
- package/cjs/data/table/column-header/DataTableColumnHeader.js +4 -2
- package/cjs/data/table/column-header/DataTableColumnHeader.js.map +1 -1
- package/cjs/data/table/column-header/useTableColumnResize.d.ts +39 -14
- package/cjs/data/table/column-header/useTableColumnResize.js +37 -39
- package/cjs/data/table/column-header/useTableColumnResize.js.map +1 -1
- package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.d.ts +6 -0
- package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.js +32 -0
- package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.js.map +1 -0
- package/cjs/data/table/helpers/collectTableRowEntries.d.ts +13 -5
- package/cjs/data/table/helpers/collectTableRowEntries.js +26 -19
- package/cjs/data/table/helpers/collectTableRowEntries.js.map +1 -1
- package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.d.ts +46 -0
- package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.js +112 -0
- package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.js.map +1 -0
- package/cjs/data/table/helpers/selection/getMultipleSelectProps.d.ts +3 -2
- package/cjs/data/table/helpers/selection/getMultipleSelectProps.js +43 -19
- package/cjs/data/table/helpers/selection/getMultipleSelectProps.js.map +1 -1
- package/cjs/data/table/helpers/selection/selection.types.d.ts +1 -0
- package/cjs/data/table/helpers/table-keyboard.d.ts +1 -2
- package/cjs/data/table/helpers/table-keyboard.js +1 -2
- package/cjs/data/table/helpers/table-keyboard.js.map +1 -1
- package/cjs/data/table/hooks/useColumnOptions.js +18 -5
- package/cjs/data/table/hooks/useColumnOptions.js.map +1 -1
- package/cjs/data/table/hooks/useTableDetailsPanel.d.ts +62 -0
- package/cjs/data/table/hooks/{useTableExpansion.js → useTableDetailsPanel.js} +26 -19
- package/cjs/data/table/hooks/useTableDetailsPanel.js.map +1 -0
- package/cjs/data/table/hooks/useTableItems.d.ts +18 -17
- package/cjs/data/table/hooks/useTableItems.js +27 -15
- package/cjs/data/table/hooks/useTableItems.js.map +1 -1
- package/cjs/data/table/hooks/useTableSelection.d.ts +6 -3
- package/cjs/data/table/hooks/useTableSelection.js +10 -4
- package/cjs/data/table/hooks/useTableSelection.js.map +1 -1
- package/cjs/data/table/index.d.ts +1 -2
- package/cjs/data/table/index.js +22 -12
- package/cjs/data/table/index.js.map +1 -1
- package/cjs/data/table/root/DataTable.types.d.ts +12 -10
- package/cjs/data/table/root/DataTableRoot.context.d.ts +5 -1
- package/cjs/data/table/root/DataTableRoot.context.js.map +1 -1
- package/cjs/data/table/root/DataTableRoot.d.ts +79 -115
- package/cjs/data/table/root/DataTableRoot.js +167 -39
- package/cjs/data/table/root/DataTableRoot.js.map +1 -1
- package/cjs/data/table/root/DataTableRoot.legacy.d.ts +177 -0
- package/cjs/data/table/root/DataTableRoot.legacy.js +104 -0
- package/cjs/data/table/root/DataTableRoot.legacy.js.map +1 -0
- package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.d.ts +6 -0
- package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.js +21 -0
- package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.js.map +1 -0
- package/cjs/data/table/tr/DataTableTr.js +11 -11
- package/cjs/data/table/tr/DataTableTr.js.map +1 -1
- package/cjs/form/checkbox/Checkbox.js +1 -0
- package/cjs/form/checkbox/Checkbox.js.map +1 -1
- package/cjs/form/radio/Radio.js +7 -1
- package/cjs/form/radio/Radio.js.map +1 -1
- package/cjs/modal/types.d.ts +8 -4
- package/cjs/utils/components/dismissablelayer/DismissableLayer.js +1 -1
- package/cjs/utils/components/dismissablelayer/DismissableLayer.js.map +1 -1
- package/cjs/utils/components/floating/Floating.d.ts +16 -1
- package/cjs/utils/components/floating/Floating.js +50 -13
- package/cjs/utils/components/floating/Floating.js.map +1 -1
- package/cjs/utils/components/floating-menu/Menu.js +1 -1
- package/cjs/utils/components/floating-menu/Menu.js.map +1 -1
- package/cjs/utils/helpers/create-strict-context.js +1 -1
- package/cjs/utils/helpers/create-strict-context.js.map +1 -1
- package/cjs/utils/hooks/useControllableState.d.ts +5 -5
- package/cjs/utils/hooks/useControllableState.js.map +1 -1
- package/cjs/utils/hooks/useValueAsRef.js +1 -1
- package/cjs/utils/hooks/useValueAsRef.js.map +1 -1
- package/cjs/utils-external/hooks/useId.js +1 -1
- package/cjs/utils-external/hooks/useId.js.map +1 -1
- package/esm/action-menu/ActionMenu.js +1 -1
- package/esm/action-menu/ActionMenu.js.map +1 -1
- package/esm/data/stories/Data.test-data.d.ts +24 -0
- package/esm/data/stories/Data.test-data.js +1607 -0
- package/esm/data/stories/Data.test-data.js.map +1 -0
- package/esm/data/table/column-header/DataTableColumnHeader.d.ts +4 -1
- package/esm/data/table/column-header/DataTableColumnHeader.js +4 -2
- package/esm/data/table/column-header/DataTableColumnHeader.js.map +1 -1
- package/esm/data/table/column-header/useTableColumnResize.d.ts +39 -14
- package/esm/data/table/column-header/useTableColumnResize.js +38 -40
- package/esm/data/table/column-header/useTableColumnResize.js.map +1 -1
- package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.d.ts +6 -0
- package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.js +27 -0
- package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.js.map +1 -0
- package/esm/data/table/helpers/collectTableRowEntries.d.ts +13 -5
- package/esm/data/table/helpers/collectTableRowEntries.js +26 -19
- package/esm/data/table/helpers/collectTableRowEntries.js.map +1 -1
- package/esm/data/table/helpers/selection/SelectionSubtreeHelper.d.ts +46 -0
- package/esm/data/table/helpers/selection/SelectionSubtreeHelper.js +109 -0
- package/esm/data/table/helpers/selection/SelectionSubtreeHelper.js.map +1 -0
- package/esm/data/table/helpers/selection/getMultipleSelectProps.d.ts +3 -2
- package/esm/data/table/helpers/selection/getMultipleSelectProps.js +43 -19
- package/esm/data/table/helpers/selection/getMultipleSelectProps.js.map +1 -1
- package/esm/data/table/helpers/selection/selection.types.d.ts +1 -0
- package/esm/data/table/helpers/table-keyboard.d.ts +1 -2
- package/esm/data/table/helpers/table-keyboard.js +1 -2
- package/esm/data/table/helpers/table-keyboard.js.map +1 -1
- package/esm/data/table/hooks/useColumnOptions.js +18 -5
- package/esm/data/table/hooks/useColumnOptions.js.map +1 -1
- package/esm/data/table/hooks/useTableDetailsPanel.d.ts +62 -0
- package/esm/data/table/hooks/useTableDetailsPanel.js +58 -0
- package/esm/data/table/hooks/useTableDetailsPanel.js.map +1 -0
- package/esm/data/table/hooks/useTableItems.d.ts +18 -17
- package/esm/data/table/hooks/useTableItems.js +27 -15
- package/esm/data/table/hooks/useTableItems.js.map +1 -1
- package/esm/data/table/hooks/useTableSelection.d.ts +6 -3
- package/esm/data/table/hooks/useTableSelection.js +10 -4
- package/esm/data/table/hooks/useTableSelection.js.map +1 -1
- package/esm/data/table/index.d.ts +1 -2
- package/esm/data/table/index.js +21 -1
- package/esm/data/table/index.js.map +1 -1
- package/esm/data/table/root/DataTable.types.d.ts +12 -10
- package/esm/data/table/root/DataTableRoot.context.d.ts +5 -1
- package/esm/data/table/root/DataTableRoot.context.js.map +1 -1
- package/esm/data/table/root/DataTableRoot.d.ts +79 -115
- package/esm/data/table/root/DataTableRoot.js +174 -37
- package/esm/data/table/root/DataTableRoot.js.map +1 -1
- package/esm/data/table/root/DataTableRoot.legacy.d.ts +177 -0
- package/esm/data/table/root/DataTableRoot.legacy.js +59 -0
- package/esm/data/table/root/DataTableRoot.legacy.js.map +1 -0
- package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.d.ts +6 -0
- package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.js +16 -0
- package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.js.map +1 -0
- package/esm/data/table/tr/DataTableTr.js +11 -11
- package/esm/data/table/tr/DataTableTr.js.map +1 -1
- package/esm/form/checkbox/Checkbox.js +1 -0
- package/esm/form/checkbox/Checkbox.js.map +1 -1
- package/esm/form/radio/Radio.js +7 -1
- package/esm/form/radio/Radio.js.map +1 -1
- package/esm/modal/types.d.ts +8 -4
- package/esm/utils/components/dismissablelayer/DismissableLayer.js +1 -1
- package/esm/utils/components/dismissablelayer/DismissableLayer.js.map +1 -1
- package/esm/utils/components/floating/Floating.d.ts +16 -1
- package/esm/utils/components/floating/Floating.js +48 -13
- package/esm/utils/components/floating/Floating.js.map +1 -1
- package/esm/utils/components/floating-menu/Menu.js +2 -2
- package/esm/utils/components/floating-menu/Menu.js.map +1 -1
- package/esm/utils/helpers/create-strict-context.js +1 -1
- package/esm/utils/helpers/create-strict-context.js.map +1 -1
- package/esm/utils/hooks/useControllableState.d.ts +5 -5
- package/esm/utils/hooks/useControllableState.js.map +1 -1
- package/esm/utils/hooks/useValueAsRef.js +1 -1
- package/esm/utils/hooks/useValueAsRef.js.map +1 -1
- package/esm/utils-external/hooks/useId.js +1 -1
- package/esm/utils-external/hooks/useId.js.map +1 -1
- package/package.json +3 -3
- package/src/action-menu/ActionMenu.tsx +1 -1
- package/src/data/stories/Data.test-data.tsx +1703 -0
- package/src/data/table/column-header/DataTableColumnHeader.tsx +11 -7
- package/src/data/table/column-header/useTableColumnResize.ts +95 -54
- package/src/data/table/details-panel-row/DataTableDetailsPanelRow.tsx +53 -0
- package/src/data/table/helpers/collectTableRowEntries.ts +55 -31
- package/src/data/table/helpers/selection/SelectionSubtreeHelper.test.ts +66 -0
- package/src/data/table/helpers/selection/SelectionSubtreeHelper.ts +162 -0
- package/src/data/table/helpers/selection/getMultipleSelectProps.ts +57 -20
- package/src/data/table/helpers/selection/selection.types.ts +1 -0
- package/src/data/table/helpers/table-keyboard.ts +1 -2
- package/src/data/table/hooks/__tests__/useTableItems.test.ts +27 -6
- package/src/data/table/hooks/__tests__/useTableSelection.test.ts +182 -58
- package/src/data/table/hooks/useColumnOptions.ts +19 -5
- package/src/data/table/hooks/useTableDetailsPanel.tsx +182 -0
- package/src/data/table/hooks/useTableItems.ts +74 -60
- package/src/data/table/hooks/useTableSelection.ts +27 -12
- package/src/data/table/index.tsx +5 -3
- package/src/data/table/root/DataTable.types.ts +25 -10
- package/src/data/table/root/DataTableRoot.context.ts +5 -1
- package/src/data/table/root/DataTableRoot.legacy.tsx +297 -0
- package/src/data/table/root/DataTableRoot.tsx +483 -219
- package/src/data/table/sub-row-toggle/DataTableSubRowToggle.tsx +39 -0
- package/src/data/table/tr/DataTableTr.tsx +14 -13
- package/src/form/checkbox/Checkbox.tsx +1 -0
- package/src/form/radio/Radio.tsx +7 -1
- package/src/modal/types.ts +8 -4
- package/src/utils/components/dismissablelayer/DismissableLayer.tsx +1 -1
- package/src/utils/components/floating/Floating.tsx +56 -13
- package/src/utils/components/floating-menu/Menu.tsx +4 -1
- package/src/utils/helpers/create-strict-context.tsx +1 -1
- package/src/utils/hooks/useControllableState.ts +11 -8
- package/src/utils/hooks/useValueAsRef.ts +1 -1
- package/src/utils-external/hooks/useId.ts +1 -1
- package/cjs/data/table/hooks/useTableExpansion.d.ts +0 -29
- package/cjs/data/table/hooks/useTableExpansion.js.map +0 -1
- package/cjs/data/table/root/DataTableAuto.d.ts +0 -174
- package/cjs/data/table/root/DataTableAuto.js +0 -206
- package/cjs/data/table/root/DataTableAuto.js.map +0 -1
- package/esm/data/table/hooks/useTableExpansion.d.ts +0 -29
- package/esm/data/table/hooks/useTableExpansion.js +0 -51
- package/esm/data/table/hooks/useTableExpansion.js.map +0 -1
- package/esm/data/table/root/DataTableAuto.d.ts +0 -174
- package/esm/data/table/root/DataTableAuto.js +0 -170
- package/esm/data/table/root/DataTableAuto.js.map +0 -1
- package/src/data/table/hooks/__tests__/useTableExpansion.test.tsx +0 -115
- package/src/data/table/hooks/useTableExpansion.tsx +0 -141
- package/src/data/table/root/DataTableAuto.test.tsx +0 -118
- package/src/data/table/root/DataTableAuto.tsx +0 -603
|
@@ -17,7 +17,13 @@ const items: Item[] = [
|
|
|
17
17
|
{ id: "c", name: "Charlie" },
|
|
18
18
|
];
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const visibleRowIds = items.map((item) => item.id);
|
|
21
|
+
const childRowIdsById = new Map<string | number, (string | number)[]>([
|
|
22
|
+
["a", ["a1", "a2"]],
|
|
23
|
+
["a1", []],
|
|
24
|
+
["a2", ["a2a"]],
|
|
25
|
+
["a2a", []],
|
|
26
|
+
]);
|
|
21
27
|
|
|
22
28
|
function asSingle(result: {
|
|
23
29
|
current: UseTableSelectionReturn;
|
|
@@ -36,8 +42,8 @@ describe("useTableSelection", () => {
|
|
|
36
42
|
test("returns empty selectedKeys and no prop getters", () => {
|
|
37
43
|
const { result } = renderHook(() =>
|
|
38
44
|
useTableSelection({
|
|
39
|
-
selectionMode: "none",
|
|
40
|
-
|
|
45
|
+
selection: { selectionMode: "none" },
|
|
46
|
+
visibleRowIds,
|
|
41
47
|
}),
|
|
42
48
|
);
|
|
43
49
|
|
|
@@ -50,8 +56,8 @@ describe("useTableSelection", () => {
|
|
|
50
56
|
test("returns getRowRadioProps", () => {
|
|
51
57
|
const { result } = renderHook(() =>
|
|
52
58
|
useTableSelection({
|
|
53
|
-
selectionMode: "single",
|
|
54
|
-
|
|
59
|
+
selection: { selectionMode: "single" },
|
|
60
|
+
visibleRowIds,
|
|
55
61
|
}),
|
|
56
62
|
);
|
|
57
63
|
|
|
@@ -63,9 +69,8 @@ describe("useTableSelection", () => {
|
|
|
63
69
|
const onChange = vi.fn();
|
|
64
70
|
const { result } = renderHook(() =>
|
|
65
71
|
useTableSelection({
|
|
66
|
-
selectionMode: "single",
|
|
67
|
-
|
|
68
|
-
onSelectionChange: onChange,
|
|
72
|
+
selection: { selectionMode: "single", onSelectionChange: onChange },
|
|
73
|
+
visibleRowIds,
|
|
69
74
|
}),
|
|
70
75
|
);
|
|
71
76
|
|
|
@@ -82,9 +87,8 @@ describe("useTableSelection", () => {
|
|
|
82
87
|
test("toggling the same row keeps it selected", () => {
|
|
83
88
|
const { result } = renderHook(() =>
|
|
84
89
|
useTableSelection({
|
|
85
|
-
selectionMode: "single",
|
|
86
|
-
|
|
87
|
-
defaultSelectedKeys: ["a"],
|
|
90
|
+
selection: { selectionMode: "single", defaultSelectedKeys: ["a"] },
|
|
91
|
+
visibleRowIds,
|
|
88
92
|
}),
|
|
89
93
|
);
|
|
90
94
|
|
|
@@ -100,9 +104,8 @@ describe("useTableSelection", () => {
|
|
|
100
104
|
test("selecting a new row replaces the previous", () => {
|
|
101
105
|
const { result } = renderHook(() =>
|
|
102
106
|
useTableSelection({
|
|
103
|
-
selectionMode: "single",
|
|
104
|
-
|
|
105
|
-
defaultSelectedKeys: ["a"],
|
|
107
|
+
selection: { selectionMode: "single", defaultSelectedKeys: ["a"] },
|
|
108
|
+
visibleRowIds,
|
|
106
109
|
}),
|
|
107
110
|
);
|
|
108
111
|
|
|
@@ -118,9 +121,8 @@ describe("useTableSelection", () => {
|
|
|
118
121
|
test("disabled rows have disabled prop", () => {
|
|
119
122
|
const { result } = renderHook(() =>
|
|
120
123
|
useTableSelection({
|
|
121
|
-
selectionMode: "single",
|
|
122
|
-
|
|
123
|
-
disabledSelectionKeys: ["b"],
|
|
124
|
+
selection: { selectionMode: "single", disabledSelectionKeys: ["b"] },
|
|
125
|
+
visibleRowIds,
|
|
124
126
|
}),
|
|
125
127
|
);
|
|
126
128
|
|
|
@@ -132,9 +134,8 @@ describe("useTableSelection", () => {
|
|
|
132
134
|
const { result, rerender } = renderHook(
|
|
133
135
|
({ selectedKeys }) =>
|
|
134
136
|
useTableSelection({
|
|
135
|
-
selectionMode: "single",
|
|
136
|
-
|
|
137
|
-
selectedKeys,
|
|
137
|
+
selection: { selectionMode: "single", selectedKeys },
|
|
138
|
+
visibleRowIds,
|
|
138
139
|
}),
|
|
139
140
|
{ initialProps: { selectedKeys: ["a"] as (string | number)[] } },
|
|
140
141
|
);
|
|
@@ -150,8 +151,8 @@ describe("useTableSelection", () => {
|
|
|
150
151
|
test("returns getTheadCheckboxProps and getRowCheckboxProps", () => {
|
|
151
152
|
const { result } = renderHook(() =>
|
|
152
153
|
useTableSelection({
|
|
153
|
-
selectionMode: "multiple",
|
|
154
|
-
|
|
154
|
+
selection: { selectionMode: "multiple" },
|
|
155
|
+
visibleRowIds,
|
|
155
156
|
}),
|
|
156
157
|
);
|
|
157
158
|
|
|
@@ -163,8 +164,8 @@ describe("useTableSelection", () => {
|
|
|
163
164
|
test("selecting individual rows", () => {
|
|
164
165
|
const { result } = renderHook(() =>
|
|
165
166
|
useTableSelection({
|
|
166
|
-
selectionMode: "multiple",
|
|
167
|
-
|
|
167
|
+
selection: { selectionMode: "multiple" },
|
|
168
|
+
visibleRowIds,
|
|
168
169
|
}),
|
|
169
170
|
);
|
|
170
171
|
|
|
@@ -188,9 +189,11 @@ describe("useTableSelection", () => {
|
|
|
188
189
|
test("deselecting a row", () => {
|
|
189
190
|
const { result } = renderHook(() =>
|
|
190
191
|
useTableSelection({
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
selection: {
|
|
193
|
+
selectionMode: "multiple",
|
|
194
|
+
defaultSelectedKeys: ["a", "b"],
|
|
195
|
+
},
|
|
196
|
+
visibleRowIds,
|
|
194
197
|
}),
|
|
195
198
|
);
|
|
196
199
|
|
|
@@ -206,8 +209,8 @@ describe("useTableSelection", () => {
|
|
|
206
209
|
test("select all via thead checkbox", () => {
|
|
207
210
|
const { result } = renderHook(() =>
|
|
208
211
|
useTableSelection({
|
|
209
|
-
selectionMode: "multiple",
|
|
210
|
-
|
|
212
|
+
selection: { selectionMode: "multiple" },
|
|
213
|
+
visibleRowIds,
|
|
211
214
|
}),
|
|
212
215
|
);
|
|
213
216
|
|
|
@@ -220,12 +223,32 @@ describe("useTableSelection", () => {
|
|
|
220
223
|
expect(asMultiple(result).selectedKeys).toEqual(["a", "b", "c"]);
|
|
221
224
|
});
|
|
222
225
|
|
|
226
|
+
test("select all via thead includes hidden descendants for visible parents", () => {
|
|
227
|
+
const { result } = renderHook(() =>
|
|
228
|
+
useTableSelection({
|
|
229
|
+
selection: { selectionMode: "multiple" },
|
|
230
|
+
visibleRowIds: ["a"],
|
|
231
|
+
childRowIdsById,
|
|
232
|
+
}),
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
act(() => {
|
|
236
|
+
asMultiple(result)
|
|
237
|
+
.getTheadCheckboxProps()
|
|
238
|
+
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
expect(asMultiple(result).selectedKeys).toEqual(["a", "a1", "a2", "a2a"]);
|
|
242
|
+
});
|
|
243
|
+
|
|
223
244
|
test("deselect all when all are selected", () => {
|
|
224
245
|
const { result } = renderHook(() =>
|
|
225
246
|
useTableSelection({
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
247
|
+
selection: {
|
|
248
|
+
selectionMode: "multiple",
|
|
249
|
+
defaultSelectedKeys: ["a", "b", "c"],
|
|
250
|
+
},
|
|
251
|
+
visibleRowIds,
|
|
229
252
|
}),
|
|
230
253
|
);
|
|
231
254
|
|
|
@@ -238,12 +261,35 @@ describe("useTableSelection", () => {
|
|
|
238
261
|
expect(asMultiple(result).selectedKeys).toEqual([]);
|
|
239
262
|
});
|
|
240
263
|
|
|
264
|
+
test("deselect all clears hidden descendants for visible parents but preserves unrelated keys", () => {
|
|
265
|
+
const { result } = renderHook(() =>
|
|
266
|
+
useTableSelection({
|
|
267
|
+
selection: {
|
|
268
|
+
selectionMode: "multiple",
|
|
269
|
+
defaultSelectedKeys: ["a", "a1", "a2", "a2a", "external"],
|
|
270
|
+
},
|
|
271
|
+
visibleRowIds: ["a"],
|
|
272
|
+
childRowIdsById,
|
|
273
|
+
}),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
act(() => {
|
|
277
|
+
asMultiple(result)
|
|
278
|
+
.getTheadCheckboxProps()
|
|
279
|
+
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
expect(asMultiple(result).selectedKeys).toEqual(["external"]);
|
|
283
|
+
});
|
|
284
|
+
|
|
241
285
|
test("select all skips disabled keys", () => {
|
|
242
286
|
const { result } = renderHook(() =>
|
|
243
287
|
useTableSelection({
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
288
|
+
selection: {
|
|
289
|
+
selectionMode: "multiple",
|
|
290
|
+
disabledSelectionKeys: ["b"],
|
|
291
|
+
},
|
|
292
|
+
visibleRowIds,
|
|
247
293
|
}),
|
|
248
294
|
);
|
|
249
295
|
|
|
@@ -259,10 +305,12 @@ describe("useTableSelection", () => {
|
|
|
259
305
|
test("deselect all preserves disabled-but-selected rows", () => {
|
|
260
306
|
const { result } = renderHook(() =>
|
|
261
307
|
useTableSelection({
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
308
|
+
selection: {
|
|
309
|
+
selectionMode: "multiple",
|
|
310
|
+
defaultSelectedKeys: ["a", "b", "c"],
|
|
311
|
+
disabledSelectionKeys: ["b"],
|
|
312
|
+
},
|
|
313
|
+
visibleRowIds,
|
|
266
314
|
}),
|
|
267
315
|
);
|
|
268
316
|
|
|
@@ -278,9 +326,8 @@ describe("useTableSelection", () => {
|
|
|
278
326
|
test("thead checkbox shows indeterminate when partially selected", () => {
|
|
279
327
|
const { result } = renderHook(() =>
|
|
280
328
|
useTableSelection({
|
|
281
|
-
selectionMode: "multiple",
|
|
282
|
-
|
|
283
|
-
defaultSelectedKeys: ["a"],
|
|
329
|
+
selection: { selectionMode: "multiple", defaultSelectedKeys: ["a"] },
|
|
330
|
+
visibleRowIds,
|
|
284
331
|
}),
|
|
285
332
|
);
|
|
286
333
|
|
|
@@ -292,9 +339,11 @@ describe("useTableSelection", () => {
|
|
|
292
339
|
test("thead checkbox shows checked when all selected", () => {
|
|
293
340
|
const { result } = renderHook(() =>
|
|
294
341
|
useTableSelection({
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
342
|
+
selection: {
|
|
343
|
+
selectionMode: "multiple",
|
|
344
|
+
defaultSelectedKeys: ["a", "b", "c"],
|
|
345
|
+
},
|
|
346
|
+
visibleRowIds,
|
|
298
347
|
}),
|
|
299
348
|
);
|
|
300
349
|
|
|
@@ -306,10 +355,12 @@ describe("useTableSelection", () => {
|
|
|
306
355
|
test("thead checkbox shows checked when all selectable rows are selected", () => {
|
|
307
356
|
const { result } = renderHook(() =>
|
|
308
357
|
useTableSelection({
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
358
|
+
selection: {
|
|
359
|
+
selectionMode: "multiple",
|
|
360
|
+
defaultSelectedKeys: ["a", "c"],
|
|
361
|
+
disabledSelectionKeys: ["b"],
|
|
362
|
+
},
|
|
363
|
+
visibleRowIds,
|
|
313
364
|
}),
|
|
314
365
|
);
|
|
315
366
|
|
|
@@ -321,9 +372,11 @@ describe("useTableSelection", () => {
|
|
|
321
372
|
test("deselecting one row when all rows are selected", () => {
|
|
322
373
|
const { result } = renderHook(() =>
|
|
323
374
|
useTableSelection({
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
375
|
+
selection: {
|
|
376
|
+
selectionMode: "multiple",
|
|
377
|
+
defaultSelectedKeys: ["a", "b", "c"],
|
|
378
|
+
},
|
|
379
|
+
visibleRowIds,
|
|
327
380
|
}),
|
|
328
381
|
);
|
|
329
382
|
|
|
@@ -339,9 +392,11 @@ describe("useTableSelection", () => {
|
|
|
339
392
|
test("disabled rows have disabled prop", () => {
|
|
340
393
|
const { result } = renderHook(() =>
|
|
341
394
|
useTableSelection({
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
395
|
+
selection: {
|
|
396
|
+
selectionMode: "multiple",
|
|
397
|
+
disabledSelectionKeys: ["b"],
|
|
398
|
+
},
|
|
399
|
+
visibleRowIds,
|
|
345
400
|
}),
|
|
346
401
|
);
|
|
347
402
|
|
|
@@ -352,13 +407,82 @@ describe("useTableSelection", () => {
|
|
|
352
407
|
test("thead checkbox disabled when all rows disabled", () => {
|
|
353
408
|
const { result } = renderHook(() =>
|
|
354
409
|
useTableSelection({
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
410
|
+
selection: {
|
|
411
|
+
selectionMode: "multiple",
|
|
412
|
+
disabledSelectionKeys: ["a", "b", "c"],
|
|
413
|
+
},
|
|
414
|
+
visibleRowIds,
|
|
358
415
|
}),
|
|
359
416
|
);
|
|
360
417
|
|
|
361
418
|
expect(asMultiple(result).getTheadCheckboxProps().disabled).toBe(true);
|
|
362
419
|
});
|
|
420
|
+
|
|
421
|
+
test("parent rows show indeterminate when visible descendants are partially selected", () => {
|
|
422
|
+
const { result } = renderHook(() =>
|
|
423
|
+
useTableSelection({
|
|
424
|
+
selection: { selectionMode: "multiple", defaultSelectedKeys: ["a1"] },
|
|
425
|
+
visibleRowIds: ["a", "a1", "a2"],
|
|
426
|
+
childRowIdsById,
|
|
427
|
+
}),
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
const parentProps = asMultiple(result).getRowCheckboxProps("a");
|
|
431
|
+
|
|
432
|
+
expect(parentProps.checked).toBe(false);
|
|
433
|
+
expect(parentProps.indeterminate).toBe(true);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test("toggling a parent row selects and deselects its descendants", () => {
|
|
437
|
+
const { result } = renderHook(() =>
|
|
438
|
+
useTableSelection({
|
|
439
|
+
selection: { selectionMode: "multiple" },
|
|
440
|
+
visibleRowIds: ["a", "a1", "a2"],
|
|
441
|
+
childRowIdsById,
|
|
442
|
+
}),
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
act(() => {
|
|
446
|
+
asMultiple(result)
|
|
447
|
+
.getRowCheckboxProps("a")
|
|
448
|
+
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
expect(asMultiple(result).selectedKeys).toEqual(["a", "a1", "a2", "a2a"]);
|
|
452
|
+
|
|
453
|
+
act(() => {
|
|
454
|
+
asMultiple(result)
|
|
455
|
+
.getRowCheckboxProps("a")
|
|
456
|
+
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
expect(asMultiple(result).selectedKeys).toEqual([]);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test("toggling a collapsed parent selects and deselects hidden descendants", () => {
|
|
463
|
+
const { result } = renderHook(() =>
|
|
464
|
+
useTableSelection({
|
|
465
|
+
selection: { selectionMode: "multiple" },
|
|
466
|
+
visibleRowIds: ["a"],
|
|
467
|
+
childRowIdsById,
|
|
468
|
+
}),
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
act(() => {
|
|
472
|
+
asMultiple(result)
|
|
473
|
+
.getRowCheckboxProps("a")
|
|
474
|
+
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
expect(asMultiple(result).selectedKeys).toEqual(["a", "a1", "a2", "a2a"]);
|
|
478
|
+
|
|
479
|
+
act(() => {
|
|
480
|
+
asMultiple(result)
|
|
481
|
+
.getRowCheckboxProps("a")
|
|
482
|
+
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
expect(asMultiple(result).selectedKeys).toEqual([]);
|
|
486
|
+
});
|
|
363
487
|
});
|
|
364
488
|
});
|
|
@@ -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
|
-
|
|
32
|
-
|
|
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
|
|
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
|
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import React, { useCallback } from "react";
|
|
2
|
+
import { createStrictContext } from "../../../utils/helpers";
|
|
3
|
+
import { useControllableState } from "../../../utils/hooks";
|
|
4
|
+
import { useTableItemsContext } from "./useTableItems";
|
|
5
|
+
|
|
6
|
+
type DetailsPanelProps<T> = {
|
|
7
|
+
/**
|
|
8
|
+
* Renders a details panel below the row when expanded.
|
|
9
|
+
* When provided, an expand toggle column is added automatically.
|
|
10
|
+
*/
|
|
11
|
+
getContent?: (rowData: T) => React.ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* Determines whether a row can be expanded to show details panel content.
|
|
14
|
+
* @default () => true
|
|
15
|
+
*/
|
|
16
|
+
isRowExpandable?: (rowData: T) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Controlled list of expanded row IDs.
|
|
19
|
+
* Use with `onDetailsPanelChange` for controlled usage, or `defaultDetailsPanelRowIds` for uncontrolled.
|
|
20
|
+
*/
|
|
21
|
+
expandedRowIds?: (string | number)[];
|
|
22
|
+
/**
|
|
23
|
+
* Initial list of expanded row IDs for uncontrolled usage.
|
|
24
|
+
* @default []
|
|
25
|
+
*/
|
|
26
|
+
defaultExpandedRowIds?: (string | number)[];
|
|
27
|
+
/**
|
|
28
|
+
* 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
|
+
*/
|
|
34
|
+
onExpandedRowIdsChange?: (ids: (string | number)[]) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Returns the height (in px) or `"auto"` for a row's details panel.
|
|
37
|
+
* When a number is returned, the panel scrolls within that fixed height.
|
|
38
|
+
* @default "auto"
|
|
39
|
+
*/
|
|
40
|
+
getHeight?: (rowData: T) => number | "auto";
|
|
41
|
+
/**
|
|
42
|
+
* Shows an expand-all toggle button in the expand column header.
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
showExpandAll?: boolean;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
type DataTableDetailsPanelContextT = {
|
|
49
|
+
isExpanded: (id: string | number) => boolean;
|
|
50
|
+
isDetailsPanelExpandable: (id: string | number) => boolean;
|
|
51
|
+
toggleExpansion: (id: string | number) => void;
|
|
52
|
+
toggleAll: () => void;
|
|
53
|
+
isAllExpanded: boolean;
|
|
54
|
+
getDetailsPanelContent?: (row: unknown) => React.ReactNode;
|
|
55
|
+
getDetailsPanelHeight?: (row: unknown) => number | "auto";
|
|
56
|
+
showExpandAll: boolean;
|
|
57
|
+
enableDetailsPanel: boolean;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const {
|
|
61
|
+
Provider: DataTableDetailsPanelContextProvider,
|
|
62
|
+
useContext: useDataTableDetailsPanel,
|
|
63
|
+
} = createStrictContext<DataTableDetailsPanelContextT>({
|
|
64
|
+
name: "DataTableDetailsPanelContext",
|
|
65
|
+
errorMessage:
|
|
66
|
+
"useDataTableDetailsPanel must be used within a DataTableDetailsPanelProvider.",
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
function DataTableDetailsPanelProvider<T>({
|
|
70
|
+
children,
|
|
71
|
+
detailsPanel = {},
|
|
72
|
+
}: { detailsPanel?: DetailsPanelProps<T> } & { children: React.ReactNode }) {
|
|
73
|
+
const {
|
|
74
|
+
expandedRowIds,
|
|
75
|
+
defaultExpandedRowIds = [],
|
|
76
|
+
onExpandedRowIdsChange,
|
|
77
|
+
getContent,
|
|
78
|
+
isRowExpandable,
|
|
79
|
+
getHeight,
|
|
80
|
+
showExpandAll = false,
|
|
81
|
+
} = detailsPanel;
|
|
82
|
+
|
|
83
|
+
const [expandedIds, setExpandedIds] = useControllableState({
|
|
84
|
+
value: expandedRowIds,
|
|
85
|
+
defaultValue: defaultExpandedRowIds,
|
|
86
|
+
onChange: onExpandedRowIdsChange,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
/* TODO: False is just fallback until auto and root is merged */
|
|
90
|
+
const tableItemsContext = useTableItemsContext(false);
|
|
91
|
+
|
|
92
|
+
const { itemDetails } = tableItemsContext ?? {
|
|
93
|
+
itemDetails: new Map(),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const expandableIds = React.useMemo(() => {
|
|
97
|
+
if (!getContent) {
|
|
98
|
+
return new Set<string | number>();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const ids = new Set<string | number>();
|
|
102
|
+
|
|
103
|
+
for (const [rowData, { id, level }] of itemDetails.entries()) {
|
|
104
|
+
/* We only allow Master - Details pattern on top level rows */
|
|
105
|
+
if (level > 0) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!isRowExpandable || isRowExpandable(rowData)) {
|
|
110
|
+
ids.add(id);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return ids;
|
|
115
|
+
}, [getContent, isRowExpandable, itemDetails]);
|
|
116
|
+
|
|
117
|
+
const isDetailsPanelExpandableById = useCallback(
|
|
118
|
+
(id: string | number) => expandableIds.has(id),
|
|
119
|
+
[expandableIds],
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const isExpanded = useCallback(
|
|
123
|
+
(id: string | number) =>
|
|
124
|
+
isDetailsPanelExpandableById(id) && expandedIds.includes(id),
|
|
125
|
+
[expandedIds, isDetailsPanelExpandableById],
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const toggleExpansion = useCallback(
|
|
129
|
+
(id: string | number) => {
|
|
130
|
+
if (!isDetailsPanelExpandableById(id)) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
setExpandedIds((currentExpandedIds) =>
|
|
135
|
+
currentExpandedIds.includes(id)
|
|
136
|
+
? currentExpandedIds.filter((expandedId) => expandedId !== id)
|
|
137
|
+
: [...currentExpandedIds, id],
|
|
138
|
+
);
|
|
139
|
+
},
|
|
140
|
+
[isDetailsPanelExpandableById, setExpandedIds],
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const isAllExpanded =
|
|
144
|
+
expandableIds.size > 0 &&
|
|
145
|
+
Array.from(expandableIds).every((key) => expandedIds.includes(key));
|
|
146
|
+
|
|
147
|
+
const toggleAll = useCallback(() => {
|
|
148
|
+
setExpandedIds(isAllExpanded ? [] : Array.from(expandableIds));
|
|
149
|
+
}, [expandableIds, isAllExpanded, setExpandedIds]);
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<DataTableDetailsPanelContextProvider
|
|
153
|
+
isExpanded={isExpanded}
|
|
154
|
+
isDetailsPanelExpandable={isDetailsPanelExpandableById}
|
|
155
|
+
toggleExpansion={toggleExpansion}
|
|
156
|
+
toggleAll={toggleAll}
|
|
157
|
+
isAllExpanded={isAllExpanded}
|
|
158
|
+
getDetailsPanelContent={
|
|
159
|
+
getContent as ((row: unknown) => React.ReactNode) | undefined
|
|
160
|
+
}
|
|
161
|
+
getDetailsPanelHeight={
|
|
162
|
+
getHeight as ((row: unknown) => number | "auto") | undefined
|
|
163
|
+
}
|
|
164
|
+
showExpandAll={showExpandAll}
|
|
165
|
+
enableDetailsPanel={!!getContent}
|
|
166
|
+
>
|
|
167
|
+
{children}
|
|
168
|
+
</DataTableDetailsPanelContextProvider>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function getDataTableDetailsPanelId(tableId: string, rowId: string | number) {
|
|
173
|
+
return `${tableId}-expansion-${rowId}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export {
|
|
177
|
+
DataTableDetailsPanelProvider,
|
|
178
|
+
getDataTableDetailsPanelId,
|
|
179
|
+
useDataTableDetailsPanel,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export type { DetailsPanelProps };
|