@multiplatform.one/table 7.10.0 → 7.15.0
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/README.md +61 -1
- package/dist/cjs/components/ChildTable/index.cjs +8 -2
- package/dist/cjs/components/ChildTable/index.native.js +8 -2
- package/dist/cjs/components/ChildTable/index.native.js.map +1 -1
- package/dist/cjs/components/ColumnHeaderFilter/index.cjs +77 -111
- package/dist/cjs/components/ColumnHeaderFilter/index.native.js +89 -129
- package/dist/cjs/components/ColumnHeaderFilter/index.native.js.map +1 -1
- package/dist/cjs/components/DataTable/DataTableCardControls.cjs +90 -54
- package/dist/cjs/components/DataTable/DataTableCardControls.native.js +93 -56
- package/dist/cjs/components/DataTable/DataTableCardControls.native.js.map +1 -1
- package/dist/cjs/components/DataTable/DataTableRoot.cjs +68 -9
- package/dist/cjs/components/DataTable/DataTableRoot.native.js +78 -11
- package/dist/cjs/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/cjs/components/DataTableCell/CellModalEditor.cjs +72 -70
- package/dist/cjs/components/DataTableCell/index.cjs +2 -2
- package/dist/cjs/components/DataTableCell/index.native.js +2 -11
- package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableDateFilter/index.cjs +135 -158
- package/dist/cjs/components/DataTableDateFilter/index.native.js +153 -183
- package/dist/cjs/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableExport/index.cjs +122 -139
- package/dist/cjs/components/DataTableExport/index.native.js +135 -157
- package/dist/cjs/components/DataTableExport/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableFilterButton/index.cjs +14 -0
- package/dist/cjs/components/DataTableFilterButton/index.native.js +15 -0
- package/dist/cjs/components/DataTableFilterButton/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableGrouping/index.cjs +192 -212
- package/dist/cjs/components/DataTableGrouping/index.native.js +209 -230
- package/dist/cjs/components/DataTableGrouping/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableSearch/index.cjs +103 -115
- package/dist/cjs/components/DataTableSearch/index.native.js +111 -123
- package/dist/cjs/components/DataTableSearch/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableToolbar/index.cjs +161 -101
- package/dist/cjs/components/DataTableToolbar/index.native.js +168 -109
- package/dist/cjs/components/DataTableToolbar/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableViewOptions/index.cjs +38 -54
- package/dist/cjs/components/DataTableViewOptions/index.native.js +40 -56
- package/dist/cjs/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/cjs/components/FilterOperatorValueEditor/index.cjs +3 -11
- package/dist/cjs/components/FilterOperatorValueEditor/index.native.js +3 -11
- package/dist/cjs/components/FilterOperatorValueEditor/index.native.js.map +1 -1
- package/dist/cjs/components/SimpleTable/index.cjs +2 -6
- package/dist/cjs/components/SimpleTable/index.native.js +2 -6
- package/dist/cjs/components/SimpleTable/index.native.js.map +1 -1
- package/dist/cjs/components/TableInput/RowDetailEditor.cjs +190 -0
- package/dist/cjs/components/TableInput/RowDetailEditor.native.js +172 -0
- package/dist/cjs/components/TableInput/RowDetailEditor.native.js.map +1 -0
- package/dist/cjs/components/TableInput/index.cjs +42 -5
- package/dist/cjs/components/TableInput/index.native.js +64 -1
- package/dist/cjs/components/TableInput/index.native.js.map +1 -1
- package/dist/cjs/components/TableInput/shared.cjs +8 -0
- package/dist/cjs/components/TableInput/shared.native.js +10 -0
- package/dist/cjs/components/TableInput/shared.native.js.map +1 -1
- package/dist/cjs/hooks/useDataTable.cjs +43 -2
- package/dist/cjs/hooks/useDataTable.native.js +49 -3
- package/dist/cjs/hooks/useDataTable.native.js.map +1 -1
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/utils/rowPress.cjs +4 -1
- package/dist/cjs/utils/rowPress.native.js +4 -1
- package/dist/cjs/utils/rowPress.native.js.map +1 -1
- package/dist/esm/components/ChildTable/index.mjs +8 -2
- package/dist/esm/components/ChildTable/index.mjs.map +1 -1
- package/dist/esm/components/ChildTable/index.native.js +8 -2
- package/dist/esm/components/ChildTable/index.native.js.map +1 -1
- package/dist/esm/components/ColumnHeaderFilter/index.mjs +80 -114
- package/dist/esm/components/ColumnHeaderFilter/index.mjs.map +1 -1
- package/dist/esm/components/ColumnHeaderFilter/index.native.js +92 -132
- package/dist/esm/components/ColumnHeaderFilter/index.native.js.map +1 -1
- package/dist/esm/components/DataTable/DataTableCardControls.mjs +89 -54
- package/dist/esm/components/DataTable/DataTableCardControls.mjs.map +1 -1
- package/dist/esm/components/DataTable/DataTableCardControls.native.js +92 -56
- package/dist/esm/components/DataTable/DataTableCardControls.native.js.map +1 -1
- package/dist/esm/components/DataTable/DataTableRoot.mjs +70 -11
- package/dist/esm/components/DataTable/DataTableRoot.mjs.map +1 -1
- package/dist/esm/components/DataTable/DataTableRoot.native.js +80 -13
- package/dist/esm/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/esm/components/DataTableCell/CellModalEditor.mjs +76 -74
- package/dist/esm/components/DataTableCell/CellModalEditor.mjs.map +1 -1
- package/dist/esm/components/DataTableCell/index.mjs +3 -3
- package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
- package/dist/esm/components/DataTableCell/index.native.js +3 -12
- package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
- package/dist/esm/components/DataTableDateFilter/index.mjs +137 -160
- package/dist/esm/components/DataTableDateFilter/index.mjs.map +1 -1
- package/dist/esm/components/DataTableDateFilter/index.native.js +155 -185
- package/dist/esm/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/esm/components/DataTableExport/index.mjs +124 -141
- package/dist/esm/components/DataTableExport/index.mjs.map +1 -1
- package/dist/esm/components/DataTableExport/index.native.js +137 -159
- package/dist/esm/components/DataTableExport/index.native.js.map +1 -1
- package/dist/esm/components/DataTableFilterButton/index.mjs +14 -1
- package/dist/esm/components/DataTableFilterButton/index.mjs.map +1 -1
- package/dist/esm/components/DataTableFilterButton/index.native.js +15 -1
- package/dist/esm/components/DataTableFilterButton/index.native.js.map +1 -1
- package/dist/esm/components/DataTableGrouping/index.mjs +194 -214
- package/dist/esm/components/DataTableGrouping/index.mjs.map +1 -1
- package/dist/esm/components/DataTableGrouping/index.native.js +211 -232
- package/dist/esm/components/DataTableGrouping/index.native.js.map +1 -1
- package/dist/esm/components/DataTableSearch/index.mjs +105 -117
- package/dist/esm/components/DataTableSearch/index.mjs.map +1 -1
- package/dist/esm/components/DataTableSearch/index.native.js +113 -125
- package/dist/esm/components/DataTableSearch/index.native.js.map +1 -1
- package/dist/esm/components/DataTableToolbar/index.mjs +165 -106
- package/dist/esm/components/DataTableToolbar/index.mjs.map +1 -1
- package/dist/esm/components/DataTableToolbar/index.native.js +172 -114
- package/dist/esm/components/DataTableToolbar/index.native.js.map +1 -1
- package/dist/esm/components/DataTableViewOptions/index.mjs +40 -56
- package/dist/esm/components/DataTableViewOptions/index.mjs.map +1 -1
- package/dist/esm/components/DataTableViewOptions/index.native.js +42 -58
- package/dist/esm/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/esm/components/FilterOperatorValueEditor/index.mjs +3 -11
- package/dist/esm/components/FilterOperatorValueEditor/index.mjs.map +1 -1
- package/dist/esm/components/FilterOperatorValueEditor/index.native.js +3 -11
- package/dist/esm/components/FilterOperatorValueEditor/index.native.js.map +1 -1
- package/dist/esm/components/SimpleTable/index.mjs +3 -7
- package/dist/esm/components/SimpleTable/index.mjs.map +1 -1
- package/dist/esm/components/SimpleTable/index.native.js +3 -7
- package/dist/esm/components/SimpleTable/index.native.js.map +1 -1
- package/dist/esm/components/TableInput/RowDetailEditor.mjs +165 -0
- package/dist/esm/components/TableInput/RowDetailEditor.mjs.map +1 -0
- package/dist/esm/components/TableInput/RowDetailEditor.native.js +144 -0
- package/dist/esm/components/TableInput/RowDetailEditor.native.js.map +1 -0
- package/dist/esm/components/TableInput/index.mjs +42 -5
- package/dist/esm/components/TableInput/index.mjs.map +1 -1
- package/dist/esm/components/TableInput/index.native.js +64 -1
- package/dist/esm/components/TableInput/index.native.js.map +1 -1
- package/dist/esm/components/TableInput/shared.mjs +7 -1
- package/dist/esm/components/TableInput/shared.mjs.map +1 -1
- package/dist/esm/components/TableInput/shared.native.js +9 -1
- package/dist/esm/components/TableInput/shared.native.js.map +1 -1
- package/dist/esm/hooks/useDataTable.mjs +43 -2
- package/dist/esm/hooks/useDataTable.mjs.map +1 -1
- package/dist/esm/hooks/useDataTable.native.js +49 -3
- package/dist/esm/hooks/useDataTable.native.js.map +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/utils/rowPress.mjs +4 -1
- package/dist/esm/utils/rowPress.mjs.map +1 -1
- package/dist/esm/utils/rowPress.native.js +4 -1
- package/dist/esm/utils/rowPress.native.js.map +1 -1
- package/dist/jsx/components/ChildTable/index.mjs +8 -2
- package/dist/jsx/components/ChildTable/index.mjs.map +1 -1
- package/dist/jsx/components/ChildTable/index.native.js +8 -2
- package/dist/jsx/components/ChildTable/index.native.js.map +1 -1
- package/dist/jsx/components/ColumnHeaderFilter/index.mjs +80 -114
- package/dist/jsx/components/ColumnHeaderFilter/index.mjs.map +1 -1
- package/dist/jsx/components/ColumnHeaderFilter/index.native.js +89 -129
- package/dist/jsx/components/ColumnHeaderFilter/index.native.js.map +1 -1
- package/dist/jsx/components/DataTable/DataTableCardControls.mjs +89 -54
- package/dist/jsx/components/DataTable/DataTableCardControls.mjs.map +1 -1
- package/dist/jsx/components/DataTable/DataTableCardControls.native.js +93 -56
- package/dist/jsx/components/DataTable/DataTableCardControls.native.js.map +1 -1
- package/dist/jsx/components/DataTable/DataTableRoot.mjs +70 -11
- package/dist/jsx/components/DataTable/DataTableRoot.mjs.map +1 -1
- package/dist/jsx/components/DataTable/DataTableRoot.native.js +78 -11
- package/dist/jsx/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/jsx/components/DataTableCell/CellModalEditor.mjs +76 -74
- package/dist/jsx/components/DataTableCell/CellModalEditor.mjs.map +1 -1
- package/dist/jsx/components/DataTableCell/index.mjs +3 -3
- package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableCell/index.native.js +2 -11
- package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableDateFilter/index.mjs +137 -160
- package/dist/jsx/components/DataTableDateFilter/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableDateFilter/index.native.js +153 -183
- package/dist/jsx/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableExport/index.mjs +124 -141
- package/dist/jsx/components/DataTableExport/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableExport/index.native.js +135 -157
- package/dist/jsx/components/DataTableExport/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableFilterButton/index.mjs +14 -1
- package/dist/jsx/components/DataTableFilterButton/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableFilterButton/index.native.js +15 -0
- package/dist/jsx/components/DataTableFilterButton/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableGrouping/index.mjs +194 -214
- package/dist/jsx/components/DataTableGrouping/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableGrouping/index.native.js +209 -230
- package/dist/jsx/components/DataTableGrouping/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableSearch/index.mjs +105 -117
- package/dist/jsx/components/DataTableSearch/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableSearch/index.native.js +111 -123
- package/dist/jsx/components/DataTableSearch/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableToolbar/index.mjs +165 -106
- package/dist/jsx/components/DataTableToolbar/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableToolbar/index.native.js +168 -109
- package/dist/jsx/components/DataTableToolbar/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableViewOptions/index.mjs +40 -56
- package/dist/jsx/components/DataTableViewOptions/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableViewOptions/index.native.js +40 -56
- package/dist/jsx/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/jsx/components/FilterOperatorValueEditor/index.mjs +3 -11
- package/dist/jsx/components/FilterOperatorValueEditor/index.mjs.map +1 -1
- package/dist/jsx/components/FilterOperatorValueEditor/index.native.js +3 -11
- package/dist/jsx/components/FilterOperatorValueEditor/index.native.js.map +1 -1
- package/dist/jsx/components/SimpleTable/index.mjs +3 -7
- package/dist/jsx/components/SimpleTable/index.mjs.map +1 -1
- package/dist/jsx/components/SimpleTable/index.native.js +2 -6
- package/dist/jsx/components/SimpleTable/index.native.js.map +1 -1
- package/dist/jsx/components/TableInput/RowDetailEditor.mjs +165 -0
- package/dist/jsx/components/TableInput/RowDetailEditor.mjs.map +1 -0
- package/dist/jsx/components/TableInput/RowDetailEditor.native.js +172 -0
- package/dist/jsx/components/TableInput/RowDetailEditor.native.js.map +1 -0
- package/dist/jsx/components/TableInput/index.mjs +42 -5
- package/dist/jsx/components/TableInput/index.mjs.map +1 -1
- package/dist/jsx/components/TableInput/index.native.js +64 -1
- package/dist/jsx/components/TableInput/index.native.js.map +1 -1
- package/dist/jsx/components/TableInput/shared.mjs +7 -1
- package/dist/jsx/components/TableInput/shared.mjs.map +1 -1
- package/dist/jsx/components/TableInput/shared.native.js +10 -0
- package/dist/jsx/components/TableInput/shared.native.js.map +1 -1
- package/dist/jsx/hooks/useDataTable.mjs +43 -2
- package/dist/jsx/hooks/useDataTable.mjs.map +1 -1
- package/dist/jsx/hooks/useDataTable.native.js +49 -3
- package/dist/jsx/hooks/useDataTable.native.js.map +1 -1
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/utils/rowPress.mjs +4 -1
- package/dist/jsx/utils/rowPress.mjs.map +1 -1
- package/dist/jsx/utils/rowPress.native.js +4 -1
- package/dist/jsx/utils/rowPress.native.js.map +1 -1
- package/package.json +16 -16
- package/src/components/ChildTable/childTable.cellHidden.spec.tsx +57 -0
- package/src/components/ChildTable/childTable.rowDetail.spec.tsx +120 -0
- package/src/components/ChildTable/index.tsx +16 -0
- package/src/components/ColumnHeaderFilter/index.tsx +89 -116
- package/src/components/DataTable/DataTable.stories.tsx +130 -6
- package/src/components/DataTable/DataTableCardControls.stories.tsx +61 -2
- package/src/components/DataTable/DataTableCardControls.tsx +92 -66
- package/src/components/DataTable/DataTableRoot.tsx +109 -18
- package/src/components/DataTable/cardLayout.spec.tsx +4 -3
- package/src/components/DataTable/cardToolbar.spec.tsx +238 -0
- package/src/components/DataTable/chromeInterplay.spec.tsx +7 -5
- package/src/components/DataTable/columnWidthLayout.spec.tsx +189 -0
- package/src/components/DataTable/dataTableCardControls.spec.tsx +3 -2
- package/src/components/DataTable/dataTableMobileCards.spec.tsx +1 -4
- package/src/components/DataTable/footerLayout.spec.tsx +250 -0
- package/src/components/DataTable/headerContainment.spec.tsx +91 -0
- package/src/components/DataTable/pinningGeometry.spec.tsx +66 -1
- package/src/components/DataTable/rowPress.spec.tsx +2 -6
- package/src/components/DataTableBulkActions/DataTableBulkActions.stories.tsx +5 -4
- package/src/components/DataTableCell/CellModalEditor.tsx +95 -75
- package/src/components/DataTableCell/cellDraftPolicy.spec.tsx +12 -2
- package/src/components/DataTableCell/cellModalEditor.spec.tsx +75 -49
- package/src/components/DataTableCell/dataTableCell.spec.tsx +31 -14
- package/src/components/DataTableCell/index.tsx +3 -2
- package/src/components/DataTableDateFilter/DataTableDateFilter.spec.tsx +6 -3
- package/src/components/DataTableDateFilter/index.tsx +151 -180
- package/src/components/DataTableExport/DataTableExport.spec.tsx +42 -18
- package/src/components/DataTableExport/index.tsx +135 -142
- package/src/components/DataTableFilterButton/index.tsx +9 -0
- package/src/components/DataTableGrouping/index.tsx +193 -195
- package/src/components/DataTableSearch/index.tsx +111 -109
- package/src/components/DataTableToolbar/index.tsx +179 -107
- package/src/components/DataTableViewOptions/DataTableViewOptions.stories.tsx +52 -0
- package/src/components/DataTableViewOptions/index.tsx +32 -40
- package/src/components/FilterOperatorValueEditor/FilterOperatorValueEditor.spec.tsx +64 -2
- package/src/components/FilterOperatorValueEditor/index.tsx +0 -8
- package/src/components/SimpleTable/index.tsx +3 -2
- package/src/components/SimpleTable/simpleTable.spec.tsx +15 -0
- package/src/components/Table/accessibility.spec.tsx +7 -19
- package/src/components/TableInput/RowDetailEditor.native.tsx +134 -0
- package/src/components/TableInput/RowDetailEditor.tsx +173 -0
- package/src/components/TableInput/index.native.tsx +49 -1
- package/src/components/TableInput/index.tsx +65 -10
- package/src/components/TableInput/shared.ts +29 -0
- package/src/components/tableDropdowns.spec.tsx +172 -0
- package/src/hooks/useDataTable.spec.ts +153 -0
- package/src/hooks/useDataTable.ts +38 -2
- package/src/index.ts +1 -0
- package/src/types.ts +14 -12
- package/src/utils/rowPress.ts +3 -0
- package/types/components/ChildTable/index.d.ts +10 -0
- package/types/components/ChildTable/index.d.ts.map +1 -1
- package/types/components/ColumnHeaderFilter/index.d.ts +1 -1
- package/types/components/ColumnHeaderFilter/index.d.ts.map +1 -1
- package/types/components/DataTable/DataTableCardControls.d.ts +3 -0
- package/types/components/DataTable/DataTableCardControls.d.ts.map +1 -1
- package/types/components/DataTable/DataTableRoot.d.ts.map +1 -1
- package/types/components/DataTableCell/CellModalEditor.d.ts +10 -7
- package/types/components/DataTableCell/CellModalEditor.d.ts.map +1 -1
- package/types/components/DataTableCell/index.d.ts.map +1 -1
- package/types/components/DataTableDateFilter/index.d.ts.map +1 -1
- package/types/components/DataTableFilterButton/index.d.ts +2 -1
- package/types/components/DataTableFilterButton/index.d.ts.map +1 -1
- package/types/components/DataTableGrouping/index.d.ts.map +1 -1
- package/types/components/DataTableSearch/index.d.ts.map +1 -1
- package/types/components/DataTableToolbar/index.d.ts +1 -0
- package/types/components/DataTableToolbar/index.d.ts.map +1 -1
- package/types/components/FilterOperatorValueEditor/index.d.ts.map +1 -1
- package/types/components/SimpleTable/index.d.ts.map +1 -1
- package/types/components/TableInput/RowDetailEditor.d.ts +24 -0
- package/types/components/TableInput/RowDetailEditor.d.ts.map +1 -0
- package/types/components/TableInput/RowDetailEditor.native.d.ts +11 -0
- package/types/components/TableInput/RowDetailEditor.native.d.ts.map +1 -0
- package/types/components/TableInput/index.d.ts +11 -2
- package/types/components/TableInput/index.d.ts.map +1 -1
- package/types/components/TableInput/index.native.d.ts +8 -1
- package/types/components/TableInput/index.native.d.ts.map +1 -1
- package/types/components/TableInput/shared.d.ts +11 -0
- package/types/components/TableInput/shared.d.ts.map +1 -1
- package/types/hooks/useDataTable.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/types.d.ts +14 -12
- package/types/types.d.ts.map +1 -1
- package/types/utils/rowPress.d.ts.map +1 -1
|
@@ -11,12 +11,34 @@
|
|
|
11
11
|
* render. The combination warns instead, naming `columnLayout` as the reason.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { fireEvent, waitFor } from "@testing-library/react";
|
|
14
15
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
15
16
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
16
17
|
import type { DataTableColumn } from "../../types";
|
|
17
18
|
import { __resetTableDevWarnSeen, warnColumnWidthIgnored } from "../../shared/devWarn";
|
|
18
19
|
import { DataTable } from "./index";
|
|
19
20
|
|
|
21
|
+
vi.mock("@tanstack/react-virtual", async (importOriginal) => {
|
|
22
|
+
const actual = await importOriginal<typeof import("@tanstack/react-virtual")>();
|
|
23
|
+
return {
|
|
24
|
+
...actual,
|
|
25
|
+
useVirtualizer: (options: Parameters<typeof actual.useVirtualizer>[0]) =>
|
|
26
|
+
actual.useVirtualizer({
|
|
27
|
+
...options,
|
|
28
|
+
initialRect: { width: 800, height: 240 },
|
|
29
|
+
observeElementRect: (_instance, callback) => {
|
|
30
|
+
callback({ width: 800, height: 240 });
|
|
31
|
+
return () => {};
|
|
32
|
+
},
|
|
33
|
+
observeElementOffset: (_instance, callback) => {
|
|
34
|
+
callback(0, false);
|
|
35
|
+
return () => {};
|
|
36
|
+
},
|
|
37
|
+
scrollToFn: () => {},
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
|
|
20
42
|
type Pokemon = { id: number; name: string; xp: number };
|
|
21
43
|
|
|
22
44
|
const sampleData: Pokemon[] = [
|
|
@@ -193,3 +215,170 @@ describe("DataTable columnLayout=equal with per-column widths", () => {
|
|
|
193
215
|
expect(widthWarnings()).toHaveLength(0);
|
|
194
216
|
});
|
|
195
217
|
});
|
|
218
|
+
|
|
219
|
+
function hasSizingStyle(element: HTMLElement, property: string, value: string, token: string) {
|
|
220
|
+
return (
|
|
221
|
+
element.style.getPropertyValue(property) === value || String(element.className).includes(token)
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function expectCellWidth(element: HTMLElement | null, width: number) {
|
|
226
|
+
expect(element).not.toBeNull();
|
|
227
|
+
expect(hasSizingStyle(element!, "width", `${width}px`, `_w-${width}px`)).toBe(true);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
describe("DataTable declared column sizes", () => {
|
|
231
|
+
beforeEach(startCapture);
|
|
232
|
+
afterEach(stopCapture);
|
|
233
|
+
|
|
234
|
+
it.each(["alias", "canonical"])(
|
|
235
|
+
"applies %s widths to scroll headers, body and footer below the old floor",
|
|
236
|
+
(spelling) => {
|
|
237
|
+
const sizingColumns = plainColumns.map((column, index) => ({
|
|
238
|
+
...column,
|
|
239
|
+
[spelling === "alias" ? "width" : "size"]: index === 0 ? 320 : 48,
|
|
240
|
+
footer: index === 0 ? "Totals" : "126",
|
|
241
|
+
}));
|
|
242
|
+
const { container } = renderTable(sizingColumns, "scroll");
|
|
243
|
+
expectCellWidth(container.querySelector("#name-header"), 320);
|
|
244
|
+
expectCellWidth(container.querySelector("#xp-header"), 48);
|
|
245
|
+
const cells = container.querySelectorAll('[aria-label="Table data"] [role="cell"]');
|
|
246
|
+
expect(cells.length).toBeGreaterThanOrEqual(4);
|
|
247
|
+
expectCellWidth(cells[0] as HTMLElement, 320);
|
|
248
|
+
expectCellWidth(cells[1] as HTMLElement, 48);
|
|
249
|
+
expect(hasSizingStyle(cells[1] as HTMLElement, "min-width", "0px", "_miw-0px")).toBe(true);
|
|
250
|
+
const footer = container.querySelectorAll('tfoot [role="cell"]');
|
|
251
|
+
expect(footer).toHaveLength(2);
|
|
252
|
+
expectCellWidth(footer[0] as HTMLElement, 320);
|
|
253
|
+
expectCellWidth(footer[1] as HTMLElement, 48);
|
|
254
|
+
expect(widthWarnings()).toHaveLength(0);
|
|
255
|
+
},
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
it("gives unpinned equal cells the same flex policy for canonical and alias declarations", () => {
|
|
259
|
+
const canonical = plainColumns.map((column, index) => ({
|
|
260
|
+
...column,
|
|
261
|
+
size: index === 0 ? 320 : 80,
|
|
262
|
+
}));
|
|
263
|
+
const withSizes = renderTable(canonical);
|
|
264
|
+
const sizeMarkup = stripGeneratedIds(withSizes.container.innerHTML);
|
|
265
|
+
withSizes.unmount();
|
|
266
|
+
const withoutSizes = renderTable(plainColumns);
|
|
267
|
+
expect(stripGeneratedIds(withoutSizes.container.innerHTML)).toBe(sizeMarkup);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("lets resizing use aliases in equal mode while preserving the fixed flex basis", () => {
|
|
271
|
+
const { container } = renderWithProviders(
|
|
272
|
+
<DataTable
|
|
273
|
+
data={sampleData}
|
|
274
|
+
columns={widthColumns}
|
|
275
|
+
columnLayout="equal"
|
|
276
|
+
layout="table"
|
|
277
|
+
enableColumnResizing
|
|
278
|
+
enablePagination={false}
|
|
279
|
+
readOnly
|
|
280
|
+
/>,
|
|
281
|
+
);
|
|
282
|
+
const header = container.querySelector("#name-header") as HTMLElement;
|
|
283
|
+
expectCellWidth(header, 320);
|
|
284
|
+
expect(hasSizingStyle(header, "flex-basis", "auto", "_fb-auto")).toBe(true);
|
|
285
|
+
expect(hasSizingStyle(header, "flex-grow", "0", "_fg-0")).toBe(true);
|
|
286
|
+
expect(widthWarnings()).toHaveLength(0);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("does not inflate a 48px resizable equal-layout cell with the primitive width floor", () => {
|
|
290
|
+
const { container } = renderWithProviders(
|
|
291
|
+
<DataTable
|
|
292
|
+
data={sampleData}
|
|
293
|
+
columns={[{ accessorKey: "xp", header: "XP", width: 48 }]}
|
|
294
|
+
columnLayout="equal"
|
|
295
|
+
layout="table"
|
|
296
|
+
enableColumnResizing
|
|
297
|
+
enablePagination={false}
|
|
298
|
+
readOnly
|
|
299
|
+
/>,
|
|
300
|
+
);
|
|
301
|
+
const header = container.querySelector("#xp-header") as HTMLElement;
|
|
302
|
+
expectCellWidth(header, 48);
|
|
303
|
+
expect(hasSizingStyle(header, "min-width", "0px", "_miw-0px")).toBe(true);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("passes size inputs to auto layout without changing its table algorithm", () => {
|
|
307
|
+
const { container } = renderTable(widthColumns, "auto");
|
|
308
|
+
expectCellWidth(container.querySelector("#name-header"), 320);
|
|
309
|
+
expect((container.querySelector('[role="table"]') as HTMLElement).style.tableLayout).toBe(
|
|
310
|
+
"fixed",
|
|
311
|
+
);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("resizes an aliased column through the real header handle within its bounds", () => {
|
|
315
|
+
const columns = widthColumns.map((column) => ({ ...column, minWidth: 50, maxWidth: 350 }));
|
|
316
|
+
const { container, getByRole } = renderWithProviders(
|
|
317
|
+
<DataTable
|
|
318
|
+
data={sampleData}
|
|
319
|
+
columns={columns}
|
|
320
|
+
columnLayout="scroll"
|
|
321
|
+
layout="table"
|
|
322
|
+
enableColumnResizing
|
|
323
|
+
enablePagination={false}
|
|
324
|
+
readOnly
|
|
325
|
+
/>,
|
|
326
|
+
);
|
|
327
|
+
const handle = getByRole("separator", { name: "Resize Name column" });
|
|
328
|
+
expect(handle.getAttribute("aria-valuenow")).toBe("320");
|
|
329
|
+
fireEvent.mouseDown(handle, { clientX: 100 });
|
|
330
|
+
fireEvent.mouseMove(document, { clientX: 120 });
|
|
331
|
+
fireEvent.mouseUp(document, { clientX: 120 });
|
|
332
|
+
expectCellWidth(container.querySelector("#name-header"), 340);
|
|
333
|
+
fireEvent.mouseDown(handle, { clientX: 100 });
|
|
334
|
+
fireEvent.mouseMove(document, { clientX: 300 });
|
|
335
|
+
fireEvent.mouseUp(document, { clientX: 300 });
|
|
336
|
+
expectCellWidth(container.querySelector("#name-header"), 350);
|
|
337
|
+
expect(handle.getAttribute("aria-valuenow")).toBe("350");
|
|
338
|
+
const cell = container.querySelector<HTMLElement>('[aria-label="Table data"] [role="cell"]');
|
|
339
|
+
expectCellWidth(cell, 350);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("exposes the normalized size to the view-options display on mount", async () => {
|
|
343
|
+
const { getByText } = renderWithProviders(
|
|
344
|
+
<DataTable
|
|
345
|
+
data={sampleData}
|
|
346
|
+
columns={widthColumns}
|
|
347
|
+
columnLayout="scroll"
|
|
348
|
+
layout="table"
|
|
349
|
+
enableColumnResizing
|
|
350
|
+
enablePagination={false}
|
|
351
|
+
readOnly
|
|
352
|
+
>
|
|
353
|
+
<DataTable.ViewOptions showColumnResizing showColumnVisibility={false} />
|
|
354
|
+
</DataTable>,
|
|
355
|
+
);
|
|
356
|
+
await waitFor(() => expect(getByText("320px")).toBeDefined());
|
|
357
|
+
expect(getByText("80px")).toBeDefined();
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("uses the real virtual row renderer with the same declared widths", async () => {
|
|
361
|
+
const { container } = renderWithProviders(
|
|
362
|
+
<DataTable
|
|
363
|
+
data={sampleData}
|
|
364
|
+
columns={widthColumns}
|
|
365
|
+
columnLayout="scroll"
|
|
366
|
+
layout="table"
|
|
367
|
+
enableVirtualization
|
|
368
|
+
virtualizationHeight={240}
|
|
369
|
+
enablePagination={false}
|
|
370
|
+
readOnly
|
|
371
|
+
/>,
|
|
372
|
+
);
|
|
373
|
+
await waitFor(() => {
|
|
374
|
+
const virtualBody = container.querySelector('tbody[role="presentation"] [role="rowgroup"]');
|
|
375
|
+
expect(virtualBody).not.toBeNull();
|
|
376
|
+
expect(virtualBody!.querySelectorAll('[role="row"]')).toHaveLength(2);
|
|
377
|
+
const cells = virtualBody!.querySelectorAll('[role="cell"]');
|
|
378
|
+
expect(cells.length).toBeGreaterThanOrEqual(4);
|
|
379
|
+
expectCellWidth(cells[0] as HTMLElement, 320);
|
|
380
|
+
expectCellWidth(cells[1] as HTMLElement, 80);
|
|
381
|
+
});
|
|
382
|
+
expectCellWidth(container.querySelector("#name-header"), 320);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
@@ -42,7 +42,7 @@ function renderCards(props: Record<string, any> = {}) {
|
|
|
42
42
|
describe("DataTableCardControls", () => {
|
|
43
43
|
it("is a labelled toolbar above the card list", () => {
|
|
44
44
|
renderCards({ enableSorting: true, enableFiltering: true });
|
|
45
|
-
const toolbar = screen.getByRole("toolbar", { name: "
|
|
45
|
+
const toolbar = screen.getByRole("toolbar", { name: "Table toolbar" });
|
|
46
46
|
expect(toolbar).toBeTruthy();
|
|
47
47
|
expect(screen.getByLabelText("Sort rows")).toBeTruthy();
|
|
48
48
|
});
|
|
@@ -61,7 +61,8 @@ describe("DataTableCardControls", () => {
|
|
|
61
61
|
it("filtering only: the filter chrome is there, the sort menu is not", () => {
|
|
62
62
|
renderCards({ enableSorting: false, enableFiltering: true });
|
|
63
63
|
expect(screen.queryByLabelText("Sort rows")).toBeNull();
|
|
64
|
-
expect(screen.
|
|
64
|
+
expect(screen.getByRole("button", { name: "Filter" })).toBeTruthy();
|
|
65
|
+
expect(screen.queryByText("Add Filter")).toBeNull();
|
|
65
66
|
});
|
|
66
67
|
|
|
67
68
|
it("with no sort applied the trigger reads the neutral label", () => {
|
|
@@ -102,12 +102,9 @@ describe("DataTableMobileCards", () => {
|
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
it("a control keeps a row of its own and prints no label", () => {
|
|
105
|
-
// Explicit id on BOTH columns: DataTable seeds columnOrder from
|
|
106
|
-
// `col.id || ""`, so an id-less accessor column beside an id'd one would
|
|
107
|
-
// put `_actions` first and make the control the card title instead.
|
|
108
105
|
const { container } = renderCards({
|
|
109
106
|
columns: [
|
|
110
|
-
{
|
|
107
|
+
{ accessorKey: "firstName", header: "First Name" },
|
|
111
108
|
{ id: "_actions", accessorKey: "lastName", header: "Actions", meta: { role: "actions" } },
|
|
112
109
|
],
|
|
113
110
|
});
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
2
|
+
import { Preset } from "@multiplatform.one/theme";
|
|
3
|
+
import { fireEvent, waitFor } from "@testing-library/react";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import type { DataTableColumn } from "../../types";
|
|
6
|
+
import { DataTable } from "./index";
|
|
7
|
+
|
|
8
|
+
// Happy DOM has no viewport measurements. Keep the actual virtualizer and row renderer.
|
|
9
|
+
vi.mock("@tanstack/react-virtual", async (importOriginal) => {
|
|
10
|
+
const actual = await importOriginal<typeof import("@tanstack/react-virtual")>();
|
|
11
|
+
return {
|
|
12
|
+
...actual,
|
|
13
|
+
useVirtualizer: (options: Parameters<typeof actual.useVirtualizer>[0]) =>
|
|
14
|
+
actual.useVirtualizer({
|
|
15
|
+
...options,
|
|
16
|
+
initialRect: { width: 800, height: 240 },
|
|
17
|
+
observeElementRect: (_instance, callback) => {
|
|
18
|
+
callback({ width: 800, height: 240 });
|
|
19
|
+
return () => {};
|
|
20
|
+
},
|
|
21
|
+
observeElementOffset: (_instance, callback) => {
|
|
22
|
+
callback(0, false);
|
|
23
|
+
return () => {};
|
|
24
|
+
},
|
|
25
|
+
scrollToFn: () => {},
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const data = [{ id: "1", account: "Account 1", balance: "10.00" }];
|
|
31
|
+
const columns: DataTableColumn<(typeof data)[number]>[] = [
|
|
32
|
+
{ accessorKey: "account", header: "Account", size: 320, footer: "Totals" },
|
|
33
|
+
{ accessorKey: "balance", header: "Balance", size: 80, footer: "10.00" },
|
|
34
|
+
];
|
|
35
|
+
const allAuxiliaries = {
|
|
36
|
+
enableRowSelection: true,
|
|
37
|
+
showRowNumbers: true,
|
|
38
|
+
enableRowExpansion: true,
|
|
39
|
+
showDeleteRow: true,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function renderTable(props = {}, size: "small" | "medium" | "large" = "medium") {
|
|
43
|
+
return renderWithProviders(
|
|
44
|
+
<Preset overrides={{ size }}>
|
|
45
|
+
<DataTable
|
|
46
|
+
data={data}
|
|
47
|
+
columns={columns}
|
|
48
|
+
layout="table"
|
|
49
|
+
columnLayout="scroll"
|
|
50
|
+
enablePagination={false}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
</Preset>,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function cells(row: Element): HTMLElement[] {
|
|
58
|
+
return [...row.children].filter((element) =>
|
|
59
|
+
["cell", "columnheader"].includes(element.getAttribute("role") ?? ""),
|
|
60
|
+
) as HTMLElement[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function width(cell: HTMLElement): number {
|
|
64
|
+
return Number.parseFloat(
|
|
65
|
+
cell.style.width || /(?:^| )_w-([\d.]+)px(?: |$)/.exec(cell.className)?.[1] || "NaN",
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function rows(container: HTMLElement) {
|
|
70
|
+
const header = container.querySelector('thead [role="row"]')!;
|
|
71
|
+
const body = container.querySelector('[aria-label="Row 1"]')!;
|
|
72
|
+
const footer = container.querySelector('tfoot [role="row"]')!;
|
|
73
|
+
expect(header).not.toBeNull();
|
|
74
|
+
expect(body).not.toBeNull();
|
|
75
|
+
expect(footer).not.toBeNull();
|
|
76
|
+
return { header: cells(header), body: cells(body), footer: cells(footer) };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function expectPinned(cell: HTMLElement, side: "start" | "end", offset: number) {
|
|
80
|
+
expect(cell.style.position).toBe("sticky");
|
|
81
|
+
expect(cell.style[side === "start" ? "insetInlineStart" : "insetInlineEnd"]).toBe(`${offset}px`);
|
|
82
|
+
expect(cell.style.zIndex).toBe("2");
|
|
83
|
+
expect(cell.style.backgroundColor).toBe("var(--color2)");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let previousDirection: string | null;
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
previousDirection = document.documentElement.getAttribute("dir");
|
|
89
|
+
});
|
|
90
|
+
afterEach(() => {
|
|
91
|
+
if (previousDirection === null) document.documentElement.removeAttribute("dir");
|
|
92
|
+
else document.documentElement.setAttribute("dir", previousDirection);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe.each(["ltr", "rtl"])("footer layout in %s", (direction) => {
|
|
96
|
+
beforeEach(() => document.documentElement.setAttribute("dir", direction));
|
|
97
|
+
|
|
98
|
+
it.each([
|
|
99
|
+
{ name: "no auxiliaries", props: {}, leading: 0, trailing: 0 },
|
|
100
|
+
{ name: "selection", props: { enableRowSelection: true }, leading: 1, trailing: 0 },
|
|
101
|
+
{ name: "row numbers", props: { showRowNumbers: true }, leading: 1, trailing: 0 },
|
|
102
|
+
{ name: "expansion", props: { enableRowExpansion: true }, leading: 1, trailing: 0 },
|
|
103
|
+
{ name: "delete", props: { showDeleteRow: true }, leading: 0, trailing: 1 },
|
|
104
|
+
{ name: "all auxiliaries", props: allAuxiliaries, leading: 3, trailing: 1 },
|
|
105
|
+
{
|
|
106
|
+
name: "read-only auxiliaries",
|
|
107
|
+
props: { ...allAuxiliaries, readOnly: true },
|
|
108
|
+
leading: 3,
|
|
109
|
+
trailing: 0,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "read-only delete suppressed",
|
|
113
|
+
props: { showDeleteRow: true, readOnly: true },
|
|
114
|
+
leading: 0,
|
|
115
|
+
trailing: 0,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "compact auxiliaries",
|
|
119
|
+
props: { ...allAuxiliaries, compact: true },
|
|
120
|
+
leading: 3,
|
|
121
|
+
trailing: 1,
|
|
122
|
+
},
|
|
123
|
+
])("matches resolved header/body slots with $name", ({ props, leading, trailing }) => {
|
|
124
|
+
const { container } = renderTable(props);
|
|
125
|
+
const row = rows(container);
|
|
126
|
+
const headerWidths = row.header.map(width);
|
|
127
|
+
expect(row.header).toHaveLength(leading + 2 + trailing);
|
|
128
|
+
expect(headerWidths.every((value) => Number.isFinite(value) && value > 0)).toBe(true);
|
|
129
|
+
expect(headerWidths.slice(leading, leading + 2)).toEqual([320, 80]);
|
|
130
|
+
expect(row.body.map(width)).toEqual(headerWidths);
|
|
131
|
+
expect(row.footer.map(width)).toEqual(headerWidths);
|
|
132
|
+
expect(row.footer[leading]).toHaveTextContent("Totals");
|
|
133
|
+
expect(row.footer[leading + 1]).toHaveTextContent("10.00");
|
|
134
|
+
const auxiliaryCells = [...row.footer.slice(0, leading), ...row.footer.slice(leading + 2)];
|
|
135
|
+
for (const cell of auxiliaryCells) {
|
|
136
|
+
expect(cell.textContent).toBe("");
|
|
137
|
+
expect(cell.tabIndex).toBe(-1);
|
|
138
|
+
expect(
|
|
139
|
+
cell.querySelector('button, input, [role="button"], [role="checkbox"], [tabindex]'),
|
|
140
|
+
).toBeNull();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it.each(["left", "right"] as const)(
|
|
145
|
+
"resolves pinned %s footer paint like header/body",
|
|
146
|
+
async (side) => {
|
|
147
|
+
const utils = renderTable({ enableColumnPinning: true });
|
|
148
|
+
fireEvent.click(utils.getByLabelText("Pin Account column left"));
|
|
149
|
+
if (side === "right") fireEvent.click(utils.getByLabelText("Pin Account column right"));
|
|
150
|
+
await waitFor(() =>
|
|
151
|
+
expect(
|
|
152
|
+
(utils.container.querySelector("#account-header") as HTMLElement).style.position,
|
|
153
|
+
).toBe("sticky"),
|
|
154
|
+
);
|
|
155
|
+
const row = rows(utils.container);
|
|
156
|
+
const index = row.header.findIndex((cell) => cell.id === "account-header");
|
|
157
|
+
const logicalSide = side === "left" ? "start" : "end";
|
|
158
|
+
expectPinned(row.header[index], logicalSide, 0);
|
|
159
|
+
expectPinned(row.body[index], logicalSide, 0);
|
|
160
|
+
expectPinned(row.footer[index], logicalSide, 0);
|
|
161
|
+
expect(row.footer[1 - index].style.position).not.toBe("sticky");
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
it("follows resolved auxiliary widths at small and large preset sizes", () => {
|
|
166
|
+
const widthsBySize = ["small", "large"].map((size) => {
|
|
167
|
+
const utils = renderTable(allAuxiliaries, size as "small" | "large");
|
|
168
|
+
const row = rows(utils.container);
|
|
169
|
+
const headerWidths = row.header.map(width);
|
|
170
|
+
expect(headerWidths).toHaveLength(6);
|
|
171
|
+
expect(headerWidths.slice(3, 5)).toEqual([320, 80]);
|
|
172
|
+
expect(row.body.map(width)).toEqual(headerWidths);
|
|
173
|
+
expect(row.footer.map(width)).toEqual(headerWidths);
|
|
174
|
+
utils.unmount();
|
|
175
|
+
return headerWidths;
|
|
176
|
+
});
|
|
177
|
+
for (const index of [0, 1, 2, 5]) {
|
|
178
|
+
expect(widthsBySize[0][index]).toBeLessThan(widthsBySize[1][index]);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it.each([false, true])(
|
|
183
|
+
"reserves auxiliary flow around both pin sides, readOnly=%s",
|
|
184
|
+
async (readOnly) => {
|
|
185
|
+
const utils = renderTable({ ...allAuxiliaries, readOnly, enableColumnPinning: true });
|
|
186
|
+
fireEvent.click(utils.getByLabelText("Pin Account column left"));
|
|
187
|
+
fireEvent.click(utils.getByLabelText("Pin Balance column left"));
|
|
188
|
+
fireEvent.click(utils.getByLabelText("Pin Balance column right"));
|
|
189
|
+
await waitFor(() => expect(rows(utils.container).header[3].style.position).toBe("sticky"));
|
|
190
|
+
const row = rows(utils.container);
|
|
191
|
+
const headerWidths = row.header.map(width);
|
|
192
|
+
const leadingWidth = headerWidths.slice(0, 3).reduce((sum, value) => sum + value, 0);
|
|
193
|
+
const trailingWidth = readOnly ? 0 : headerWidths[5];
|
|
194
|
+
expect(leadingWidth).toBeGreaterThan(0);
|
|
195
|
+
expect(row.body.map(width)).toEqual(headerWidths);
|
|
196
|
+
expect(row.footer.map(width)).toEqual(headerWidths);
|
|
197
|
+
for (const section of [row.header, row.body, row.footer]) {
|
|
198
|
+
expectPinned(section[3], "start", leadingWidth);
|
|
199
|
+
expectPinned(section[4], "end", trailingWidth);
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
it("keeps actual virtual rows and auxiliary footer slots aligned after pinned resize", async () => {
|
|
205
|
+
const utils = renderWithProviders(
|
|
206
|
+
<DataTable
|
|
207
|
+
data={[
|
|
208
|
+
{ id: "1", rank: 1, label: "Record 1" },
|
|
209
|
+
{ id: "2", rank: 2, label: "Record 2" },
|
|
210
|
+
]}
|
|
211
|
+
columns={[
|
|
212
|
+
{ accessorKey: "rank", header: "Rank", size: 48, minSize: 32, maxSize: 64, footer: "2" },
|
|
213
|
+
{ accessorKey: "label", header: "Label", size: 320, footer: "Total" },
|
|
214
|
+
]}
|
|
215
|
+
layout="table"
|
|
216
|
+
columnLayout="scroll"
|
|
217
|
+
enableColumnResizing
|
|
218
|
+
enableColumnPinning
|
|
219
|
+
enableVirtualization
|
|
220
|
+
virtualizationHeight={240}
|
|
221
|
+
enablePagination={false}
|
|
222
|
+
{...allAuxiliaries}
|
|
223
|
+
/>,
|
|
224
|
+
);
|
|
225
|
+
const virtualRows = () =>
|
|
226
|
+
utils.container.querySelectorAll('tbody[role="presentation"] [role="rowgroup"] [role="row"]');
|
|
227
|
+
await waitFor(() => expect(virtualRows()).toHaveLength(2));
|
|
228
|
+
fireEvent.click(utils.getByLabelText("Pin Rank column left"));
|
|
229
|
+
fireEvent.click(utils.getByLabelText("Pin Label column left"));
|
|
230
|
+
const header = () => cells(utils.container.querySelector('thead [role="row"]')!);
|
|
231
|
+
const leadingWidth = header()
|
|
232
|
+
.slice(0, 3)
|
|
233
|
+
.reduce((sum, cell) => sum + width(cell), 0);
|
|
234
|
+
expect(width(header()[3])).toBe(48);
|
|
235
|
+
expectPinned(header()[4], "start", leadingWidth + 48);
|
|
236
|
+
const handle = utils.getByRole("separator", { name: "Resize Rank column" });
|
|
237
|
+
fireEvent.mouseDown(handle, { clientX: 100 });
|
|
238
|
+
fireEvent.mouseMove(document, { clientX: 130 });
|
|
239
|
+
fireEvent.mouseUp(document, { clientX: 130 });
|
|
240
|
+
expect(width(header()[3])).toBe(64);
|
|
241
|
+
expectPinned(header()[4], "start", leadingWidth + 64);
|
|
242
|
+
const footer = cells(utils.container.querySelector('tfoot [role="row"]')!);
|
|
243
|
+
expect(virtualRows()).toHaveLength(2);
|
|
244
|
+
for (const section of [...virtualRows()].map(cells).concat([footer])) {
|
|
245
|
+
expect(section.map(width)).toEqual(header().map(width));
|
|
246
|
+
expectPinned(section[3], "start", leadingWidth);
|
|
247
|
+
expectPinned(section[4], "start", leadingWidth + 64);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPO-336 / LC-10 FRAME-CONTAINMENT at the header grain.
|
|
3
|
+
*
|
|
4
|
+
* A header label wider than its column painted over the next column: the
|
|
5
|
+
* label run would not shrink below its max-content width, so a start-aligned
|
|
6
|
+
* label pushed its sort and filter glyphs into the neighbour and an
|
|
7
|
+
* end-aligned (number) label spilled left over the previous column. The
|
|
8
|
+
* label is now the part that yields: it shrinks to the column and wraps,
|
|
9
|
+
* breaking a long word if it must, while the glyphs keep their size.
|
|
10
|
+
*
|
|
11
|
+
* happy-dom does no layout, so this pins the flex contract that produces the
|
|
12
|
+
* containment. The painted geometry is measured in Storybook by the LC-10
|
|
13
|
+
* probe (`table-datatable--narrow-headers`, scripts/storybook-rubric).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
17
|
+
import { describe, expect, it } from "vitest";
|
|
18
|
+
import type { DataTableColumn } from "../../types";
|
|
19
|
+
import { DataTable } from "./index";
|
|
20
|
+
|
|
21
|
+
type Row = { id: string; account: string; quantity: number };
|
|
22
|
+
|
|
23
|
+
const rows: Row[] = [
|
|
24
|
+
{ id: "1", account: "acme-website", quantity: 142 },
|
|
25
|
+
{ id: "2", account: "tamagui-docs", quantity: 89 },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const columns: DataTableColumn<Row>[] = [
|
|
29
|
+
{ id: "account", accessorKey: "account", header: "Customer account (input)", width: 96 },
|
|
30
|
+
{
|
|
31
|
+
id: "quantity",
|
|
32
|
+
accessorKey: "quantity",
|
|
33
|
+
header: "Quantity (stepper)",
|
|
34
|
+
align: "right",
|
|
35
|
+
width: 96,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
function style(el: Element, prop: "flexShrink" | "minWidth" | "overflowWrap"): string {
|
|
40
|
+
const inline = (el as HTMLElement).style[prop];
|
|
41
|
+
return inline || getComputedStyle(el)[prop];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function renderNarrow() {
|
|
45
|
+
return renderWithProviders(
|
|
46
|
+
<DataTable<Row>
|
|
47
|
+
data={rows}
|
|
48
|
+
columns={columns}
|
|
49
|
+
layout="table"
|
|
50
|
+
columnLayout="scroll"
|
|
51
|
+
enableSorting
|
|
52
|
+
enableFiltering
|
|
53
|
+
enablePagination={false}
|
|
54
|
+
readOnly
|
|
55
|
+
/>,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
describe("DataTable header labels stay inside their column (MPO-336)", () => {
|
|
60
|
+
it.each(["account", "quantity"])("the %s label shrinks and wraps", (id) => {
|
|
61
|
+
const { container } = renderNarrow();
|
|
62
|
+
const header = container.querySelector(`#${id}-header`) as HTMLElement;
|
|
63
|
+
expect(header).toBeTruthy();
|
|
64
|
+
const label = header.querySelector("[data-header-label]");
|
|
65
|
+
expect(label, "header label run is marked").not.toBeNull();
|
|
66
|
+
expect(style(label!, "flexShrink")).toBe("1");
|
|
67
|
+
expect(style(label!, "minWidth")).toMatch(/^0(px)?$/);
|
|
68
|
+
expect(style(label!, "overflowWrap")).toBe("anywhere");
|
|
69
|
+
// Every ancestor between the label and the cell yields too, or the
|
|
70
|
+
// label's min-content width comes back through its parent.
|
|
71
|
+
for (let node = label!.parentElement; node && node !== header; node = node.parentElement) {
|
|
72
|
+
if (getComputedStyle(node).display === "contents") continue;
|
|
73
|
+
expect(style(node, "minWidth"), "ancestor min-width").toMatch(/^0(px)?$/);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("the sort and filter glyphs keep their size", () => {
|
|
78
|
+
const { container } = renderNarrow();
|
|
79
|
+
const header = container.querySelector("#account-header") as HTMLElement;
|
|
80
|
+
const label = header.querySelector("[data-header-label]")!;
|
|
81
|
+
const glyphs = [...header.querySelectorAll("svg")];
|
|
82
|
+
expect(glyphs.length).toBeGreaterThanOrEqual(2);
|
|
83
|
+
for (const svg of glyphs) {
|
|
84
|
+
let node: Element | null = svg;
|
|
85
|
+
while (node && node.parentElement && !node.parentElement.contains(label)) {
|
|
86
|
+
node = node.parentElement;
|
|
87
|
+
}
|
|
88
|
+
expect(style(node!, "flexShrink"), "glyph slot").toBe("0");
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
18
18
|
import { fireEvent, waitFor } from "@testing-library/react";
|
|
19
|
-
import { describe, expect, it, vi } from "vitest";
|
|
19
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
20
20
|
import type { DataTableColumn } from "../../types";
|
|
21
21
|
import { DataTable } from "./index";
|
|
22
22
|
|
|
@@ -142,3 +142,68 @@ describe("column pinning geometry", () => {
|
|
|
142
142
|
});
|
|
143
143
|
});
|
|
144
144
|
});
|
|
145
|
+
|
|
146
|
+
afterEach(() => document.documentElement.removeAttribute("dir"));
|
|
147
|
+
|
|
148
|
+
it.each(["ltr", "rtl"])(
|
|
149
|
+
"keeps declared small pinned boxes and logical offsets aligned in %s",
|
|
150
|
+
async (direction) => {
|
|
151
|
+
document.documentElement.setAttribute("dir", direction);
|
|
152
|
+
const utils = renderPinned({
|
|
153
|
+
columns: columns.map((column, index) => ({
|
|
154
|
+
...column,
|
|
155
|
+
footer: String(column.header),
|
|
156
|
+
width: index === 0 ? 48 : 320,
|
|
157
|
+
})),
|
|
158
|
+
});
|
|
159
|
+
await pinIdColumn(utils);
|
|
160
|
+
fireEvent.click(utils.getByLabelText("Pin First Name column left"));
|
|
161
|
+
await waitFor(() => {
|
|
162
|
+
const first = utils.container.querySelector("#id-header") as HTMLElement;
|
|
163
|
+
const second = utils.container.querySelector("#firstName-header") as HTMLElement;
|
|
164
|
+
expect(hasStyle(first, "width", "48px", "_w-48px")).toBe(true);
|
|
165
|
+
expect(hasStyle(first, "minWidth", "48px", "_miw-48px")).toBe(true);
|
|
166
|
+
expect(hasStyle(first, "maxWidth", "48px", "_maw-48px")).toBe(true);
|
|
167
|
+
expect(second.style.insetInlineStart).toBe("48px");
|
|
168
|
+
const body = utils.container.querySelector('[role="row"][aria-label="Row 1"]')!;
|
|
169
|
+
const cells = body.querySelectorAll('[role="cell"]');
|
|
170
|
+
expect(hasStyle(cells[0] as HTMLElement, "width", "48px", "_w-48px")).toBe(true);
|
|
171
|
+
expect((cells[1] as HTMLElement).style.insetInlineStart).toBe("48px");
|
|
172
|
+
const footer = utils.container.querySelectorAll('tfoot [role="cell"]');
|
|
173
|
+
expect(hasStyle(footer[0] as HTMLElement, "width", "48px", "_w-48px")).toBe(true);
|
|
174
|
+
expect((footer[1] as HTMLElement).style.insetInlineStart).toBe("48px");
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
it.each(["ltr", "rtl"])(
|
|
180
|
+
"aligns right-pinned headers, rows and footers in %s",
|
|
181
|
+
async (direction) => {
|
|
182
|
+
document.documentElement.setAttribute("dir", direction);
|
|
183
|
+
const utils = renderPinned({
|
|
184
|
+
columns: columns.map((column, index) => ({
|
|
185
|
+
...column,
|
|
186
|
+
footer: String(column.header),
|
|
187
|
+
width: index === 3 ? 76 : 320,
|
|
188
|
+
})),
|
|
189
|
+
});
|
|
190
|
+
for (const label of ["Last Name", "Age"]) {
|
|
191
|
+
fireEvent.click(utils.getByLabelText(`Pin ${label} column left`));
|
|
192
|
+
fireEvent.click(utils.getByLabelText(`Pin ${label} column right`));
|
|
193
|
+
}
|
|
194
|
+
await waitFor(() => {
|
|
195
|
+
const last = utils.container.querySelector("#age-header") as HTMLElement;
|
|
196
|
+
const previous = utils.container.querySelector("#lastName-header") as HTMLElement;
|
|
197
|
+
expect(hasStyle(last, "width", "76px", "_w-76px")).toBe(true);
|
|
198
|
+
expect(last.style.insetInlineEnd).toBe("0px");
|
|
199
|
+
expect(previous.style.insetInlineEnd).toBe("76px");
|
|
200
|
+
const body = utils.container.querySelector('[role="row"][aria-label="Row 1"]')!;
|
|
201
|
+
const cells = body.querySelectorAll('[role="cell"]');
|
|
202
|
+
expect(hasStyle(cells[3] as HTMLElement, "width", "76px", "_w-76px")).toBe(true);
|
|
203
|
+
expect((cells[2] as HTMLElement).style.insetInlineEnd).toBe("76px");
|
|
204
|
+
const footer = utils.container.querySelectorAll('tfoot [role="cell"]');
|
|
205
|
+
expect(hasStyle(footer[3] as HTMLElement, "width", "76px", "_w-76px")).toBe(true);
|
|
206
|
+
expect((footer[2] as HTMLElement).style.insetInlineEnd).toBe("76px");
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
);
|
|
@@ -31,13 +31,9 @@ const sampleData: Person[] = [
|
|
|
31
31
|
{ id: 3, firstName: "Carol", age: 20 },
|
|
32
32
|
];
|
|
33
33
|
|
|
34
|
-
// Explicit ids: DataTable seeds columnOrder from `col.id || ""`, so mixing
|
|
35
|
-
// id-less accessor columns with an id'd display column would reorder the
|
|
36
|
-
// display column first (and identity fallbacks would read it as the "first
|
|
37
|
-
// visible cell").
|
|
38
34
|
const columns: DataTableColumn<Person>[] = [
|
|
39
|
-
{
|
|
40
|
-
{
|
|
35
|
+
{ accessorKey: "firstName", header: "First Name" },
|
|
36
|
+
{ accessorKey: "age", header: "Age" },
|
|
41
37
|
];
|
|
42
38
|
|
|
43
39
|
function pressRows(container: HTMLElement): HTMLElement[] {
|