@navikt/ds-react 8.10.0 → 8.10.2
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/table/column-header/DataTableColumnHeader.d.ts +1 -2
- package/cjs/data/table/column-header/DataTableColumnHeader.js +13 -11
- package/cjs/data/table/column-header/DataTableColumnHeader.js.map +1 -1
- package/cjs/data/table/column-header/useTableColumnResize.d.ts +5 -3
- package/cjs/data/table/column-header/useTableColumnResize.js +128 -53
- package/cjs/data/table/column-header/useTableColumnResize.js.map +1 -1
- package/cjs/data/table/helpers/collectTableRowEntries.d.ts +16 -0
- package/cjs/data/table/helpers/collectTableRowEntries.js +27 -0
- package/cjs/data/table/helpers/collectTableRowEntries.js.map +1 -0
- package/cjs/data/table/helpers/table-keyboard.js +3 -3
- package/cjs/data/table/helpers/table-keyboard.js.map +1 -1
- package/cjs/data/table/hooks/useTableExpansion.d.ts +9 -6
- package/cjs/data/table/hooks/useTableExpansion.js +36 -15
- package/cjs/data/table/hooks/useTableExpansion.js.map +1 -1
- package/cjs/data/table/hooks/useTableItems.d.ts +29 -0
- package/cjs/data/table/hooks/useTableItems.js +63 -0
- package/cjs/data/table/hooks/useTableItems.js.map +1 -0
- package/cjs/data/table/hooks/useTableKeyboardNav.js +3 -3
- package/cjs/data/table/hooks/useTableKeyboardNav.js.map +1 -1
- package/cjs/data/table/root/DataTableAuto.d.ts +18 -0
- package/cjs/data/table/root/DataTableAuto.js +71 -29
- package/cjs/data/table/root/DataTableAuto.js.map +1 -1
- package/cjs/data/table/root/DataTableRoot.context.d.ts +5 -3
- package/cjs/data/table/root/DataTableRoot.context.js.map +1 -1
- package/cjs/data/table/root/DataTableRoot.js +7 -4
- package/cjs/data/table/root/DataTableRoot.js.map +1 -1
- package/cjs/data/table/tr/DataTableTr.js +30 -32
- package/cjs/data/table/tr/DataTableTr.js.map +1 -1
- package/cjs/modal/Modal.js +3 -3
- package/cjs/modal/Modal.js.map +1 -1
- package/cjs/modal/types.d.ts +1 -0
- package/esm/data/table/column-header/DataTableColumnHeader.d.ts +1 -2
- package/esm/data/table/column-header/DataTableColumnHeader.js +14 -12
- package/esm/data/table/column-header/DataTableColumnHeader.js.map +1 -1
- package/esm/data/table/column-header/useTableColumnResize.d.ts +5 -3
- package/esm/data/table/column-header/useTableColumnResize.js +129 -54
- package/esm/data/table/column-header/useTableColumnResize.js.map +1 -1
- package/esm/data/table/helpers/collectTableRowEntries.d.ts +16 -0
- package/esm/data/table/helpers/collectTableRowEntries.js +25 -0
- package/esm/data/table/helpers/collectTableRowEntries.js.map +1 -0
- package/esm/data/table/helpers/table-keyboard.js +3 -3
- package/esm/data/table/helpers/table-keyboard.js.map +1 -1
- package/esm/data/table/hooks/useTableExpansion.d.ts +9 -6
- package/esm/data/table/hooks/useTableExpansion.js +36 -16
- package/esm/data/table/hooks/useTableExpansion.js.map +1 -1
- package/esm/data/table/hooks/useTableItems.d.ts +29 -0
- package/esm/data/table/hooks/useTableItems.js +58 -0
- package/esm/data/table/hooks/useTableItems.js.map +1 -0
- package/esm/data/table/hooks/useTableKeyboardNav.js +3 -3
- package/esm/data/table/hooks/useTableKeyboardNav.js.map +1 -1
- package/esm/data/table/root/DataTableAuto.d.ts +18 -0
- package/esm/data/table/root/DataTableAuto.js +72 -30
- package/esm/data/table/root/DataTableAuto.js.map +1 -1
- package/esm/data/table/root/DataTableRoot.context.d.ts +5 -3
- package/esm/data/table/root/DataTableRoot.context.js.map +1 -1
- package/esm/data/table/root/DataTableRoot.js +7 -4
- package/esm/data/table/root/DataTableRoot.js.map +1 -1
- package/esm/data/table/tr/DataTableTr.js +32 -34
- package/esm/data/table/tr/DataTableTr.js.map +1 -1
- package/esm/modal/Modal.js +3 -3
- package/esm/modal/Modal.js.map +1 -1
- package/esm/modal/types.d.ts +1 -0
- package/package.json +7 -7
- package/src/data/table/column-header/DataTableColumnHeader.tsx +22 -14
- package/src/data/table/column-header/useTableColumnResize.ts +152 -79
- package/src/data/table/helpers/collectTableRowEntries.ts +58 -0
- package/src/data/table/helpers/table-keyboard.ts +4 -4
- package/src/data/table/hooks/__tests__/useTableExpansion.test.tsx +115 -0
- package/src/data/table/hooks/__tests__/useTableItems.test.ts +131 -0
- package/src/data/table/hooks/useTableExpansion.tsx +63 -22
- package/src/data/table/hooks/useTableItems.ts +123 -0
- package/src/data/table/hooks/useTableKeyboardNav.ts +3 -3
- package/src/data/table/root/DataTableAuto.test.tsx +118 -0
- package/src/data/table/root/DataTableAuto.tsx +159 -49
- package/src/data/table/root/DataTableRoot.context.ts +4 -2
- package/src/data/table/root/DataTableRoot.tsx +20 -13
- package/src/data/table/tr/DataTableTr.tsx +48 -47
- package/src/modal/Modal.tsx +12 -6
- package/src/modal/types.ts +1 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { describe, expect, test } from "vitest";
|
|
4
|
+
import { DataTableColumnHeader } from "../column-header/DataTableColumnHeader";
|
|
5
|
+
import type { ColumnDefinitions } from "./DataTable.types";
|
|
6
|
+
import { DataTableAuto } from "./DataTableAuto";
|
|
7
|
+
import { DataTable } from "./DataTableRoot";
|
|
8
|
+
|
|
9
|
+
type TestRow = {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
subRows?: TestRow[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const data: TestRow[] = [
|
|
16
|
+
{
|
|
17
|
+
id: "root",
|
|
18
|
+
name: "Root",
|
|
19
|
+
subRows: [{ id: "child", name: "Child" }],
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const fallbackIdData: TestRow[] = [
|
|
24
|
+
{
|
|
25
|
+
id: "unused-root-1",
|
|
26
|
+
name: "Root",
|
|
27
|
+
subRows: [{ id: "unused-child", name: "Child" }],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "unused-root-2",
|
|
31
|
+
name: "Sibling",
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const columns: ColumnDefinitions<TestRow> = [
|
|
36
|
+
{
|
|
37
|
+
id: "name",
|
|
38
|
+
label: "Name",
|
|
39
|
+
header: "Name",
|
|
40
|
+
cell: (row) => row.name,
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
describe("DataTableAuto", () => {
|
|
45
|
+
test("renders expanded child rows and includes them in select-all", () => {
|
|
46
|
+
render(
|
|
47
|
+
<DataTableAuto
|
|
48
|
+
columnDefinitions={columns}
|
|
49
|
+
data={data}
|
|
50
|
+
getRowId={(row) => row.id}
|
|
51
|
+
getSubRows={(row) => row.subRows ?? []}
|
|
52
|
+
selectionMode="multiple"
|
|
53
|
+
/>,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expect(screen.queryByText("Child")).not.toBeInTheDocument();
|
|
57
|
+
|
|
58
|
+
fireEvent.click(screen.getByRole("button", { name: "Vis under-rader" }));
|
|
59
|
+
|
|
60
|
+
expect(screen.getByText("Child")).toBeInTheDocument();
|
|
61
|
+
expect(screen.getAllByRole("checkbox")).toHaveLength(3);
|
|
62
|
+
|
|
63
|
+
fireEvent.click(
|
|
64
|
+
screen.getByRole("checkbox", { name: "Velg alle synlige rader" }),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(
|
|
68
|
+
screen
|
|
69
|
+
.getAllByRole("checkbox")
|
|
70
|
+
.every((checkbox) => (checkbox as HTMLInputElement).checked),
|
|
71
|
+
).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("select-all checks all visible rows when fallback ids are used", () => {
|
|
75
|
+
render(
|
|
76
|
+
<DataTableAuto
|
|
77
|
+
columnDefinitions={columns}
|
|
78
|
+
data={fallbackIdData}
|
|
79
|
+
getSubRows={(row) => row.subRows ?? []}
|
|
80
|
+
defaultExpandedSubRowIds={[0]}
|
|
81
|
+
selectionMode="multiple"
|
|
82
|
+
/>,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
fireEvent.click(
|
|
86
|
+
screen.getByRole("checkbox", { name: "Velg alle synlige rader" }),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const rowCheckboxes = screen
|
|
90
|
+
.getAllByRole("checkbox")
|
|
91
|
+
.slice(1) as HTMLInputElement[];
|
|
92
|
+
|
|
93
|
+
expect(rowCheckboxes).toHaveLength(3);
|
|
94
|
+
expect(rowCheckboxes.every((checkbox) => checkbox.checked)).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("does not render expansion controls in the manual table variant", () => {
|
|
98
|
+
render(
|
|
99
|
+
<DataTable>
|
|
100
|
+
<DataTable.Thead>
|
|
101
|
+
<DataTable.Tr>
|
|
102
|
+
<DataTableColumnHeader>Name</DataTableColumnHeader>
|
|
103
|
+
</DataTable.Tr>
|
|
104
|
+
</DataTable.Thead>
|
|
105
|
+
<DataTable.Tbody>
|
|
106
|
+
<DataTable.Tr>
|
|
107
|
+
<DataTable.Td>Root</DataTable.Td>
|
|
108
|
+
</DataTable.Tr>
|
|
109
|
+
</DataTable.Tbody>
|
|
110
|
+
</DataTable>,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(screen.getByText("Root")).toBeInTheDocument();
|
|
114
|
+
expect(
|
|
115
|
+
screen.queryByRole("button", { name: /vis detaljer|skjul detaljer/i }),
|
|
116
|
+
).not.toBeInTheDocument();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** biome-ignore-all lint/correctness/useHookAtTopLevel: False positive because of the way forwardRef() is added */
|
|
2
2
|
import React, { forwardRef, useMemo } from "react";
|
|
3
|
+
import { ChevronDownIcon, ChevronRightIcon } from "@navikt/aksel-icons";
|
|
4
|
+
import { Button } from "../../../button";
|
|
3
5
|
import { Skeleton } from "../../../skeleton";
|
|
4
6
|
import { useId } from "../../../utils-external";
|
|
5
7
|
import { cl } from "../../../utils/helpers";
|
|
@@ -7,12 +9,18 @@ import { useMergeRefs } from "../../../utils/hooks";
|
|
|
7
9
|
import { DataTableBaseCell } from "../base-cell/DataTableBaseCell";
|
|
8
10
|
import { DataTableColumnHeader } from "../column-header/DataTableColumnHeader";
|
|
9
11
|
import { DataTableEmptyState } from "../empty-state/DataTableEmptyState";
|
|
10
|
-
import type { UseColumnOptionsResult } from "../hooks/useColumnOptions";
|
|
11
12
|
import { useColumnOptions } from "../hooks/useColumnOptions";
|
|
12
13
|
import {
|
|
13
14
|
DataTableExpansionProvider,
|
|
15
|
+
getDataTableExpansionId,
|
|
14
16
|
useDataTableExpansion,
|
|
15
17
|
} from "../hooks/useTableExpansion";
|
|
18
|
+
import {
|
|
19
|
+
type ItemDetail,
|
|
20
|
+
TableItemsProvider,
|
|
21
|
+
useTableItems,
|
|
22
|
+
useTableItemsContext,
|
|
23
|
+
} from "../hooks/useTableItems";
|
|
16
24
|
import { useTableKeyboardNav } from "../hooks/useTableKeyboardNav";
|
|
17
25
|
import {
|
|
18
26
|
type SelectionProps,
|
|
@@ -157,6 +165,11 @@ interface DataTableProps<T>
|
|
|
157
165
|
* When provided, an expand toggle column is added automatically.
|
|
158
166
|
*/
|
|
159
167
|
getDetailsPanelContent?: (rowData: T) => React.ReactNode;
|
|
168
|
+
/**
|
|
169
|
+
* Determines whether a row can be expanded to show details panel content.
|
|
170
|
+
* @default () => true
|
|
171
|
+
*/
|
|
172
|
+
isDetailsPanelExpandable?: (rowData: T) => boolean;
|
|
160
173
|
/**
|
|
161
174
|
* Controlled list of expanded row IDs.
|
|
162
175
|
* Use with `onDetailsPanelChange` for controlled usage, or `defaultDetailsPanelRowIds` for uncontrolled.
|
|
@@ -169,6 +182,10 @@ interface DataTableProps<T>
|
|
|
169
182
|
defaultDetailsPanelRowIds?: (string | number)[];
|
|
170
183
|
/**
|
|
171
184
|
* Called when the list of expanded row IDs changes.
|
|
185
|
+
*
|
|
186
|
+
*
|
|
187
|
+
* TODO:
|
|
188
|
+
* - Docs: This pattern is called "Master / Detail" in general terms
|
|
172
189
|
*/
|
|
173
190
|
onDetailsPanelChange?: (ids: (string | number)[]) => void;
|
|
174
191
|
/**
|
|
@@ -182,6 +199,15 @@ interface DataTableProps<T>
|
|
|
182
199
|
* @default false
|
|
183
200
|
*/
|
|
184
201
|
showExpandAll?: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* Function to get sub-rows for a given row, used for nested rows.
|
|
204
|
+
* When provided, an expand toggle column is added automatically.
|
|
205
|
+
*/
|
|
206
|
+
getSubRows?: (rowData: T) => T[];
|
|
207
|
+
expandedSubRowIds?: (string | number)[];
|
|
208
|
+
defaultExpandedSubRowIds?: (string | number)[];
|
|
209
|
+
isSubRowExpandable?: (rowData: T) => boolean;
|
|
210
|
+
onExpandedSubRowIdsChange?: (ids: (string | number)[]) => void;
|
|
185
211
|
}
|
|
186
212
|
|
|
187
213
|
function DataTableAutoInner<T>(
|
|
@@ -215,11 +241,17 @@ function DataTableAutoInner<T>(
|
|
|
215
241
|
loadingLabel = "Laster innhold",
|
|
216
242
|
disableRowSelectionOnClick = false,
|
|
217
243
|
getDetailsPanelContent,
|
|
244
|
+
isDetailsPanelExpandable,
|
|
218
245
|
getDetailsPanelHeight,
|
|
219
246
|
showExpandAll = false,
|
|
220
247
|
detailsPanelRowIds,
|
|
221
248
|
defaultDetailsPanelRowIds,
|
|
222
249
|
onDetailsPanelChange,
|
|
250
|
+
getSubRows,
|
|
251
|
+
expandedSubRowIds,
|
|
252
|
+
defaultExpandedSubRowIds,
|
|
253
|
+
isSubRowExpandable,
|
|
254
|
+
onExpandedSubRowIdsChange,
|
|
223
255
|
...rest
|
|
224
256
|
}: DataTableProps<T>,
|
|
225
257
|
forwardedRef: React.ForwardedRef<HTMLTableElement>,
|
|
@@ -237,12 +269,25 @@ function DataTableAutoInner<T>(
|
|
|
237
269
|
|
|
238
270
|
const mergedRef = useMergeRefs(forwardedRef, setTableRef);
|
|
239
271
|
|
|
272
|
+
const tableItems = useTableItems({
|
|
273
|
+
items: data,
|
|
274
|
+
getRowId,
|
|
275
|
+
getSubRows,
|
|
276
|
+
expandedSubRowIds,
|
|
277
|
+
defaultExpandedSubRowIds,
|
|
278
|
+
isSubRowExpandable,
|
|
279
|
+
onExpandedSubRowIdsChange,
|
|
280
|
+
});
|
|
281
|
+
|
|
240
282
|
const allRowKeys = useMemo(() => {
|
|
241
|
-
const
|
|
242
|
-
|
|
283
|
+
const rowKeys: (string | number)[] = [];
|
|
284
|
+
|
|
285
|
+
for (const details of tableItems.itemDetails.values()) {
|
|
286
|
+
rowKeys.push(details.id);
|
|
287
|
+
}
|
|
243
288
|
|
|
244
|
-
return
|
|
245
|
-
}, [
|
|
289
|
+
return rowKeys;
|
|
290
|
+
}, [tableItems.itemDetails]);
|
|
246
291
|
|
|
247
292
|
const tableSelectionState = useTableSelection({
|
|
248
293
|
selectionMode: selectionModeProp,
|
|
@@ -258,6 +303,12 @@ function DataTableAutoInner<T>(
|
|
|
258
303
|
selectionMode: tableSelectionState.selection.selectionMode,
|
|
259
304
|
});
|
|
260
305
|
|
|
306
|
+
const fullWidthColSpan =
|
|
307
|
+
columns.length +
|
|
308
|
+
(layout === "fixed" ? 1 : 0) +
|
|
309
|
+
(tableSelectionState.selection.selectionMode !== "none" ? 1 : 0) +
|
|
310
|
+
(getDetailsPanelContent ? 1 : 0);
|
|
311
|
+
|
|
261
312
|
const tableId = useId(id);
|
|
262
313
|
|
|
263
314
|
return (
|
|
@@ -273,13 +324,15 @@ function DataTableAutoInner<T>(
|
|
|
273
324
|
disableRowSelectionOnClick={disableRowSelectionOnClick}
|
|
274
325
|
isLoading={isLoading}
|
|
275
326
|
showLoadingOverlay={isLoading && !loadingState && !loadingRows}
|
|
327
|
+
columns={columns}
|
|
276
328
|
>
|
|
277
329
|
<DataTableExpansionProvider
|
|
278
330
|
detailsPanelRowIds={detailsPanelRowIds}
|
|
279
331
|
defaultDetailsPanelRowIds={defaultDetailsPanelRowIds}
|
|
280
332
|
onDetailsPanelChange={onDetailsPanelChange}
|
|
281
|
-
|
|
333
|
+
itemDetails={tableItems.itemDetails}
|
|
282
334
|
getDetailsPanelContent={getDetailsPanelContent}
|
|
335
|
+
isDetailsPanelExpandable={isDetailsPanelExpandable}
|
|
283
336
|
getDetailsPanelHeight={getDetailsPanelHeight}
|
|
284
337
|
showExpandAll={showExpandAll}
|
|
285
338
|
>
|
|
@@ -323,17 +376,22 @@ function DataTableAutoInner<T>(
|
|
|
323
376
|
})}
|
|
324
377
|
</DataTableTr>
|
|
325
378
|
</DataTableThead>
|
|
326
|
-
<
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
379
|
+
<TableItemsProvider
|
|
380
|
+
itemDetails={tableItems.itemDetails}
|
|
381
|
+
items={tableItems.items}
|
|
382
|
+
onExpandedSubRowIdsChange={tableItems.onExpandedSubRowIdsChange}
|
|
383
|
+
isSubRowExpanded={tableItems.isSubRowExpanded}
|
|
384
|
+
>
|
|
385
|
+
<DataTableTbody>
|
|
386
|
+
<DataTableAutoTBodyContent
|
|
387
|
+
loadingState={loadingState}
|
|
388
|
+
loadingRows={loadingRows}
|
|
389
|
+
loadingLabel={loadingLabel}
|
|
390
|
+
emptyState={emptyState}
|
|
391
|
+
fullWidthColSpan={fullWidthColSpan}
|
|
392
|
+
/>
|
|
393
|
+
</DataTableTbody>
|
|
394
|
+
</TableItemsProvider>
|
|
337
395
|
</table>
|
|
338
396
|
</div>
|
|
339
397
|
</div>
|
|
@@ -342,30 +400,27 @@ function DataTableAutoInner<T>(
|
|
|
342
400
|
);
|
|
343
401
|
}
|
|
344
402
|
|
|
345
|
-
interface DataTableAutoTBodyContentProps
|
|
346
|
-
columns: UseColumnOptionsResult<T>["columns"];
|
|
347
|
-
data: T[];
|
|
348
|
-
allRowKeys: (string | number)[];
|
|
403
|
+
interface DataTableAutoTBodyContentProps {
|
|
349
404
|
loadingState: React.ReactNode;
|
|
350
405
|
loadingLabel: string;
|
|
351
406
|
loadingRows?: number;
|
|
352
407
|
emptyState: React.ReactNode;
|
|
408
|
+
fullWidthColSpan: number;
|
|
353
409
|
}
|
|
354
410
|
|
|
355
|
-
function DataTableAutoTBodyContent
|
|
356
|
-
columns,
|
|
357
|
-
data,
|
|
358
|
-
allRowKeys,
|
|
411
|
+
function DataTableAutoTBodyContent({
|
|
359
412
|
loadingState,
|
|
360
413
|
loadingRows,
|
|
361
414
|
loadingLabel,
|
|
362
415
|
emptyState,
|
|
363
|
-
|
|
364
|
-
|
|
416
|
+
fullWidthColSpan,
|
|
417
|
+
}: DataTableAutoTBodyContentProps) {
|
|
418
|
+
const { items, itemDetails } = useTableItemsContext();
|
|
419
|
+
const { columns, isLoading } = useDataTableContext();
|
|
365
420
|
|
|
366
421
|
if (isLoading && loadingState != null) {
|
|
367
422
|
return (
|
|
368
|
-
<DataTableLoadingState colSpan={
|
|
423
|
+
<DataTableLoadingState colSpan={fullWidthColSpan}>
|
|
369
424
|
{loadingState}
|
|
370
425
|
</DataTableLoadingState>
|
|
371
426
|
);
|
|
@@ -375,7 +430,7 @@ function DataTableAutoTBodyContent<T>({
|
|
|
375
430
|
return (
|
|
376
431
|
<>
|
|
377
432
|
<tr>
|
|
378
|
-
<td colSpan={
|
|
433
|
+
<td colSpan={fullWidthColSpan} className="aksel-sr-only">
|
|
379
434
|
{loadingLabel}
|
|
380
435
|
</td>
|
|
381
436
|
</tr>
|
|
@@ -397,29 +452,45 @@ function DataTableAutoTBodyContent<T>({
|
|
|
397
452
|
);
|
|
398
453
|
}
|
|
399
454
|
|
|
400
|
-
if (
|
|
455
|
+
if (items.length === 0 && emptyState !== undefined) {
|
|
401
456
|
return (
|
|
402
|
-
<DataTableEmptyState colSpan={
|
|
457
|
+
<DataTableEmptyState colSpan={fullWidthColSpan}>
|
|
403
458
|
{emptyState}
|
|
404
459
|
</DataTableEmptyState>
|
|
405
460
|
);
|
|
406
461
|
}
|
|
407
462
|
|
|
408
|
-
const
|
|
463
|
+
const renderLoadingAnnouncement = isLoading && !loadingState && !loadingRows;
|
|
464
|
+
|
|
465
|
+
return items.map((rowData) => {
|
|
466
|
+
const details = itemDetails.get(rowData);
|
|
467
|
+
|
|
468
|
+
/* Should in theory be impossible. Look about typing this? */
|
|
469
|
+
if (!details) {
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const hasSubRows = details.children.length > 0;
|
|
409
474
|
|
|
410
|
-
return data.map((rowData, rowIndex) => {
|
|
411
|
-
const rowId = allRowKeys[rowIndex];
|
|
412
475
|
return (
|
|
413
|
-
<React.Fragment key={
|
|
414
|
-
{
|
|
476
|
+
<React.Fragment key={details.id}>
|
|
477
|
+
{renderLoadingAnnouncement && (
|
|
415
478
|
<tr>
|
|
416
|
-
<td colSpan={
|
|
479
|
+
<td colSpan={fullWidthColSpan} className="aksel-sr-only">
|
|
417
480
|
{loadingLabel}
|
|
418
481
|
</td>
|
|
419
482
|
</tr>
|
|
420
483
|
)}
|
|
421
|
-
<DataTableTr rowId={
|
|
484
|
+
<DataTableTr rowId={details.id}>
|
|
422
485
|
{columns.map(({ isSticky, colDef }, colDefIndex) => {
|
|
486
|
+
const renderNestedToggle = colDefIndex === 0 && hasSubRows;
|
|
487
|
+
const renderNestedIndent =
|
|
488
|
+
colDefIndex === 0 && (details.level > 0 || hasSubRows);
|
|
489
|
+
|
|
490
|
+
const style: React.CSSProperties = {
|
|
491
|
+
"--__axc-data-table-nested-depth": details.level,
|
|
492
|
+
};
|
|
493
|
+
|
|
423
494
|
return (
|
|
424
495
|
<DataTableBaseCell
|
|
425
496
|
/* TODO: Make this configurable */
|
|
@@ -427,47 +498,86 @@ function DataTableAutoTBodyContent<T>({
|
|
|
427
498
|
key={colDef.id || colDefIndex}
|
|
428
499
|
as={colDef.isRowHeader ? "th" : "td"}
|
|
429
500
|
isSticky={isSticky}
|
|
501
|
+
data-nested={renderNestedIndent || undefined}
|
|
502
|
+
style={style}
|
|
430
503
|
>
|
|
504
|
+
{renderNestedToggle && <NestedRowToggle details={details} />}
|
|
431
505
|
{colDef.cell(rowData)}
|
|
432
506
|
</DataTableBaseCell>
|
|
433
507
|
);
|
|
434
508
|
})}
|
|
435
509
|
</DataTableTr>
|
|
436
510
|
<DataTableExpandedRow
|
|
437
|
-
rowId={
|
|
511
|
+
rowId={details.id}
|
|
438
512
|
rowData={rowData}
|
|
439
|
-
|
|
513
|
+
fullWidthColSpan={fullWidthColSpan}
|
|
440
514
|
/>
|
|
441
515
|
</React.Fragment>
|
|
442
516
|
);
|
|
443
517
|
});
|
|
444
518
|
}
|
|
445
519
|
|
|
520
|
+
function NestedRowToggle({ details }: { details: ItemDetail<any> }) {
|
|
521
|
+
const { isSubRowExpanded, onExpandedSubRowIdsChange } =
|
|
522
|
+
useTableItemsContext();
|
|
523
|
+
|
|
524
|
+
const subRows = details.children;
|
|
525
|
+
const hasSubRows = subRows && subRows.length > 0;
|
|
526
|
+
const isRowExpanded = isSubRowExpanded(details.id);
|
|
527
|
+
|
|
528
|
+
return (
|
|
529
|
+
<div className="aksel-data-table__nested-toggle">
|
|
530
|
+
{hasSubRows && (
|
|
531
|
+
<Button
|
|
532
|
+
variant="tertiary"
|
|
533
|
+
data-color="neutral"
|
|
534
|
+
size="small"
|
|
535
|
+
onClick={(e) => {
|
|
536
|
+
e.stopPropagation();
|
|
537
|
+
onExpandedSubRowIdsChange(details.id);
|
|
538
|
+
}}
|
|
539
|
+
aria-expanded={isRowExpanded}
|
|
540
|
+
aria-label={isRowExpanded ? "Skjul under-rader" : "Vis under-rader"}
|
|
541
|
+
icon={
|
|
542
|
+
isRowExpanded ? (
|
|
543
|
+
<ChevronDownIcon aria-hidden />
|
|
544
|
+
) : (
|
|
545
|
+
<ChevronRightIcon aria-hidden />
|
|
546
|
+
)
|
|
547
|
+
}
|
|
548
|
+
/>
|
|
549
|
+
)}
|
|
550
|
+
</div>
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
|
|
446
554
|
function DataTableExpandedRow<T>({
|
|
447
555
|
rowId,
|
|
448
556
|
rowData,
|
|
449
|
-
|
|
557
|
+
fullWidthColSpan,
|
|
450
558
|
}: {
|
|
451
559
|
rowId: string | number;
|
|
452
560
|
rowData: T;
|
|
453
|
-
|
|
561
|
+
fullWidthColSpan: number;
|
|
454
562
|
}) {
|
|
455
563
|
const { tableId } = useDataTableContext();
|
|
456
|
-
const
|
|
564
|
+
const {
|
|
565
|
+
enableDetailsPanel,
|
|
566
|
+
isExpanded,
|
|
567
|
+
getDetailsPanelContent,
|
|
568
|
+
getDetailsPanelHeight,
|
|
569
|
+
} = useDataTableExpansion();
|
|
457
570
|
|
|
458
|
-
|
|
459
|
-
if (!expansionContext) {
|
|
571
|
+
if (!enableDetailsPanel) {
|
|
460
572
|
return null;
|
|
461
573
|
}
|
|
462
574
|
|
|
463
|
-
const { isExpanded, getDetailsPanelContent, getDetailsPanelHeight } =
|
|
464
|
-
expansionContext;
|
|
465
|
-
|
|
466
575
|
if (!isExpanded(rowId)) {
|
|
467
576
|
return null;
|
|
468
577
|
}
|
|
469
578
|
|
|
470
579
|
const content = getDetailsPanelContent?.(rowData);
|
|
580
|
+
const expansionId = getDataTableExpansionId(tableId, rowId);
|
|
471
581
|
|
|
472
582
|
if (!content) {
|
|
473
583
|
return null;
|
|
@@ -475,7 +585,7 @@ function DataTableExpandedRow<T>({
|
|
|
475
585
|
|
|
476
586
|
return (
|
|
477
587
|
<tr>
|
|
478
|
-
<td id={
|
|
588
|
+
<td id={expansionId} colSpan={fullWidthColSpan}>
|
|
479
589
|
<div style={{ height: getDetailsPanelHeight?.(rowData) }}>
|
|
480
590
|
{content}
|
|
481
591
|
</div>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createStrictContext } from "../../../utils/helpers";
|
|
2
|
+
import type { UseColumnOptionsResult } from "../hooks/useColumnOptions";
|
|
2
3
|
import type { UseTableSelectionReturn } from "../hooks/useTableSelection";
|
|
3
4
|
|
|
4
|
-
type DataTableContextProps = {
|
|
5
|
+
type DataTableContextProps<T> = {
|
|
5
6
|
layout: "fixed" | "auto";
|
|
6
7
|
withKeyboardNav: boolean;
|
|
7
8
|
selectionState: UseTableSelectionReturn;
|
|
@@ -16,10 +17,11 @@ type DataTableContextProps = {
|
|
|
16
17
|
disableRowSelectionOnClick: boolean;
|
|
17
18
|
isLoading?: boolean;
|
|
18
19
|
showLoadingOverlay: boolean;
|
|
20
|
+
columns: UseColumnOptionsResult<T>["columns"];
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
const { Provider: DataTableContextProvider, useContext: useDataTableContext } =
|
|
22
|
-
createStrictContext<DataTableContextProps
|
|
24
|
+
createStrictContext<DataTableContextProps<any>>({
|
|
23
25
|
name: "DataTableContext",
|
|
24
26
|
errorMessage: "useDataTableContext must be used within DataTable",
|
|
25
27
|
});
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
DataTableEmptyState,
|
|
11
11
|
type DataTableEmptyStateProps,
|
|
12
12
|
} from "../empty-state/DataTableEmptyState";
|
|
13
|
+
import { DataTableExpansionProvider } from "../hooks/useTableExpansion";
|
|
14
|
+
import type { ItemDetail } from "../hooks/useTableItems";
|
|
13
15
|
import { useTableKeyboardNav } from "../hooks/useTableKeyboardNav";
|
|
14
16
|
import { type SelectionProps } from "../hooks/useTableSelection";
|
|
15
17
|
import { noSelectionState } from "../hooks/useTableSelection";
|
|
@@ -34,6 +36,8 @@ import {
|
|
|
34
36
|
import { DataTableTr, type DataTableTrProps } from "../tr/DataTableTr";
|
|
35
37
|
import { DataTableContextProvider } from "./DataTableRoot.context";
|
|
36
38
|
|
|
39
|
+
const EMPTY_ITEM_DETAILS = new Map<never, ItemDetail<never>>();
|
|
40
|
+
|
|
37
41
|
interface DataTableProps
|
|
38
42
|
extends React.HTMLAttributes<HTMLTableElement>, SelectionProps {
|
|
39
43
|
children: React.ReactNode;
|
|
@@ -234,21 +238,24 @@ const DataTable = forwardRef<HTMLTableElement, DataTableProps>(
|
|
|
234
238
|
onRowClick={undefined}
|
|
235
239
|
disableRowSelectionOnClick={false}
|
|
236
240
|
showLoadingOverlay={false}
|
|
241
|
+
columns={[]}
|
|
237
242
|
>
|
|
238
|
-
<
|
|
239
|
-
<div className="aksel-data-
|
|
240
|
-
<
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
243
|
+
<DataTableExpansionProvider itemDetails={EMPTY_ITEM_DETAILS}>
|
|
244
|
+
<div className="aksel-data-table__border-wrapper">
|
|
245
|
+
<div className="aksel-data-table__scroll-wrapper">
|
|
246
|
+
<table
|
|
247
|
+
{...rest}
|
|
248
|
+
ref={mergedRef}
|
|
249
|
+
className={cl("aksel-data-table", className)}
|
|
250
|
+
data-zebra-stripes={zebraStripes}
|
|
251
|
+
data-truncate-content={truncateContent}
|
|
252
|
+
data-density={rowDensity}
|
|
253
|
+
data-layout={layout}
|
|
254
|
+
tabIndex={tabIndex}
|
|
255
|
+
/>
|
|
256
|
+
</div>
|
|
250
257
|
</div>
|
|
251
|
-
</
|
|
258
|
+
</DataTableExpansionProvider>
|
|
252
259
|
</DataTableContextProvider>
|
|
253
260
|
);
|
|
254
261
|
},
|