@navikt/ds-react 8.10.5 → 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.
- package/cjs/data/data-grid/index.d.ts +2 -0
- package/cjs/data/data-grid/index.js +7 -0
- package/cjs/data/data-grid/index.js.map +1 -0
- package/cjs/data/data-grid/root/DataGridRoot.context.d.ts +11 -0
- package/cjs/data/data-grid/root/DataGridRoot.context.js +11 -0
- package/cjs/data/data-grid/root/DataGridRoot.context.js.map +1 -0
- package/cjs/data/data-grid/root/DataGridRoot.d.ts +38 -0
- package/cjs/data/data-grid/root/DataGridRoot.js +68 -0
- package/cjs/data/data-grid/root/DataGridRoot.js.map +1 -0
- package/cjs/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js +0 -1
- package/cjs/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js.map +1 -1
- package/cjs/data/drag-and-drop/root/DragAndDropRoot.d.ts +5 -5
- package/cjs/data/drag-and-drop/root/DragAndDropRoot.js +4 -27
- package/cjs/data/drag-and-drop/root/DragAndDropRoot.js.map +1 -1
- package/cjs/data/stories/Data.test-data.d.ts +2 -5
- package/cjs/data/stories/Data.test-data.js +30 -38
- package/cjs/data/stories/Data.test-data.js.map +1 -1
- package/cjs/data/table/base-cell/DataTableBaseCell.d.ts +15 -15
- package/cjs/data/table/base-cell/DataTableBaseCell.js +4 -8
- package/cjs/data/table/base-cell/DataTableBaseCell.js.map +1 -1
- package/cjs/data/table/column-header/DataTableColumnHeader.d.ts +24 -6
- package/cjs/data/table/column-header/DataTableColumnHeader.js +22 -27
- package/cjs/data/table/column-header/DataTableColumnHeader.js.map +1 -1
- package/cjs/data/table/column-header/useTableColumnResize.d.ts +19 -29
- package/cjs/data/table/column-header/useTableColumnResize.js +24 -22
- package/cjs/data/table/column-header/useTableColumnResize.js.map +1 -1
- package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.d.ts +1 -1
- package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.js +2 -2
- package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.js.map +1 -1
- package/cjs/data/table/helpers/collectTableRowEntries.d.ts +2 -2
- package/cjs/data/table/helpers/selection/getMultipleSelectProps.d.ts +13 -11
- package/cjs/data/table/helpers/selection/getMultipleSelectProps.js +43 -53
- package/cjs/data/table/helpers/selection/getMultipleSelectProps.js.map +1 -1
- package/cjs/data/table/helpers/selection/getSingleSelectProps.d.ts +9 -8
- package/cjs/data/table/helpers/selection/getSingleSelectProps.js +23 -10
- package/cjs/data/table/helpers/selection/getSingleSelectProps.js.map +1 -1
- package/cjs/data/table/helpers/selection/selection.types.d.ts +19 -19
- package/cjs/data/table/helpers/selection/selection.utils.d.ts +21 -0
- package/cjs/data/table/helpers/selection/selection.utils.js +46 -0
- package/cjs/data/table/helpers/selection/selection.utils.js.map +1 -0
- package/cjs/data/table/hooks/useColumnOptions.d.ts +16 -5
- package/cjs/data/table/hooks/useColumnOptions.js +26 -8
- package/cjs/data/table/hooks/useColumnOptions.js.map +1 -1
- package/cjs/data/table/hooks/useTableDetailsPanel.d.ts +10 -13
- package/cjs/data/table/hooks/useTableDetailsPanel.js +6 -5
- package/cjs/data/table/hooks/useTableDetailsPanel.js.map +1 -1
- package/cjs/data/table/hooks/useTableItems.d.ts +29 -15
- package/cjs/data/table/hooks/useTableItems.js +2 -12
- package/cjs/data/table/hooks/useTableItems.js.map +1 -1
- package/cjs/data/table/hooks/useTableKeyboardNav.d.ts +1 -6
- package/cjs/data/table/hooks/useTableKeyboardNav.js +1 -4
- package/cjs/data/table/hooks/useTableKeyboardNav.js.map +1 -1
- package/cjs/data/table/hooks/useTableSelection.d.ts +6 -6
- package/cjs/data/table/hooks/useTableSelection.js +13 -13
- package/cjs/data/table/hooks/useTableSelection.js.map +1 -1
- package/cjs/data/table/hooks/useTableSort.d.ts +2 -2
- package/cjs/data/table/hooks/useTableSort.js +4 -5
- package/cjs/data/table/hooks/useTableSort.js.map +1 -1
- package/cjs/data/table/root/DataTable.types.d.ts +22 -10
- package/cjs/data/table/root/DataTableRoot.context.d.ts +13 -7
- package/cjs/data/table/root/DataTableRoot.context.js.map +1 -1
- package/cjs/data/table/root/DataTableRoot.d.ts +49 -72
- package/cjs/data/table/root/DataTableRoot.js +54 -66
- package/cjs/data/table/root/DataTableRoot.js.map +1 -1
- package/cjs/data/table/root/DataTableRoot.legacy.d.ts +2 -7
- package/cjs/data/table/root/DataTableRoot.legacy.js +17 -3
- package/cjs/data/table/root/DataTableRoot.legacy.js.map +1 -1
- package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.js +4 -4
- package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.js.map +1 -1
- package/cjs/data/table/tbody/DataTableTbody.js +4 -2
- package/cjs/data/table/tbody/DataTableTbody.js.map +1 -1
- package/cjs/data/table/tr/DataTableTr.d.ts +5 -3
- package/cjs/data/table/tr/DataTableTr.js +36 -23
- package/cjs/data/table/tr/DataTableTr.js.map +1 -1
- package/cjs/table/ColumnHeader.js +2 -1
- package/cjs/table/ColumnHeader.js.map +1 -1
- package/esm/data/data-grid/index.d.ts +2 -0
- package/esm/data/data-grid/index.js +3 -0
- package/esm/data/data-grid/index.js.map +1 -0
- package/esm/data/data-grid/root/DataGridRoot.context.d.ts +11 -0
- package/esm/data/data-grid/root/DataGridRoot.context.js +7 -0
- package/esm/data/data-grid/root/DataGridRoot.context.js.map +1 -0
- package/esm/data/data-grid/root/DataGridRoot.d.ts +38 -0
- package/esm/data/data-grid/root/DataGridRoot.js +32 -0
- package/esm/data/data-grid/root/DataGridRoot.js.map +1 -0
- package/esm/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js +0 -1
- package/esm/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js.map +1 -1
- package/esm/data/drag-and-drop/root/DragAndDropRoot.d.ts +5 -5
- package/esm/data/drag-and-drop/root/DragAndDropRoot.js +4 -27
- package/esm/data/drag-and-drop/root/DragAndDropRoot.js.map +1 -1
- package/esm/data/stories/Data.test-data.d.ts +2 -5
- package/esm/data/stories/Data.test-data.js +30 -38
- package/esm/data/stories/Data.test-data.js.map +1 -1
- package/esm/data/table/base-cell/DataTableBaseCell.d.ts +15 -15
- package/esm/data/table/base-cell/DataTableBaseCell.js +4 -8
- package/esm/data/table/base-cell/DataTableBaseCell.js.map +1 -1
- package/esm/data/table/column-header/DataTableColumnHeader.d.ts +24 -6
- package/esm/data/table/column-header/DataTableColumnHeader.js +23 -28
- package/esm/data/table/column-header/DataTableColumnHeader.js.map +1 -1
- package/esm/data/table/column-header/useTableColumnResize.d.ts +19 -29
- package/esm/data/table/column-header/useTableColumnResize.js +24 -22
- package/esm/data/table/column-header/useTableColumnResize.js.map +1 -1
- package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.d.ts +1 -1
- package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.js +2 -2
- package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.js.map +1 -1
- package/esm/data/table/helpers/collectTableRowEntries.d.ts +2 -2
- package/esm/data/table/helpers/selection/getMultipleSelectProps.d.ts +13 -11
- package/esm/data/table/helpers/selection/getMultipleSelectProps.js +43 -53
- package/esm/data/table/helpers/selection/getMultipleSelectProps.js.map +1 -1
- package/esm/data/table/helpers/selection/getSingleSelectProps.d.ts +9 -8
- package/esm/data/table/helpers/selection/getSingleSelectProps.js +23 -10
- package/esm/data/table/helpers/selection/getSingleSelectProps.js.map +1 -1
- package/esm/data/table/helpers/selection/selection.types.d.ts +19 -19
- package/esm/data/table/helpers/selection/selection.utils.d.ts +21 -0
- package/esm/data/table/helpers/selection/selection.utils.js +43 -0
- package/esm/data/table/helpers/selection/selection.utils.js.map +1 -0
- package/esm/data/table/hooks/useColumnOptions.d.ts +16 -5
- package/esm/data/table/hooks/useColumnOptions.js +26 -8
- package/esm/data/table/hooks/useColumnOptions.js.map +1 -1
- package/esm/data/table/hooks/useTableDetailsPanel.d.ts +10 -13
- package/esm/data/table/hooks/useTableDetailsPanel.js +6 -5
- package/esm/data/table/hooks/useTableDetailsPanel.js.map +1 -1
- package/esm/data/table/hooks/useTableItems.d.ts +29 -15
- package/esm/data/table/hooks/useTableItems.js +3 -10
- package/esm/data/table/hooks/useTableItems.js.map +1 -1
- package/esm/data/table/hooks/useTableKeyboardNav.d.ts +1 -6
- package/esm/data/table/hooks/useTableKeyboardNav.js +1 -4
- package/esm/data/table/hooks/useTableKeyboardNav.js.map +1 -1
- package/esm/data/table/hooks/useTableSelection.d.ts +6 -6
- package/esm/data/table/hooks/useTableSelection.js +13 -13
- package/esm/data/table/hooks/useTableSelection.js.map +1 -1
- package/esm/data/table/hooks/useTableSort.d.ts +2 -2
- package/esm/data/table/hooks/useTableSort.js +4 -5
- package/esm/data/table/hooks/useTableSort.js.map +1 -1
- package/esm/data/table/root/DataTable.types.d.ts +22 -10
- package/esm/data/table/root/DataTableRoot.context.d.ts +13 -7
- package/esm/data/table/root/DataTableRoot.context.js.map +1 -1
- package/esm/data/table/root/DataTableRoot.d.ts +49 -72
- package/esm/data/table/root/DataTableRoot.js +56 -68
- package/esm/data/table/root/DataTableRoot.js.map +1 -1
- package/esm/data/table/root/DataTableRoot.legacy.d.ts +2 -7
- package/esm/data/table/root/DataTableRoot.legacy.js +17 -3
- package/esm/data/table/root/DataTableRoot.legacy.js.map +1 -1
- package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.js +4 -4
- package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.js.map +1 -1
- package/esm/data/table/tbody/DataTableTbody.js +4 -2
- package/esm/data/table/tbody/DataTableTbody.js.map +1 -1
- package/esm/data/table/tr/DataTableTr.d.ts +5 -3
- package/esm/data/table/tr/DataTableTr.js +35 -23
- package/esm/data/table/tr/DataTableTr.js.map +1 -1
- package/esm/table/ColumnHeader.js +2 -1
- package/esm/table/ColumnHeader.js.map +1 -1
- package/package.json +3 -3
- package/src/data/data-grid/index.ts +3 -0
- package/src/data/data-grid/root/DataGridRoot.context.ts +16 -0
- package/src/data/data-grid/root/DataGridRoot.tsx +71 -0
- package/src/data/drag-and-drop/drag-handler/DragAndDropDragHandler.tsx +0 -1
- package/src/data/drag-and-drop/root/DragAndDropRoot.tsx +15 -49
- package/src/data/stories/Data.test-data.tsx +52 -43
- package/src/data/table/agent-component-review.md +175 -0
- package/src/data/table/base-cell/DataTableBaseCell.tsx +31 -21
- package/src/data/table/column-header/DataTableColumnHeader.tsx +61 -58
- package/src/data/table/column-header/useTableColumnResize.ts +55 -71
- package/src/data/table/details-panel-row/DataTableDetailsPanelRow.tsx +3 -3
- package/src/data/table/helpers/collectTableRowEntries.ts +1 -2
- package/src/data/table/helpers/selection/getMultipleSelectProps.ts +65 -85
- package/src/data/table/helpers/selection/getSingleSelectProps.ts +35 -17
- package/src/data/table/helpers/selection/selection.types.ts +19 -19
- package/src/data/table/helpers/selection/selection.utils.test.ts +161 -0
- package/src/data/table/helpers/selection/selection.utils.ts +73 -0
- package/src/data/table/hooks/__tests__/useTableItems.test.ts +2 -1
- package/src/data/table/hooks/useColumnOptions.ts +48 -14
- package/src/data/table/hooks/useTableDetailsPanel.tsx +22 -25
- package/src/data/table/hooks/useTableItems.ts +32 -24
- package/src/data/table/hooks/useTableKeyboardNav.ts +1 -13
- package/src/data/table/hooks/useTableSelection.ts +26 -31
- package/src/data/table/hooks/useTableSort.ts +10 -9
- package/src/data/table/root/DataTable.types.ts +30 -22
- package/src/data/table/root/DataTableRoot.context.ts +19 -7
- package/src/data/table/root/DataTableRoot.legacy.tsx +22 -14
- package/src/data/table/root/DataTableRoot.tsx +244 -293
- package/src/data/table/sub-row-toggle/DataTableSubRowToggle.tsx +5 -4
- package/src/data/table/tbody/DataTableTbody.tsx +6 -2
- package/src/data/table/tr/DataTableTr.tsx +98 -35
- package/src/table/ColumnHeader.tsx +2 -1
- package/cjs/data/drag-and-drop-legacy/drag-handler/DragAndDropDragHandlerLegacy.d.ts +0 -22
- package/cjs/data/drag-and-drop-legacy/drag-handler/DragAndDropDragHandlerLegacy.js +0 -35
- package/cjs/data/drag-and-drop-legacy/drag-handler/DragAndDropDragHandlerLegacy.js.map +0 -1
- package/cjs/data/drag-and-drop-legacy/item/DragAndDropItemLegacy.d.ts +0 -27
- package/cjs/data/drag-and-drop-legacy/item/DragAndDropItemLegacy.js +0 -86
- package/cjs/data/drag-and-drop-legacy/item/DragAndDropItemLegacy.js.map +0 -1
- package/cjs/data/drag-and-drop-legacy/root/DragAndDropLegacy.context.d.ts +0 -5
- package/cjs/data/drag-and-drop-legacy/root/DragAndDropLegacy.context.js +0 -6
- package/cjs/data/drag-and-drop-legacy/root/DragAndDropLegacy.context.js.map +0 -1
- package/cjs/data/drag-and-drop-legacy/root/DragAndDropLegacyRoot.d.ts +0 -24
- package/cjs/data/drag-and-drop-legacy/root/DragAndDropLegacyRoot.js +0 -108
- package/cjs/data/drag-and-drop-legacy/root/DragAndDropLegacyRoot.js.map +0 -1
- package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.d.ts +0 -46
- package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.js +0 -112
- package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.js.map +0 -1
- package/esm/data/drag-and-drop-legacy/drag-handler/DragAndDropDragHandlerLegacy.d.ts +0 -22
- package/esm/data/drag-and-drop-legacy/drag-handler/DragAndDropDragHandlerLegacy.js +0 -29
- package/esm/data/drag-and-drop-legacy/drag-handler/DragAndDropDragHandlerLegacy.js.map +0 -1
- package/esm/data/drag-and-drop-legacy/item/DragAndDropItemLegacy.d.ts +0 -27
- package/esm/data/drag-and-drop-legacy/item/DragAndDropItemLegacy.js +0 -50
- package/esm/data/drag-and-drop-legacy/item/DragAndDropItemLegacy.js.map +0 -1
- package/esm/data/drag-and-drop-legacy/root/DragAndDropLegacy.context.d.ts +0 -5
- package/esm/data/drag-and-drop-legacy/root/DragAndDropLegacy.context.js +0 -3
- package/esm/data/drag-and-drop-legacy/root/DragAndDropLegacy.context.js.map +0 -1
- package/esm/data/drag-and-drop-legacy/root/DragAndDropLegacyRoot.d.ts +0 -24
- package/esm/data/drag-and-drop-legacy/root/DragAndDropLegacyRoot.js +0 -68
- package/esm/data/drag-and-drop-legacy/root/DragAndDropLegacyRoot.js.map +0 -1
- package/esm/data/table/helpers/selection/SelectionSubtreeHelper.d.ts +0 -46
- package/esm/data/table/helpers/selection/SelectionSubtreeHelper.js +0 -109
- package/esm/data/table/helpers/selection/SelectionSubtreeHelper.js.map +0 -1
- package/src/data/drag-and-drop-legacy/drag-handler/DragAndDropDragHandlerLegacy.tsx +0 -104
- package/src/data/drag-and-drop-legacy/item/DragAndDropItemLegacy.tsx +0 -74
- package/src/data/drag-and-drop-legacy/root/DragAndDropLegacy.context.tsx +0 -11
- package/src/data/drag-and-drop-legacy/root/DragAndDropLegacyRoot.tsx +0 -94
- package/src/data/table/helpers/selection/SelectionSubtreeHelper.test.ts +0 -66
- package/src/data/table/helpers/selection/SelectionSubtreeHelper.ts +0 -162
- package/src/data/table/hooks/__tests__/useTableSelection.test.ts +0 -488
- package/src/data/table/root/agent-feature-gap.md +0 -96
|
@@ -1,488 +0,0 @@
|
|
|
1
|
-
import { act, renderHook } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, test, vi } from "vitest";
|
|
3
|
-
import type {
|
|
4
|
-
MultipleSelection,
|
|
5
|
-
SingleSelection,
|
|
6
|
-
} from "../../helpers/selection/selection.types";
|
|
7
|
-
import {
|
|
8
|
-
type UseTableSelectionReturn,
|
|
9
|
-
useTableSelection,
|
|
10
|
-
} from "../useTableSelection";
|
|
11
|
-
|
|
12
|
-
type Item = { id: string; name: string };
|
|
13
|
-
|
|
14
|
-
const items: Item[] = [
|
|
15
|
-
{ id: "a", name: "Alpha" },
|
|
16
|
-
{ id: "b", name: "Beta" },
|
|
17
|
-
{ id: "c", name: "Charlie" },
|
|
18
|
-
];
|
|
19
|
-
|
|
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
|
-
]);
|
|
27
|
-
|
|
28
|
-
function asSingle(result: {
|
|
29
|
-
current: UseTableSelectionReturn;
|
|
30
|
-
}): SingleSelection {
|
|
31
|
-
return result.current.selection as SingleSelection;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function asMultiple(result: {
|
|
35
|
-
current: UseTableSelectionReturn;
|
|
36
|
-
}): MultipleSelection {
|
|
37
|
-
return result.current.selection as MultipleSelection;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
describe("useTableSelection", () => {
|
|
41
|
-
describe('selectionMode="none"', () => {
|
|
42
|
-
test("returns empty selectedKeys and no prop getters", () => {
|
|
43
|
-
const { result } = renderHook(() =>
|
|
44
|
-
useTableSelection({
|
|
45
|
-
selection: { selectionMode: "none" },
|
|
46
|
-
visibleRowIds,
|
|
47
|
-
}),
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
expect(result.current.selection.selectionMode).toBe("none");
|
|
51
|
-
expect(result.current.selection.selectedKeys).toEqual([]);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe('selectionMode="single"', () => {
|
|
56
|
-
test("returns getRowRadioProps", () => {
|
|
57
|
-
const { result } = renderHook(() =>
|
|
58
|
-
useTableSelection({
|
|
59
|
-
selection: { selectionMode: "single" },
|
|
60
|
-
visibleRowIds,
|
|
61
|
-
}),
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
expect(result.current.selection.selectionMode).toBe("single");
|
|
65
|
-
expect(asSingle(result).getRowRadioProps).toBeDefined();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("selecting a row via radio onChange", () => {
|
|
69
|
-
const onChange = vi.fn();
|
|
70
|
-
const { result } = renderHook(() =>
|
|
71
|
-
useTableSelection({
|
|
72
|
-
selection: { selectionMode: "single", onSelectionChange: onChange },
|
|
73
|
-
visibleRowIds,
|
|
74
|
-
}),
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
const radioProps = asSingle(result).getRowRadioProps("a");
|
|
78
|
-
expect(radioProps.checked).toBe(false);
|
|
79
|
-
|
|
80
|
-
act(() => {
|
|
81
|
-
radioProps.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
expect(asSingle(result).selectedKeys).toEqual(["a"]);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("toggling the same row keeps it selected", () => {
|
|
88
|
-
const { result } = renderHook(() =>
|
|
89
|
-
useTableSelection({
|
|
90
|
-
selection: { selectionMode: "single", defaultSelectedKeys: ["a"] },
|
|
91
|
-
visibleRowIds,
|
|
92
|
-
}),
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
act(() => {
|
|
96
|
-
asSingle(result)
|
|
97
|
-
.getRowRadioProps("a")
|
|
98
|
-
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
expect(asSingle(result).selectedKeys).toEqual(["a"]);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("selecting a new row replaces the previous", () => {
|
|
105
|
-
const { result } = renderHook(() =>
|
|
106
|
-
useTableSelection({
|
|
107
|
-
selection: { selectionMode: "single", defaultSelectedKeys: ["a"] },
|
|
108
|
-
visibleRowIds,
|
|
109
|
-
}),
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
act(() => {
|
|
113
|
-
asSingle(result)
|
|
114
|
-
.getRowRadioProps("b")
|
|
115
|
-
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
expect(asSingle(result).selectedKeys).toEqual(["b"]);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test("disabled rows have disabled prop", () => {
|
|
122
|
-
const { result } = renderHook(() =>
|
|
123
|
-
useTableSelection({
|
|
124
|
-
selection: { selectionMode: "single", disabledSelectionKeys: ["b"] },
|
|
125
|
-
visibleRowIds,
|
|
126
|
-
}),
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
expect(asSingle(result).getRowRadioProps("a").disabled).toBe(false);
|
|
130
|
-
expect(asSingle(result).getRowRadioProps("b").disabled).toBe(true);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test("controlled selectedKeys", () => {
|
|
134
|
-
const { result, rerender } = renderHook(
|
|
135
|
-
({ selectedKeys }) =>
|
|
136
|
-
useTableSelection({
|
|
137
|
-
selection: { selectionMode: "single", selectedKeys },
|
|
138
|
-
visibleRowIds,
|
|
139
|
-
}),
|
|
140
|
-
{ initialProps: { selectedKeys: ["a"] as (string | number)[] } },
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
expect(asSingle(result).selectedKeys).toEqual(["a"]);
|
|
144
|
-
|
|
145
|
-
rerender({ selectedKeys: ["b"] });
|
|
146
|
-
expect(asSingle(result).selectedKeys).toEqual(["b"]);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe('selectionMode="multiple"', () => {
|
|
151
|
-
test("returns getTheadCheckboxProps and getRowCheckboxProps", () => {
|
|
152
|
-
const { result } = renderHook(() =>
|
|
153
|
-
useTableSelection({
|
|
154
|
-
selection: { selectionMode: "multiple" },
|
|
155
|
-
visibleRowIds,
|
|
156
|
-
}),
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
expect(result.current.selection.selectionMode).toBe("multiple");
|
|
160
|
-
expect(asMultiple(result).getTheadCheckboxProps).toBeDefined();
|
|
161
|
-
expect(asMultiple(result).getRowCheckboxProps).toBeDefined();
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test("selecting individual rows", () => {
|
|
165
|
-
const { result } = renderHook(() =>
|
|
166
|
-
useTableSelection({
|
|
167
|
-
selection: { selectionMode: "multiple" },
|
|
168
|
-
visibleRowIds,
|
|
169
|
-
}),
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
act(() => {
|
|
173
|
-
asMultiple(result)
|
|
174
|
-
.getRowCheckboxProps("a")
|
|
175
|
-
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
expect(asMultiple(result).selectedKeys).toEqual(["a"]);
|
|
179
|
-
|
|
180
|
-
act(() => {
|
|
181
|
-
asMultiple(result)
|
|
182
|
-
.getRowCheckboxProps("c")
|
|
183
|
-
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
expect(asMultiple(result).selectedKeys).toEqual(["a", "c"]);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test("deselecting a row", () => {
|
|
190
|
-
const { result } = renderHook(() =>
|
|
191
|
-
useTableSelection({
|
|
192
|
-
selection: {
|
|
193
|
-
selectionMode: "multiple",
|
|
194
|
-
defaultSelectedKeys: ["a", "b"],
|
|
195
|
-
},
|
|
196
|
-
visibleRowIds,
|
|
197
|
-
}),
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
act(() => {
|
|
201
|
-
asMultiple(result)
|
|
202
|
-
.getRowCheckboxProps("a")
|
|
203
|
-
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
expect(asMultiple(result).selectedKeys).toEqual(["b"]);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test("select all via thead checkbox", () => {
|
|
210
|
-
const { result } = renderHook(() =>
|
|
211
|
-
useTableSelection({
|
|
212
|
-
selection: { selectionMode: "multiple" },
|
|
213
|
-
visibleRowIds,
|
|
214
|
-
}),
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
act(() => {
|
|
218
|
-
asMultiple(result)
|
|
219
|
-
.getTheadCheckboxProps()
|
|
220
|
-
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
expect(asMultiple(result).selectedKeys).toEqual(["a", "b", "c"]);
|
|
224
|
-
});
|
|
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
|
-
|
|
244
|
-
test("deselect all when all are selected", () => {
|
|
245
|
-
const { result } = renderHook(() =>
|
|
246
|
-
useTableSelection({
|
|
247
|
-
selection: {
|
|
248
|
-
selectionMode: "multiple",
|
|
249
|
-
defaultSelectedKeys: ["a", "b", "c"],
|
|
250
|
-
},
|
|
251
|
-
visibleRowIds,
|
|
252
|
-
}),
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
act(() => {
|
|
256
|
-
asMultiple(result)
|
|
257
|
-
.getTheadCheckboxProps()
|
|
258
|
-
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
expect(asMultiple(result).selectedKeys).toEqual([]);
|
|
262
|
-
});
|
|
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
|
-
|
|
285
|
-
test("select all skips disabled keys", () => {
|
|
286
|
-
const { result } = renderHook(() =>
|
|
287
|
-
useTableSelection({
|
|
288
|
-
selection: {
|
|
289
|
-
selectionMode: "multiple",
|
|
290
|
-
disabledSelectionKeys: ["b"],
|
|
291
|
-
},
|
|
292
|
-
visibleRowIds,
|
|
293
|
-
}),
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
act(() => {
|
|
297
|
-
asMultiple(result)
|
|
298
|
-
.getTheadCheckboxProps()
|
|
299
|
-
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
expect(asMultiple(result).selectedKeys).toEqual(["a", "c"]);
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
test("deselect all preserves disabled-but-selected rows", () => {
|
|
306
|
-
const { result } = renderHook(() =>
|
|
307
|
-
useTableSelection({
|
|
308
|
-
selection: {
|
|
309
|
-
selectionMode: "multiple",
|
|
310
|
-
defaultSelectedKeys: ["a", "b", "c"],
|
|
311
|
-
disabledSelectionKeys: ["b"],
|
|
312
|
-
},
|
|
313
|
-
visibleRowIds,
|
|
314
|
-
}),
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
act(() => {
|
|
318
|
-
asMultiple(result)
|
|
319
|
-
.getTheadCheckboxProps()
|
|
320
|
-
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
expect(asMultiple(result).selectedKeys).toEqual(["b"]);
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
test("thead checkbox shows indeterminate when partially selected", () => {
|
|
327
|
-
const { result } = renderHook(() =>
|
|
328
|
-
useTableSelection({
|
|
329
|
-
selection: { selectionMode: "multiple", defaultSelectedKeys: ["a"] },
|
|
330
|
-
visibleRowIds,
|
|
331
|
-
}),
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
const theadProps = asMultiple(result).getTheadCheckboxProps();
|
|
335
|
-
expect(theadProps.indeterminate).toBe(true);
|
|
336
|
-
expect(theadProps.checked).toBe(false);
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
test("thead checkbox shows checked when all selected", () => {
|
|
340
|
-
const { result } = renderHook(() =>
|
|
341
|
-
useTableSelection({
|
|
342
|
-
selection: {
|
|
343
|
-
selectionMode: "multiple",
|
|
344
|
-
defaultSelectedKeys: ["a", "b", "c"],
|
|
345
|
-
},
|
|
346
|
-
visibleRowIds,
|
|
347
|
-
}),
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
const theadProps = asMultiple(result).getTheadCheckboxProps();
|
|
351
|
-
expect(theadProps.indeterminate).toBe(false);
|
|
352
|
-
expect(theadProps.checked).toBe(true);
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
test("thead checkbox shows checked when all selectable rows are selected", () => {
|
|
356
|
-
const { result } = renderHook(() =>
|
|
357
|
-
useTableSelection({
|
|
358
|
-
selection: {
|
|
359
|
-
selectionMode: "multiple",
|
|
360
|
-
defaultSelectedKeys: ["a", "c"],
|
|
361
|
-
disabledSelectionKeys: ["b"],
|
|
362
|
-
},
|
|
363
|
-
visibleRowIds,
|
|
364
|
-
}),
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
const theadProps = asMultiple(result).getTheadCheckboxProps();
|
|
368
|
-
expect(theadProps.indeterminate).toBe(false);
|
|
369
|
-
expect(theadProps.checked).toBe(true);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
test("deselecting one row when all rows are selected", () => {
|
|
373
|
-
const { result } = renderHook(() =>
|
|
374
|
-
useTableSelection({
|
|
375
|
-
selection: {
|
|
376
|
-
selectionMode: "multiple",
|
|
377
|
-
defaultSelectedKeys: ["a", "b", "c"],
|
|
378
|
-
},
|
|
379
|
-
visibleRowIds,
|
|
380
|
-
}),
|
|
381
|
-
);
|
|
382
|
-
|
|
383
|
-
act(() => {
|
|
384
|
-
asMultiple(result)
|
|
385
|
-
.getRowCheckboxProps("b")
|
|
386
|
-
.onChange?.({} as React.ChangeEvent<HTMLInputElement>);
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
expect(asMultiple(result).selectedKeys).toEqual(["a", "c"]);
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
test("disabled rows have disabled prop", () => {
|
|
393
|
-
const { result } = renderHook(() =>
|
|
394
|
-
useTableSelection({
|
|
395
|
-
selection: {
|
|
396
|
-
selectionMode: "multiple",
|
|
397
|
-
disabledSelectionKeys: ["b"],
|
|
398
|
-
},
|
|
399
|
-
visibleRowIds,
|
|
400
|
-
}),
|
|
401
|
-
);
|
|
402
|
-
|
|
403
|
-
expect(asMultiple(result).getRowCheckboxProps("a").disabled).toBe(false);
|
|
404
|
-
expect(asMultiple(result).getRowCheckboxProps("b").disabled).toBe(true);
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
test("thead checkbox disabled when all rows disabled", () => {
|
|
408
|
-
const { result } = renderHook(() =>
|
|
409
|
-
useTableSelection({
|
|
410
|
-
selection: {
|
|
411
|
-
selectionMode: "multiple",
|
|
412
|
-
disabledSelectionKeys: ["a", "b", "c"],
|
|
413
|
-
},
|
|
414
|
-
visibleRowIds,
|
|
415
|
-
}),
|
|
416
|
-
);
|
|
417
|
-
|
|
418
|
-
expect(asMultiple(result).getTheadCheckboxProps().disabled).toBe(true);
|
|
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
|
-
});
|
|
487
|
-
});
|
|
488
|
-
});
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
### Component: DataTable
|
|
2
|
-
|
|
3
|
-
**Primitive type:** Data table with optional selection, sorting, nested rows, details panels, sticky columns, column resizing, and keyboard cell navigation.
|
|
4
|
-
**Reference libraries:** React Aria Table / Adobe Spectrum TableView, MUI Data Grid, Base UI table patterns. Cloudscape Table is a useful secondary reference for resize/details behavior.
|
|
5
|
-
|
|
6
|
-
### Launch Review Findings
|
|
7
|
-
|
|
8
|
-
1. **The auto API has no visible caption slot or caption prop.**
|
|
9
|
-
Consumers can still pass `aria-label`/`aria-labelledby` through native table props, but `children?: never` means the preview API cannot render a native `caption` or a built-in visible summary/title. Mature table APIs usually provide at least one clear accessible naming path.
|
|
10
|
-
|
|
11
|
-
2. **Column resizing is present, but the preview API still reads as unfinished.**
|
|
12
|
-
Resizing is effectively opt-out in fixed layout because `DataTableColumnHeader` defaults `resizable` to `true`. The interaction also still has unfinished accessibility and i18n details, and `onWidthChange` currently fires for every width step while dragging.
|
|
13
|
-
|
|
14
|
-
3. **Critical behaviors are under-tested for a preview launch.**
|
|
15
|
-
Existing tests cover selection trees, item expansion state, and low-level keyboard helpers, but not the end-to-end behaviors users will notice first: sorting, details panels, loading states, row click behavior, column resizing, and selection accessibility.
|
|
16
|
-
|
|
17
|
-
### Feature Gap Table
|
|
18
|
-
|
|
19
|
-
| # | Feature | Status | Priority | Reference | Notes |
|
|
20
|
-
| --- | --------------------------------------------------- | ---------- | --------------- | ---------------------------- | ----------------------------------------------------------------------- |
|
|
21
|
-
| 1 | Arrow/Home/End keyboard cell navigation | ✅ Present | — | React Aria, MUI | Good base coverage in helper tests. |
|
|
22
|
-
| 2 | Controlled/uncontrolled sorting | ✅ Present | — | React Aria, MUI | Supports controlled and uncontrolled sort arrays. |
|
|
23
|
-
| 3 | Multi-column sorting | ✅ Present | — | MUI, Spectrum | Shift-click is supported. |
|
|
24
|
-
| 4 | Controlled/uncontrolled row selection | ✅ Present | — | React Aria, MUI | Single and multiple selection both exist. |
|
|
25
|
-
| 5 | Controlled/uncontrolled nested row expansion | ✅ Present | — | MUI, Spectrum | `subRows` API is already solid for preview. |
|
|
26
|
-
| 6 | Controlled/uncontrolled details panel expansion | ✅ Present | — | MUI | Good baseline, limited to top-level rows by design. |
|
|
27
|
-
| 7 | Loading overlay / loading rows / empty state | ✅ Present | — | MUI, Spectrum | Baseline states exist. |
|
|
28
|
-
| 9 | Visible caption / built-in accessible naming path | ❌ Missing | 🔴 Expected | React Aria, Spectrum | Auto API cannot render `caption`; users only get raw HTML attrs. |
|
|
29
|
-
| 13 | Opt-in / polished column resize API | ⚠️ Partial | 🟡 Valuable | MUI, Cloudscape | Feature exists, but defaults and a11y polish are not preview-ready yet. |
|
|
30
|
-
| 14 | Per-row disabled selection callback | ❌ Missing | 🟡 Valuable | React Aria, MUI | Current API only accepts disabled keys, not row-driven logic. |
|
|
31
|
-
| 15 | Row-level prop/className callback | ❌ Missing | 🟡 Valuable | MUI | No way to attach row-specific attrs/classes without custom rendering. |
|
|
32
|
-
| 16 | Header-group / multi-row header support in auto API | ❌ Missing | 🟢 Nice-to-have | React Aria, MUI | Current auto API is intentionally flat. |
|
|
33
|
-
| 17 | Accessor shorthand for simple columns | ❌ Missing | 🟢 Nice-to-have | MUI, TanStack-style patterns | Would reduce boilerplate for simple data tables. |
|
|
34
|
-
| 18 | Typeahead row navigation | ❌ Missing | 🟢 Nice-to-have | React Aria | Useful on large data sets, not necessary for first preview. |
|
|
35
|
-
| 19 | RTL-aware sticky/nesting layout | ❌ Missing | 🟢 Nice-to-have | React Aria, MUI | Current sticky/nesting CSS is mostly physical, not logical. |
|
|
36
|
-
|
|
37
|
-
### Implementation Notes
|
|
38
|
-
|
|
39
|
-
#### 9. Visible caption / built-in accessible naming path
|
|
40
|
-
|
|
41
|
-
- **What it does:** Lets consumers provide a native table caption or a clearly supported naming API without dropping to raw HTML attributes.
|
|
42
|
-
- **Reference behavior:** React Aria tables require an accessible label; Spectrum and MUI also expose a clear naming path.
|
|
43
|
-
- **Approach here:** Keep `children` forbidden, but add a `caption?: React.ReactNode` prop that renders a native `caption`. Optionally add `captionProps` for styling hooks.
|
|
44
|
-
- **Props/API additions needed:** `caption?: React.ReactNode`, optionally `captionProps?: React.HTMLAttributes<HTMLTableCaptionElement>`.
|
|
45
|
-
|
|
46
|
-
#### 13. Opt-in / polished column resize API
|
|
47
|
-
|
|
48
|
-
- **What it does:** Makes resize behavior predictable and avoids surprising every preview consumer with advanced header affordances.
|
|
49
|
-
- **Reference behavior:** MUI and Cloudscape treat column resize as an explicit feature and spend a lot of API surface on labels, limits, and commit behavior.
|
|
50
|
-
- **Approach here:** Default `resizable` to `false`, add missing slider aria metadata, and only call `onWidthChange` on commit or with a small debounce. Also move hard-coded Norwegian labels into the same i18n pattern as the rest of the component.
|
|
51
|
-
- **Props/API additions needed:** No new public props required for a first pass. The main change is default behavior and polish.
|
|
52
|
-
|
|
53
|
-
#### 14. Per-row disabled selection callback
|
|
54
|
-
|
|
55
|
-
- **What it does:** Lets consumers disable selection from row data instead of precomputing key arrays.
|
|
56
|
-
- **Reference behavior:** Many mature table APIs allow row-driven selection rules.
|
|
57
|
-
- **Approach here:** Resolve disabled state during row collection so selection helpers can still work with ids internally.
|
|
58
|
-
- **Props/API additions needed:** `isRowSelectionDisabled?: (rowData: T) => boolean`.
|
|
59
|
-
|
|
60
|
-
#### 15. Row-level prop/className callback
|
|
61
|
-
|
|
62
|
-
- **What it does:** Lets consumers add row-specific classes, test hooks, aria hooks, and state styling without replacing the table implementation.
|
|
63
|
-
- **Reference behavior:** MUI exposes row-class callbacks and row metadata hooks.
|
|
64
|
-
- **Approach here:** Apply callbacks when rendering `DataTableTr` so the row id and row data are both available.
|
|
65
|
-
- **Props/API additions needed:** `getRowProps?: (rowData: T, rowId: string | number) => React.HTMLAttributes<HTMLTableRowElement>` or a narrower `getRowClassName` if you want to keep the API tighter.
|
|
66
|
-
|
|
67
|
-
### Internal Refactors
|
|
68
|
-
|
|
69
|
-
1. Split the current root into a small orchestration hook plus presentational sub-components. `DataTableRoot.tsx` currently owns state setup, context assembly, loading/empty rendering, row rendering, and sticky-wrapper logic in one file.
|
|
70
|
-
|
|
71
|
-
2. Replace object-keyed `itemDetails` lookups with id-keyed row entries. This fixes the duplicate-object bug and simplifies details-panel and selection wiring.
|
|
72
|
-
|
|
73
|
-
3. Unify loading-state presence checks. `DataTableTBodyContent` uses nullish checks for `loadingState`, while `showLoadingOverlay` in the root uses truthiness checks; those should match.
|
|
74
|
-
|
|
75
|
-
4. Make localization an explicit seam. The component currently hard-codes visible and aria labels such as sort-resize instructions, expand/collapse labels, and selection labels directly in render code.
|
|
76
|
-
|
|
77
|
-
5. Revisit the resize default and commit path before large-table performance work. Continuous `onWidthChange` callbacks can easily push rerenders through the whole table if consumers control widths externally.
|
|
78
|
-
|
|
79
|
-
6. Decide whether the preview API is a plain table or the start of a data-grid abstraction. A few current seams, especially keyboard navigation and selection, are already grid-like, while captioning and row-header semantics still assume a plain table model.
|
|
80
|
-
|
|
81
|
-
### Test Gaps Before Preview
|
|
82
|
-
|
|
83
|
-
1. Sorting interaction story/test covering click, keyboard activation, and multi-sort.
|
|
84
|
-
2. Details-panel story/test covering expand, collapse, `aria-controls`, and controlled mode.
|
|
85
|
-
3. Loading/empty-state tests covering overlay vs skeleton vs custom loading content.
|
|
86
|
-
4. Row-click tests covering selection interaction, text-selection guard, and interactive-content guard.
|
|
87
|
-
5. Resize tests covering keyboard resize, pointer resize, and form embedding.
|
|
88
|
-
6. Accessibility test coverage for selection labels and row-header semantics once those are implemented.
|
|
89
|
-
|
|
90
|
-
### Suggested Order For Follow-up Work
|
|
91
|
-
|
|
92
|
-
1. Fix selection labeling and row-header semantics together.
|
|
93
|
-
2. Add a built-in caption/naming API.
|
|
94
|
-
3. Harden resize defaults and button semantics.
|
|
95
|
-
4. Refactor row metadata to be id-based.
|
|
96
|
-
5. Fill the missing user-facing stories/tests around sort, details, loading, and row click.
|