@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
|
@@ -32,7 +32,8 @@ import type {
|
|
|
32
32
|
} from "../../types";
|
|
33
33
|
import { DataTableCell } from "../DataTableCell";
|
|
34
34
|
import { withInterp } from "../../utils/withInterp";
|
|
35
|
-
import {
|
|
35
|
+
import { RowDetailEditor } from "./RowDetailEditor";
|
|
36
|
+
import { generateRowId, isEmptyRows, SKELETON_ROW_COUNT, type ChildCellHidden } from "./shared";
|
|
36
37
|
|
|
37
38
|
export interface TableInputProps<TData extends DataTableData = DataTableData> {
|
|
38
39
|
/** Data array for the table (ignored in favor of form state in the form branch) */
|
|
@@ -80,6 +81,12 @@ export interface TableInputProps<TData extends DataTableData = DataTableData> {
|
|
|
80
81
|
compact?: boolean;
|
|
81
82
|
/** Leading grab handle + keyboard reorder via kanbanDnd (web implements). */
|
|
82
83
|
enableReorder?: boolean;
|
|
84
|
+
/** Per-row button that opens the row in a detail editor (MPO-22). */
|
|
85
|
+
rowDetail?: boolean;
|
|
86
|
+
/** Fields the detail editor shows. Defaults to `columns`. */
|
|
87
|
+
detailColumns?: DataTableColumn<TData>[];
|
|
88
|
+
/** Hides one row's cell, in the grid and in the detail editor. */
|
|
89
|
+
isCellHidden?: ChildCellHidden<TData>;
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
export function TableInput<TData extends DataTableData = DataTableData>({
|
|
@@ -103,6 +110,9 @@ export function TableInput<TData extends DataTableData = DataTableData>({
|
|
|
103
110
|
emptyMessage,
|
|
104
111
|
"aria-label": ariaLabel,
|
|
105
112
|
compact = true,
|
|
113
|
+
rowDetail = false,
|
|
114
|
+
detailColumns,
|
|
115
|
+
isCellHidden,
|
|
106
116
|
}: TableInputProps<TData>) {
|
|
107
117
|
const { t } = useTranslation();
|
|
108
118
|
const { resolvedForm } = useFormField({ form: formProp });
|
|
@@ -138,6 +148,9 @@ export function TableInput<TData extends DataTableData = DataTableData>({
|
|
|
138
148
|
emptyMessage: emptyMessage ?? t("No rows"),
|
|
139
149
|
ariaLabel: resolvedAriaLabel,
|
|
140
150
|
compact,
|
|
151
|
+
rowDetail,
|
|
152
|
+
detailColumns: detailColumns ?? columns,
|
|
153
|
+
isCellHidden,
|
|
141
154
|
t,
|
|
142
155
|
};
|
|
143
156
|
|
|
@@ -191,6 +204,9 @@ interface TableInputInnerProps<TData extends DataTableData> {
|
|
|
191
204
|
emptyMessage: string;
|
|
192
205
|
ariaLabel: string;
|
|
193
206
|
compact: boolean;
|
|
207
|
+
rowDetail: boolean;
|
|
208
|
+
detailColumns: DataTableColumn<TData>[];
|
|
209
|
+
isCellHidden?: ChildCellHidden<TData>;
|
|
194
210
|
t: (key: string, options?: Record<string, unknown>) => string;
|
|
195
211
|
}
|
|
196
212
|
|
|
@@ -210,6 +226,9 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
210
226
|
emptyMessage,
|
|
211
227
|
ariaLabel,
|
|
212
228
|
compact,
|
|
229
|
+
rowDetail,
|
|
230
|
+
detailColumns,
|
|
231
|
+
isCellHidden,
|
|
213
232
|
t,
|
|
214
233
|
}: TableInputInnerProps<TData>) {
|
|
215
234
|
const theme = useTheme();
|
|
@@ -219,6 +238,7 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
219
238
|
const indexWidth = Math.max(36, chromePx);
|
|
220
239
|
const actionWidth = chromePx + 16;
|
|
221
240
|
const [activeRowId, setActiveRowId] = useState<string | null>(null);
|
|
241
|
+
const Detail = RowDetailEditor<TData>;
|
|
222
242
|
|
|
223
243
|
const handleCellChange = useCallback(
|
|
224
244
|
(rowIndex: number, columnId: string, value: unknown) => {
|
|
@@ -250,6 +270,16 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
250
270
|
[rows, commit, minRows],
|
|
251
271
|
);
|
|
252
272
|
|
|
273
|
+
const handleRowDetailCommit = useCallback(
|
|
274
|
+
(rowIndex: number, next: TData, changed: string[]) => {
|
|
275
|
+
for (const columnId of changed) {
|
|
276
|
+
onCellChange?.(rowIndex, columnId, next[columnId as keyof TData]);
|
|
277
|
+
}
|
|
278
|
+
commit(rows.map((row, index) => (index === rowIndex ? next : row)));
|
|
279
|
+
},
|
|
280
|
+
[rows, commit, onCellChange],
|
|
281
|
+
);
|
|
282
|
+
|
|
253
283
|
const canAddRow = !maxRows || rows.length < maxRows;
|
|
254
284
|
const canDelete = !minRows || rows.length > (minRows || 0);
|
|
255
285
|
|
|
@@ -277,6 +307,7 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
277
307
|
</Text>
|
|
278
308
|
</View>
|
|
279
309
|
))}
|
|
310
|
+
{rowDetail && <View width={actionWidth} />}
|
|
280
311
|
{showDeleteRow && !readOnly && <View width={actionWidth} />}
|
|
281
312
|
</XStack>
|
|
282
313
|
);
|
|
@@ -304,6 +335,9 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
304
335
|
const field = (col.field || "input") as DataTableField;
|
|
305
336
|
const fieldProps = col.fieldProps as DataTableFieldProps | undefined;
|
|
306
337
|
|
|
338
|
+
if (isCellHidden?.(item, columnId, index)) {
|
|
339
|
+
return <View key={columnId} flex={1} paddingHorizontal="$1" />;
|
|
340
|
+
}
|
|
307
341
|
return (
|
|
308
342
|
<View key={columnId} flex={1} paddingHorizontal="$1">
|
|
309
343
|
<DataTableCell
|
|
@@ -321,6 +355,19 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
321
355
|
</View>
|
|
322
356
|
);
|
|
323
357
|
})}
|
|
358
|
+
{rowDetail && (
|
|
359
|
+
<View width={actionWidth} justifyContent="center" alignItems="center">
|
|
360
|
+
<Detail
|
|
361
|
+
row={item}
|
|
362
|
+
rowIndex={index}
|
|
363
|
+
columns={detailColumns}
|
|
364
|
+
readOnly={readOnly}
|
|
365
|
+
compact={compact}
|
|
366
|
+
isCellHidden={isCellHidden}
|
|
367
|
+
onCommit={(next, changed) => handleRowDetailCommit(index, next, changed)}
|
|
368
|
+
/>
|
|
369
|
+
</View>
|
|
370
|
+
)}
|
|
324
371
|
{showDeleteRow && !readOnly && (
|
|
325
372
|
<View width={actionWidth} justifyContent="center" alignItems="center">
|
|
326
373
|
<Button
|
|
@@ -355,6 +402,7 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
355
402
|
<Skeleton variant="rounded" width="70%" height={16} />
|
|
356
403
|
</View>
|
|
357
404
|
))}
|
|
405
|
+
{rowDetail && <View width={actionWidth} />}
|
|
358
406
|
{showDeleteRow && !readOnly && <View width={actionWidth} />}
|
|
359
407
|
</XStack>
|
|
360
408
|
))}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TableInput — the child-table field (the forms↔table bridge).
|
|
3
3
|
*
|
|
4
|
-
* Follows the forms field contract (
|
|
4
|
+
* Follows the forms field contract (docs/standards/frontend/field-architecture.md):
|
|
5
5
|
* - Canonical change prop is `onChange(rows)` (LC-14 ONCHANGE-CANONICAL);
|
|
6
6
|
* `onDataChange(rows)` survives as a deprecated alias that fires from the
|
|
7
7
|
* same emit site, and `onCellChange(rowIndex, columnId, value)` is a
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* Row identity: added rows get a collision-free generated `id`. TableInput
|
|
18
18
|
* reorders through the shared kanbanDnd machine (`enableReorder`); Frappe
|
|
19
19
|
* `idx` renumbering is the ChildTable layer (MPO-22), not this field.
|
|
20
|
+
* `rowDetail` opens one row as a form over `detailColumns` (RowDetailEditor);
|
|
21
|
+
* `isCellHidden` blanks a cell per row, and the caller owns the rule.
|
|
20
22
|
*/
|
|
21
23
|
|
|
22
24
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
@@ -47,14 +49,18 @@ import type {
|
|
|
47
49
|
} from "../../types";
|
|
48
50
|
import { DataTableCell } from "../DataTableCell";
|
|
49
51
|
import { withInterp } from "../../utils/withInterp";
|
|
52
|
+
import { RowDetailEditor } from "./RowDetailEditor";
|
|
50
53
|
import {
|
|
51
54
|
childRowAttrs,
|
|
52
55
|
childTableKeyAction,
|
|
53
56
|
generateRowId,
|
|
54
57
|
isEmptyRows,
|
|
55
58
|
SKELETON_ROW_COUNT,
|
|
59
|
+
type ChildCellHidden,
|
|
56
60
|
} from "./shared";
|
|
57
61
|
|
|
62
|
+
const chromeColumnIds = new Set(["_reorder", "_rowNumber", "_detail", "_actions"]);
|
|
63
|
+
|
|
58
64
|
export interface TableInputProps<TData extends DataTableData = DataTableData> {
|
|
59
65
|
/** Data array for the table (ignored in favor of form state in the form branch) */
|
|
60
66
|
data?: TData[];
|
|
@@ -104,6 +110,12 @@ export interface TableInputProps<TData extends DataTableData = DataTableData> {
|
|
|
104
110
|
* this on and writes `idx` on the change payload (MPO-22).
|
|
105
111
|
*/
|
|
106
112
|
enableReorder?: boolean;
|
|
113
|
+
/** Per-row button that opens the row in a detail editor (MPO-22). */
|
|
114
|
+
rowDetail?: boolean;
|
|
115
|
+
/** Fields the detail editor shows. Defaults to `columns`. */
|
|
116
|
+
detailColumns?: DataTableColumn<TData>[];
|
|
117
|
+
/** Hides one row's cell, in the grid and in the detail editor. */
|
|
118
|
+
isCellHidden?: ChildCellHidden<TData>;
|
|
107
119
|
}
|
|
108
120
|
|
|
109
121
|
export function TableInput<TData extends DataTableData = DataTableData>({
|
|
@@ -128,6 +140,9 @@ export function TableInput<TData extends DataTableData = DataTableData>({
|
|
|
128
140
|
"aria-label": ariaLabel,
|
|
129
141
|
compact = true,
|
|
130
142
|
enableReorder = false,
|
|
143
|
+
rowDetail = false,
|
|
144
|
+
detailColumns,
|
|
145
|
+
isCellHidden,
|
|
131
146
|
}: TableInputProps<TData>) {
|
|
132
147
|
const { t } = useTranslation();
|
|
133
148
|
const { resolvedForm } = useFormField({ form: formProp });
|
|
@@ -164,6 +179,9 @@ export function TableInput<TData extends DataTableData = DataTableData>({
|
|
|
164
179
|
ariaLabel: resolvedAriaLabel,
|
|
165
180
|
compact,
|
|
166
181
|
enableReorder,
|
|
182
|
+
rowDetail,
|
|
183
|
+
detailColumns: detailColumns ?? columns,
|
|
184
|
+
isCellHidden,
|
|
167
185
|
t,
|
|
168
186
|
};
|
|
169
187
|
|
|
@@ -218,6 +236,9 @@ interface TableInputInnerProps<TData extends DataTableData> {
|
|
|
218
236
|
ariaLabel: string;
|
|
219
237
|
compact: boolean;
|
|
220
238
|
enableReorder: boolean;
|
|
239
|
+
rowDetail: boolean;
|
|
240
|
+
detailColumns: DataTableColumn<TData>[];
|
|
241
|
+
isCellHidden?: ChildCellHidden<TData>;
|
|
221
242
|
t: (key: string, options?: Record<string, unknown>) => string;
|
|
222
243
|
}
|
|
223
244
|
|
|
@@ -238,6 +259,9 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
238
259
|
ariaLabel,
|
|
239
260
|
compact,
|
|
240
261
|
enableReorder,
|
|
262
|
+
rowDetail,
|
|
263
|
+
detailColumns,
|
|
264
|
+
isCellHidden,
|
|
241
265
|
t,
|
|
242
266
|
}: TableInputInnerProps<TData>) {
|
|
243
267
|
const theme = useTheme();
|
|
@@ -248,6 +272,7 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
248
272
|
const pendingFocusRef = useRef<string | null>(null);
|
|
249
273
|
const gridRef = useRef<HTMLElement | null>(null);
|
|
250
274
|
const reorderEnabled = enableReorder && !readOnly && !skeleton;
|
|
275
|
+
const Detail = RowDetailEditor<TData>;
|
|
251
276
|
|
|
252
277
|
const firstDataKey = useMemo(() => {
|
|
253
278
|
const first = columns[0];
|
|
@@ -329,6 +354,16 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
329
354
|
[rows, commit, minRows],
|
|
330
355
|
);
|
|
331
356
|
|
|
357
|
+
const handleRowDetailCommit = useCallback(
|
|
358
|
+
(rowIndex: number, next: TData, changed: string[]) => {
|
|
359
|
+
for (const columnId of changed) {
|
|
360
|
+
onCellChange?.(rowIndex, columnId, next[columnId as keyof TData]);
|
|
361
|
+
}
|
|
362
|
+
commit(rows.map((row, index) => (index === rowIndex ? next : row)));
|
|
363
|
+
},
|
|
364
|
+
[rows, commit, onCellChange],
|
|
365
|
+
);
|
|
366
|
+
|
|
332
367
|
useEffect(() => {
|
|
333
368
|
const id = pendingFocusRef.current;
|
|
334
369
|
if (!id || typeof document === "undefined") return;
|
|
@@ -432,6 +467,7 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
432
467
|
...col,
|
|
433
468
|
id: columnId,
|
|
434
469
|
cell: ({ row }) => {
|
|
470
|
+
if (isCellHidden?.(row.original, columnId, row.index)) return null;
|
|
435
471
|
const value = row.original[columnId as keyof TData];
|
|
436
472
|
const field = (col.field || "input") as DataTableField;
|
|
437
473
|
const fieldProps = col.fieldProps as DataTableFieldProps | undefined;
|
|
@@ -454,6 +490,25 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
454
490
|
} as ColumnDef<TData, unknown>);
|
|
455
491
|
});
|
|
456
492
|
|
|
493
|
+
if (rowDetail) {
|
|
494
|
+
cols.push({
|
|
495
|
+
id: "_detail",
|
|
496
|
+
header: "",
|
|
497
|
+
size: chromePx + 16,
|
|
498
|
+
cell: ({ row }) => (
|
|
499
|
+
<Detail
|
|
500
|
+
row={row.original}
|
|
501
|
+
rowIndex={row.index}
|
|
502
|
+
columns={detailColumns}
|
|
503
|
+
readOnly={readOnly}
|
|
504
|
+
compact={compact}
|
|
505
|
+
isCellHidden={isCellHidden}
|
|
506
|
+
onCommit={(next, changed) => handleRowDetailCommit(row.index, next, changed)}
|
|
507
|
+
/>
|
|
508
|
+
),
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
457
512
|
if (showDeleteRow && !readOnly) {
|
|
458
513
|
cols.push({
|
|
459
514
|
id: "_actions",
|
|
@@ -495,6 +550,10 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
495
550
|
knobProps.label.fontSize,
|
|
496
551
|
knobProps.nestedControl.hitSlop,
|
|
497
552
|
reorderEnabled,
|
|
553
|
+
rowDetail,
|
|
554
|
+
detailColumns,
|
|
555
|
+
isCellHidden,
|
|
556
|
+
handleRowDetailCommit,
|
|
498
557
|
]);
|
|
499
558
|
|
|
500
559
|
const table = useReactTable({
|
|
@@ -556,10 +615,7 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
556
615
|
key={header.id}
|
|
557
616
|
style={{
|
|
558
617
|
width: header.getSize(),
|
|
559
|
-
...(isEmpty &&
|
|
560
|
-
header.column.id !== "_rowNumber" &&
|
|
561
|
-
header.column.id !== "_actions" &&
|
|
562
|
-
header.column.id !== "_reorder"
|
|
618
|
+
...(isEmpty && !chromeColumnIds.has(header.column.id)
|
|
563
619
|
? { flexGrow: 1 }
|
|
564
620
|
: null),
|
|
565
621
|
}}
|
|
@@ -578,7 +634,9 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
578
634
|
<Table.Row key={`skeleton-${rowIndex}`}>
|
|
579
635
|
{table.getAllLeafColumns().map((column) => (
|
|
580
636
|
<Table.Cell key={column.id}>
|
|
581
|
-
{column.id === "_actions" ||
|
|
637
|
+
{column.id === "_actions" ||
|
|
638
|
+
column.id === "_reorder" ||
|
|
639
|
+
column.id === "_detail" ? null : (
|
|
582
640
|
<Skeleton
|
|
583
641
|
variant="rounded"
|
|
584
642
|
width={column.id === "_rowNumber" ? 16 : "70%"}
|
|
@@ -617,10 +675,7 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
617
675
|
>)}
|
|
618
676
|
>
|
|
619
677
|
{row.getVisibleCells().map((cell) => {
|
|
620
|
-
const isData =
|
|
621
|
-
cell.column.id !== "_rowNumber" &&
|
|
622
|
-
cell.column.id !== "_actions" &&
|
|
623
|
-
cell.column.id !== "_reorder";
|
|
678
|
+
const isData = !chromeColumnIds.has(cell.column.id);
|
|
624
679
|
const field = columns.find((col) => {
|
|
625
680
|
const id =
|
|
626
681
|
typeof col.accessorKey === "string"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** Shared TableInput internals (web + native). */
|
|
2
2
|
|
|
3
|
+
import type { DataTableColumn, DataTableData } from "../../types";
|
|
4
|
+
|
|
3
5
|
let rowIdCounter = 0;
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -75,3 +77,30 @@ export function childTableKeyAction(
|
|
|
75
77
|
if (next < 0 || next >= ctx.rowCount) return null;
|
|
76
78
|
return { type: "move", rowIndex: next, columnId: ctx.columnId };
|
|
77
79
|
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Per-row cell visibility (Frappe child `depends_on`, evaluated by the
|
|
83
|
+
* caller). True hides that cell's editor in the grid and its field in the
|
|
84
|
+
* row detail editor.
|
|
85
|
+
*/
|
|
86
|
+
export type ChildCellHidden<TData extends DataTableData = DataTableData> = (
|
|
87
|
+
row: TData,
|
|
88
|
+
columnId: string,
|
|
89
|
+
rowIndex: number,
|
|
90
|
+
) => boolean;
|
|
91
|
+
|
|
92
|
+
/** Row-data key a column reads (accessorKey, else id). */
|
|
93
|
+
export function columnKey<TData extends DataTableData>(col: DataTableColumn<TData>): string {
|
|
94
|
+
return typeof col.accessorKey === "string" ? col.accessorKey : col.id || String(col.accessorKey);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Column keys whose value differs between two versions of a row. */
|
|
98
|
+
export function changedColumns<TData extends DataTableData>(
|
|
99
|
+
before: TData,
|
|
100
|
+
after: TData,
|
|
101
|
+
columns: DataTableColumn<TData>[],
|
|
102
|
+
): string[] {
|
|
103
|
+
return columns
|
|
104
|
+
.map(columnKey)
|
|
105
|
+
.filter((key) => !Object.is(before[key as keyof TData], after[key as keyof TData]));
|
|
106
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPO-221: a table dropdown is a FloatingPanel (the cover family, MPO-38),
|
|
3
|
+
* never Tamagui's Popover. Each surface opens the FloatingPanel viewport from
|
|
4
|
+
* its trigger, and no component source under components/ imports Popover.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
10
|
+
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
|
11
|
+
import type { ReactElement } from "react";
|
|
12
|
+
import { describe, expect, it, vi } from "vitest";
|
|
13
|
+
import { ColumnHeaderFilter } from "./ColumnHeaderFilter";
|
|
14
|
+
import { DataTableDateFilter } from "./DataTableDateFilter";
|
|
15
|
+
import { DataTableExport } from "./DataTableExport";
|
|
16
|
+
import { DataTableGroupingConfig } from "./DataTableGrouping";
|
|
17
|
+
import { DataTableSearch } from "./DataTableSearch";
|
|
18
|
+
import { DataTableToolbar } from "./DataTableToolbar";
|
|
19
|
+
import { DataTableViewOptions } from "./DataTableViewOptions";
|
|
20
|
+
|
|
21
|
+
const panel = () =>
|
|
22
|
+
document.querySelector('[data-testid="floating-panel-viewport"]') as HTMLElement | null;
|
|
23
|
+
|
|
24
|
+
const rows = [{ name: "Ada", city: "London" }];
|
|
25
|
+
const columns = [
|
|
26
|
+
{ accessorKey: "name", header: "Name", id: "name", searchable: true },
|
|
27
|
+
{ accessorKey: "city", header: "City", id: "city", searchable: true },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
interface Surface {
|
|
31
|
+
name: string;
|
|
32
|
+
ui: ReactElement;
|
|
33
|
+
trigger: () => HTMLElement;
|
|
34
|
+
content: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const surfaces: Surface[] = [
|
|
38
|
+
{
|
|
39
|
+
name: "column header filter",
|
|
40
|
+
ui: (
|
|
41
|
+
<ColumnHeaderFilter
|
|
42
|
+
columnId="name"
|
|
43
|
+
columnLabel="Name"
|
|
44
|
+
columnType="text"
|
|
45
|
+
onFilterChange={vi.fn()}
|
|
46
|
+
/>
|
|
47
|
+
),
|
|
48
|
+
trigger: () => screen.getByLabelText("Filter column Name"),
|
|
49
|
+
content: "Filter: Name",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "export options",
|
|
53
|
+
ui: <DataTableExport data={rows} columns={columns} />,
|
|
54
|
+
trigger: () => screen.getByRole("button", { name: "Export options" }),
|
|
55
|
+
content: "Export Options",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "grouping",
|
|
59
|
+
ui: (
|
|
60
|
+
<DataTableGroupingConfig
|
|
61
|
+
columns={columns}
|
|
62
|
+
currentConfig={{ columns: [], aggregations: [] }}
|
|
63
|
+
onConfigChange={vi.fn()}
|
|
64
|
+
/>
|
|
65
|
+
),
|
|
66
|
+
trigger: () => screen.getByLabelText("Grouping configuration"),
|
|
67
|
+
content: "Grouping & Aggregation",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "date filter",
|
|
71
|
+
ui: <DataTableDateFilter placeholder="Pick a date" />,
|
|
72
|
+
trigger: () => screen.getByText("Pick a date"),
|
|
73
|
+
content: "Date Filter",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "search options",
|
|
77
|
+
ui: <DataTableSearch data={rows} columns={columns} onSearchResults={vi.fn()} />,
|
|
78
|
+
trigger: () => screen.getByRole("button", { name: "Search options" }),
|
|
79
|
+
content: "Search Options",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "toolbar save-view",
|
|
83
|
+
ui: (
|
|
84
|
+
<DataTableToolbar
|
|
85
|
+
onSaveView={vi.fn()}
|
|
86
|
+
onLoadView={vi.fn()}
|
|
87
|
+
views={[]}
|
|
88
|
+
enableViews
|
|
89
|
+
showViewSelector
|
|
90
|
+
/>
|
|
91
|
+
),
|
|
92
|
+
trigger: () => screen.getByRole("button", { name: "Save view" }),
|
|
93
|
+
content: "",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "view options save-view",
|
|
97
|
+
ui: (
|
|
98
|
+
<DataTableViewOptions
|
|
99
|
+
columns={[{ id: "name", label: "Name", visible: true, order: 0 }]}
|
|
100
|
+
onSaveView={vi.fn()}
|
|
101
|
+
onLoadView={vi.fn()}
|
|
102
|
+
onResetToDefault={vi.fn()}
|
|
103
|
+
showViewManagement
|
|
104
|
+
/>
|
|
105
|
+
),
|
|
106
|
+
trigger: () => screen.getByText("Save Current View"),
|
|
107
|
+
content: "Save View",
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
describe("table dropdowns open FloatingPanel (MPO-221)", () => {
|
|
112
|
+
it.each(surfaces)("$name opens the FloatingPanel viewport", async ({ ui, trigger, content }) => {
|
|
113
|
+
renderWithProviders(ui);
|
|
114
|
+
expect(panel()).toBeNull();
|
|
115
|
+
fireEvent.click(trigger());
|
|
116
|
+
await waitFor(() => expect(panel()).not.toBeNull());
|
|
117
|
+
if (content) expect(panel()!.textContent).toContain(content);
|
|
118
|
+
// A compose-class editor declares its dismissal contract (LC-21 VERIFY).
|
|
119
|
+
expect(panel()!.querySelector('[data-dismiss-class="compose"]')).not.toBeNull();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it.each(surfaces)("$name closes on Escape", async ({ ui, trigger }) => {
|
|
123
|
+
renderWithProviders(ui);
|
|
124
|
+
fireEvent.click(trigger());
|
|
125
|
+
await waitFor(() => expect(panel()).not.toBeNull());
|
|
126
|
+
fireEvent.keyDown(document.activeElement ?? document.body, { key: "Escape" });
|
|
127
|
+
await waitFor(() => expect(panel()).toBeNull());
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("the save-view panel holds the view-name field and its two actions", async () => {
|
|
131
|
+
const onSaveView = vi.fn();
|
|
132
|
+
renderWithProviders(
|
|
133
|
+
<DataTableToolbar
|
|
134
|
+
onSaveView={onSaveView}
|
|
135
|
+
onLoadView={vi.fn()}
|
|
136
|
+
views={[]}
|
|
137
|
+
enableViews
|
|
138
|
+
showViewSelector
|
|
139
|
+
/>,
|
|
140
|
+
);
|
|
141
|
+
fireEvent.click(screen.getByRole("button", { name: "Save view" }));
|
|
142
|
+
await waitFor(() => expect(panel()).not.toBeNull());
|
|
143
|
+
fireEvent.change(screen.getByLabelText("View name"), { target: { value: "Mine" } });
|
|
144
|
+
fireEvent.click(screen.getByRole("button", { name: "Save" }));
|
|
145
|
+
expect(onSaveView).toHaveBeenCalledWith("Mine");
|
|
146
|
+
await waitFor(() => expect(panel()).toBeNull());
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
function componentSources(dir: string): string[] {
|
|
151
|
+
return readdirSync(dir).flatMap((entry) => {
|
|
152
|
+
const path = join(dir, entry);
|
|
153
|
+
if (statSync(path).isDirectory()) return componentSources(path);
|
|
154
|
+
return /\.tsx?$/.test(entry) && !/\.(spec|stories)\.tsx?$/.test(entry) ? [path] : [];
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
describe("no Tamagui Popover in the table (MPO-221)", () => {
|
|
159
|
+
it("no component source imports Popover from tamagui", () => {
|
|
160
|
+
const offenders = componentSources(__dirname).filter((file) =>
|
|
161
|
+
/import\s*{[^}]*\bPopover\b[^}]*}\s*from\s*["']tamagui["']/.test(readFileSync(file, "utf8")),
|
|
162
|
+
);
|
|
163
|
+
expect(offenders).toEqual([]);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("no component source mounts the components PopoverContent", () => {
|
|
167
|
+
const offenders = componentSources(__dirname).filter((file) =>
|
|
168
|
+
/\bPopoverContent\b/.test(readFileSync(file, "utf8")),
|
|
169
|
+
);
|
|
170
|
+
expect(offenders).toEqual([]);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -111,3 +111,156 @@ describe("useDataTable controlledState adoption", () => {
|
|
|
111
111
|
expect(result.current.state.filters).toEqual([]);
|
|
112
112
|
});
|
|
113
113
|
});
|
|
114
|
+
|
|
115
|
+
describe("useDataTable column width normalization", () => {
|
|
116
|
+
type Row = (typeof data)[number];
|
|
117
|
+
|
|
118
|
+
it.each<[Partial<DataTableColumn<Row>>, number]>([
|
|
119
|
+
[{ width: 320 }, 320],
|
|
120
|
+
[{ width: 320, size: 90 }, 90],
|
|
121
|
+
[{ width: 320, minWidth: 200, maxWidth: 240 }, 240],
|
|
122
|
+
[{ width: 320, size: 90, minWidth: 200, minSize: 60, maxWidth: 80, maxSize: 100 }, 90],
|
|
123
|
+
[{ width: 320, size: 180, maxWidth: 140 }, 140],
|
|
124
|
+
[{ width: 125.5 }, 125.5],
|
|
125
|
+
[{ width: 0 }, 20],
|
|
126
|
+
[{ width: 0, minWidth: 0 }, 0],
|
|
127
|
+
[{ width: 320, size: 0, minSize: 0 }, 0],
|
|
128
|
+
[{ width: Number.NaN }, 150],
|
|
129
|
+
[{ width: Number.POSITIVE_INFINITY }, 150],
|
|
130
|
+
[{ width: -1 }, 150],
|
|
131
|
+
[{ minWidth: Number.NaN, maxWidth: -1 }, 150],
|
|
132
|
+
])("resolves %j to %s through the real table", (sizing, expected) => {
|
|
133
|
+
const { result } = renderHook(() =>
|
|
134
|
+
useDataTable({ data, columns: [{ id: "name", accessorKey: "name", ...sizing }] }),
|
|
135
|
+
);
|
|
136
|
+
expect(result.current.table.getColumn("name")?.getSize()).toBe(expected);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("preserves input objects and normalizes grouped leaves without distributing group width", () => {
|
|
140
|
+
const leaf: DataTableColumn<Row> = { id: "name", accessorKey: "name", width: 320 };
|
|
141
|
+
const group: DataTableColumn<Row> = {
|
|
142
|
+
id: "group",
|
|
143
|
+
header: "Group",
|
|
144
|
+
width: 900,
|
|
145
|
+
columns: [leaf],
|
|
146
|
+
};
|
|
147
|
+
const plain: DataTableColumn<Row> = { id: "status", accessorKey: "status" };
|
|
148
|
+
Object.freeze(leaf);
|
|
149
|
+
Object.freeze(group.columns);
|
|
150
|
+
Object.freeze(group);
|
|
151
|
+
Object.freeze(plain);
|
|
152
|
+
const source = [group, plain];
|
|
153
|
+
Object.freeze(source);
|
|
154
|
+
const { result, rerender } = renderHook(() => useDataTable({ data, columns: source }));
|
|
155
|
+
expect(result.current.table.getColumn("name")?.getSize()).toBe(320);
|
|
156
|
+
expect(leaf).not.toHaveProperty("size");
|
|
157
|
+
expect(group.columns?.[0]).toBe(leaf);
|
|
158
|
+
expect(result.current.table.options.columns[1]).toBe(plain);
|
|
159
|
+
const normalized = result.current.table.options.columns;
|
|
160
|
+
rerender();
|
|
161
|
+
expect(result.current.table.options.columns).toBe(normalized);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("keeps sizing state ahead of updated declarations and resets to the current definition", () => {
|
|
165
|
+
const { result, rerender } = renderHook(
|
|
166
|
+
({ width }) => useDataTable({ data, columns: [{ id: "name", accessorKey: "name", width }] }),
|
|
167
|
+
{ initialProps: { width: 320 } },
|
|
168
|
+
);
|
|
169
|
+
expect(result.current.table.getColumn("name")?.getSize()).toBe(320);
|
|
170
|
+
act(() => result.current.actions.setColumnSizing({ name: 210 }));
|
|
171
|
+
rerender({ width: 400 });
|
|
172
|
+
expect(result.current.table.getColumn("name")?.getSize()).toBe(210);
|
|
173
|
+
act(() => result.current.actions.resetToDefault());
|
|
174
|
+
expect(result.current.table.getColumn("name")?.getSize()).toBe(400);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("loads existing view width state without renaming its persistence field", () => {
|
|
178
|
+
const { result } = renderHook(() =>
|
|
179
|
+
useDataTable({
|
|
180
|
+
data,
|
|
181
|
+
columns: [{ id: "name", accessorKey: "name", width: 320 }],
|
|
182
|
+
initialState: {
|
|
183
|
+
views: [
|
|
184
|
+
{
|
|
185
|
+
id: "saved",
|
|
186
|
+
name: "Saved",
|
|
187
|
+
columns: { name: { id: "name", visible: true, width: 205, order: 0 } },
|
|
188
|
+
filters: [],
|
|
189
|
+
sorting: [],
|
|
190
|
+
pagination: { pageIndex: 0, pageSize: 10 },
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
}),
|
|
195
|
+
);
|
|
196
|
+
act(() => result.current.actions.loadView("saved"));
|
|
197
|
+
expect(result.current.table.getColumn("name")?.getSize()).toBe(205);
|
|
198
|
+
expect(result.current.state.columnSizing).toEqual({ name: 205 });
|
|
199
|
+
act(() => result.current.actions.resetToDefault());
|
|
200
|
+
expect(result.current.table.getColumn("name")?.getSize()).toBe(320);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("preserves sorting/filter aliases and explicit overrides alongside width", () => {
|
|
204
|
+
const sortingFn = vi.fn(() => 0);
|
|
205
|
+
const { result } = renderHook(() =>
|
|
206
|
+
useDataTable({
|
|
207
|
+
data,
|
|
208
|
+
columns: [
|
|
209
|
+
{ id: "name", accessorKey: "name", width: 320, sortable: false, filterable: false },
|
|
210
|
+
{
|
|
211
|
+
id: "status",
|
|
212
|
+
accessorKey: "status",
|
|
213
|
+
width: 90,
|
|
214
|
+
sortable: false,
|
|
215
|
+
enableSorting: true,
|
|
216
|
+
sortingFn,
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
221
|
+
expect(result.current.table.getColumn("name")?.getSize()).toBe(320);
|
|
222
|
+
expect(result.current.table.getColumn("name")?.getCanSort()).toBe(false);
|
|
223
|
+
expect(result.current.table.getColumn("name")?.getCanFilter()).toBe(false);
|
|
224
|
+
expect(result.current.table.getColumn("status")?.getCanSort()).toBe(true);
|
|
225
|
+
expect(result.current.table.getColumn("status")?.columnDef.sortingFn).toBe(sortingFn);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// columnOrder seeding — id-less columns keep the caller's declared order
|
|
231
|
+
// (MPO-292: `col.id || ""` matched no column, so every id-less column fell
|
|
232
|
+
// behind every id'd one)
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
describe("useDataTable columnOrder seeding", () => {
|
|
236
|
+
type Row = { id: number; name: string; status: string; owner: { name: string } };
|
|
237
|
+
const rows: Row[] = [
|
|
238
|
+
{ id: 1, name: "Alice", status: "Active", owner: { name: "Ops" } },
|
|
239
|
+
{ id: 2, name: "Bob", status: "Lead", owner: { name: "Sales" } },
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
it("seeds columnOrder with TanStack's own key for every column, id or not", () => {
|
|
243
|
+
const columns = [
|
|
244
|
+
{ accessorKey: "name", header: "Name" },
|
|
245
|
+
{ id: "_actions", header: "Actions", cell: () => null },
|
|
246
|
+
{ accessorKey: "owner.name", header: "Owner" },
|
|
247
|
+
{ accessorFn: (row: Row) => row.status, header: "Status" },
|
|
248
|
+
] as DataTableColumn<Row>[];
|
|
249
|
+
const { result } = renderHook(() => useDataTable({ data: rows, columns }));
|
|
250
|
+
const order = result.current.state.columnOrder;
|
|
251
|
+
expect(order).toEqual(["name", "_actions", "owner_name", "Status"]);
|
|
252
|
+
expect(new Set(order).size).toBe(columns.length);
|
|
253
|
+
for (const key of order) expect(result.current.table.getColumn(key)?.id).toBe(key);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("renders getVisibleCells() in the declared order when id'd and id-less columns mix", () => {
|
|
257
|
+
const columns = [
|
|
258
|
+
{ accessorKey: "name", header: "Name" },
|
|
259
|
+
{ id: "_actions", header: "Actions", cell: () => null },
|
|
260
|
+
{ accessorKey: "status", header: "Status" },
|
|
261
|
+
] as DataTableColumn<Row>[];
|
|
262
|
+
const { result } = renderHook(() => useDataTable({ data: rows, columns }));
|
|
263
|
+
const cells = result.current.table.getRowModel().rows[0].getVisibleCells();
|
|
264
|
+
expect(cells.map((cell) => cell.column.id)).toEqual(["name", "_actions", "status"]);
|
|
265
|
+
});
|
|
266
|
+
});
|