@multiplatform.one/table 7.7.6 → 7.8.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/dist/cjs/components/ColumnHeaderFilter/index.cjs +74 -38
- package/dist/cjs/components/ColumnHeaderFilter/index.native.js +97 -51
- package/dist/cjs/components/ColumnHeaderFilter/index.native.js.map +1 -1
- package/dist/cjs/components/DataTable/DataTableCardControls.cjs +0 -1
- package/dist/cjs/components/DataTable/DataTableCardControls.native.js +0 -1
- package/dist/cjs/components/DataTable/DataTableCardControls.native.js.map +1 -1
- package/dist/cjs/components/DataTable/DataTableRoot.cjs +33 -6
- package/dist/cjs/components/DataTable/DataTableRoot.native.js +29 -4
- package/dist/cjs/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/cjs/components/DataTable/DataTableStateContext.native.js.map +1 -1
- package/dist/cjs/components/DataTableBulkActions/index.cjs +0 -4
- package/dist/cjs/components/DataTableBulkActions/index.native.js +0 -4
- package/dist/cjs/components/DataTableBulkActions/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableCell/index.cjs +16 -1
- package/dist/cjs/components/DataTableCell/index.native.js +16 -1
- package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableDateFilter/index.cjs +0 -7
- package/dist/cjs/components/DataTableDateFilter/index.native.js +0 -7
- package/dist/cjs/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableFilterButton/index.cjs +0 -1
- package/dist/cjs/components/DataTableFilterButton/index.native.js +0 -1
- package/dist/cjs/components/DataTableFilterChips/FilterChip.cjs +2 -1
- package/dist/cjs/components/DataTableFilterChips/FilterChip.native.js +4 -1
- package/dist/cjs/components/DataTableFilterChips/FilterChip.native.js.map +1 -1
- package/dist/cjs/components/DataTableFilters/index.cjs +0 -1
- package/dist/cjs/components/DataTableFilters/index.native.js +0 -1
- package/dist/cjs/components/DataTableGrouping/index.cjs +0 -8
- package/dist/cjs/components/DataTableGrouping/index.native.js +0 -8
- package/dist/cjs/components/DataTableGrouping/index.native.js.map +1 -1
- package/dist/cjs/components/DataTablePagination/index.cjs +29 -13
- package/dist/cjs/components/DataTablePagination/index.native.js +30 -14
- package/dist/cjs/components/DataTablePagination/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableSearch/index.cjs +0 -3
- package/dist/cjs/components/DataTableSearch/index.native.js +0 -3
- package/dist/cjs/components/DataTableSearch/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableToolbar/index.cjs +0 -1
- package/dist/cjs/components/DataTableToolbar/index.native.js +0 -1
- package/dist/cjs/components/DataTableToolbar/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableViewOptions/index.cjs +0 -5
- package/dist/cjs/components/DataTableViewOptions/index.native.js +0 -5
- package/dist/cjs/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/cjs/components/FilterManager.cjs +0 -1
- package/dist/cjs/components/FilterManager.native.js +0 -1
- package/dist/cjs/components/FilterManager.native.js.map +1 -1
- package/dist/cjs/components/FilterOperatorValueEditor/index.cjs +2 -2
- package/dist/cjs/components/FilterOperatorValueEditor/index.native.js +2 -2
- package/dist/cjs/components/FilterOperatorValueEditor/index.native.js.map +1 -1
- package/dist/cjs/components/TableInput/index.cjs +2 -1
- package/dist/cjs/components/TableInput/index.native.js +4 -2
- package/dist/cjs/components/TableInput/index.native.js.map +1 -1
- package/dist/cjs/hooks/useDataTable.cjs +24 -8
- package/dist/cjs/hooks/useDataTable.native.js +28 -8
- package/dist/cjs/hooks/useDataTable.native.js.map +1 -1
- package/dist/cjs/index.cjs +12 -0
- package/dist/cjs/index.native.js +12 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/shared/devWarn.cjs +70 -0
- package/dist/cjs/shared/devWarn.native.js +84 -0
- package/dist/cjs/shared/devWarn.native.js.map +1 -0
- package/dist/cjs/utils/columnAlign.cjs +4 -1
- package/dist/cjs/utils/columnAlign.native.js +4 -1
- package/dist/cjs/utils/columnAlign.native.js.map +1 -1
- package/dist/cjs/utils/filterFields.cjs +5 -0
- package/dist/cjs/utils/filterFields.native.js +5 -0
- package/dist/cjs/utils/filterFields.native.js.map +1 -1
- package/dist/cjs/utils/filterUtils.cjs +30 -0
- package/dist/cjs/utils/filterUtils.native.js +50 -0
- package/dist/cjs/utils/filterUtils.native.js.map +1 -1
- package/dist/cjs/utils/money.cjs +137 -0
- package/dist/cjs/utils/money.native.js +165 -0
- package/dist/cjs/utils/money.native.js.map +1 -0
- package/dist/cjs/utils/rowAccumulator.cjs +48 -0
- package/dist/cjs/utils/rowAccumulator.native.js +51 -0
- package/dist/cjs/utils/rowAccumulator.native.js.map +1 -0
- package/dist/esm/components/ColumnHeaderFilter/index.mjs +78 -42
- package/dist/esm/components/ColumnHeaderFilter/index.mjs.map +1 -1
- package/dist/esm/components/ColumnHeaderFilter/index.native.js +101 -55
- package/dist/esm/components/ColumnHeaderFilter/index.native.js.map +1 -1
- package/dist/esm/components/DataTable/DataTableCardControls.mjs +0 -1
- package/dist/esm/components/DataTable/DataTableCardControls.mjs.map +1 -1
- package/dist/esm/components/DataTable/DataTableCardControls.native.js +0 -1
- package/dist/esm/components/DataTable/DataTableCardControls.native.js.map +1 -1
- package/dist/esm/components/DataTable/DataTableRoot.mjs +33 -6
- package/dist/esm/components/DataTable/DataTableRoot.mjs.map +1 -1
- package/dist/esm/components/DataTable/DataTableRoot.native.js +29 -4
- package/dist/esm/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/esm/components/DataTable/DataTableStateContext.mjs.map +1 -1
- package/dist/esm/components/DataTable/DataTableStateContext.native.js.map +1 -1
- package/dist/esm/components/DataTableBulkActions/index.mjs +0 -4
- package/dist/esm/components/DataTableBulkActions/index.mjs.map +1 -1
- package/dist/esm/components/DataTableBulkActions/index.native.js +0 -4
- package/dist/esm/components/DataTableBulkActions/index.native.js.map +1 -1
- package/dist/esm/components/DataTableCell/index.mjs +17 -2
- package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
- package/dist/esm/components/DataTableCell/index.native.js +17 -2
- package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
- package/dist/esm/components/DataTableDateFilter/index.mjs +0 -7
- package/dist/esm/components/DataTableDateFilter/index.mjs.map +1 -1
- package/dist/esm/components/DataTableDateFilter/index.native.js +0 -7
- package/dist/esm/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/esm/components/DataTableFilterButton/index.mjs +0 -1
- package/dist/esm/components/DataTableFilterButton/index.mjs.map +1 -1
- package/dist/esm/components/DataTableFilterButton/index.native.js +0 -1
- package/dist/esm/components/DataTableFilterChips/FilterChip.mjs +3 -2
- package/dist/esm/components/DataTableFilterChips/FilterChip.mjs.map +1 -1
- package/dist/esm/components/DataTableFilterChips/FilterChip.native.js +5 -2
- package/dist/esm/components/DataTableFilterChips/FilterChip.native.js.map +1 -1
- package/dist/esm/components/DataTableFilters/index.mjs +0 -1
- package/dist/esm/components/DataTableFilters/index.mjs.map +1 -1
- package/dist/esm/components/DataTableFilters/index.native.js +0 -1
- package/dist/esm/components/DataTableGrouping/index.mjs +0 -8
- package/dist/esm/components/DataTableGrouping/index.mjs.map +1 -1
- package/dist/esm/components/DataTableGrouping/index.native.js +0 -8
- package/dist/esm/components/DataTableGrouping/index.native.js.map +1 -1
- package/dist/esm/components/DataTablePagination/index.mjs +30 -14
- package/dist/esm/components/DataTablePagination/index.mjs.map +1 -1
- package/dist/esm/components/DataTablePagination/index.native.js +31 -15
- package/dist/esm/components/DataTablePagination/index.native.js.map +1 -1
- package/dist/esm/components/DataTableSearch/index.mjs +0 -3
- package/dist/esm/components/DataTableSearch/index.mjs.map +1 -1
- package/dist/esm/components/DataTableSearch/index.native.js +0 -3
- package/dist/esm/components/DataTableSearch/index.native.js.map +1 -1
- package/dist/esm/components/DataTableToolbar/index.mjs +0 -1
- package/dist/esm/components/DataTableToolbar/index.mjs.map +1 -1
- package/dist/esm/components/DataTableToolbar/index.native.js +0 -1
- package/dist/esm/components/DataTableToolbar/index.native.js.map +1 -1
- package/dist/esm/components/DataTableViewOptions/index.mjs +0 -5
- package/dist/esm/components/DataTableViewOptions/index.mjs.map +1 -1
- package/dist/esm/components/DataTableViewOptions/index.native.js +0 -5
- package/dist/esm/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/esm/components/FilterManager.mjs +0 -1
- package/dist/esm/components/FilterManager.mjs.map +1 -1
- package/dist/esm/components/FilterManager.native.js +0 -1
- package/dist/esm/components/FilterManager.native.js.map +1 -1
- package/dist/esm/components/FilterOperatorValueEditor/index.mjs +2 -2
- package/dist/esm/components/FilterOperatorValueEditor/index.mjs.map +1 -1
- package/dist/esm/components/FilterOperatorValueEditor/index.native.js +2 -2
- package/dist/esm/components/FilterOperatorValueEditor/index.native.js.map +1 -1
- package/dist/esm/components/TableInput/index.mjs +3 -2
- package/dist/esm/components/TableInput/index.mjs.map +1 -1
- package/dist/esm/components/TableInput/index.native.js +5 -3
- package/dist/esm/components/TableInput/index.native.js.map +1 -1
- package/dist/esm/hooks/useDataTable.mjs +24 -8
- package/dist/esm/hooks/useDataTable.mjs.map +1 -1
- package/dist/esm/hooks/useDataTable.native.js +28 -8
- package/dist/esm/hooks/useDataTable.native.js.map +1 -1
- package/dist/esm/index.mjs +3 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +3 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/shared/devWarn.mjs +43 -0
- package/dist/esm/shared/devWarn.mjs.map +1 -0
- package/dist/esm/shared/devWarn.native.js +54 -0
- package/dist/esm/shared/devWarn.native.js.map +1 -0
- package/dist/esm/utils/columnAlign.mjs +4 -1
- package/dist/esm/utils/columnAlign.mjs.map +1 -1
- package/dist/esm/utils/columnAlign.native.js +4 -1
- package/dist/esm/utils/columnAlign.native.js.map +1 -1
- package/dist/esm/utils/filterFields.mjs +5 -0
- package/dist/esm/utils/filterFields.mjs.map +1 -1
- package/dist/esm/utils/filterFields.native.js +5 -0
- package/dist/esm/utils/filterFields.native.js.map +1 -1
- package/dist/esm/utils/filterUtils.mjs +30 -0
- package/dist/esm/utils/filterUtils.mjs.map +1 -1
- package/dist/esm/utils/filterUtils.native.js +50 -0
- package/dist/esm/utils/filterUtils.native.js.map +1 -1
- package/dist/esm/utils/money.mjs +104 -0
- package/dist/esm/utils/money.mjs.map +1 -0
- package/dist/esm/utils/money.native.js +129 -0
- package/dist/esm/utils/money.native.js.map +1 -0
- package/dist/esm/utils/rowAccumulator.mjs +23 -0
- package/dist/esm/utils/rowAccumulator.mjs.map +1 -0
- package/dist/esm/utils/rowAccumulator.native.js +23 -0
- package/dist/esm/utils/rowAccumulator.native.js.map +1 -0
- package/dist/jsx/components/ColumnHeaderFilter/index.mjs +78 -42
- package/dist/jsx/components/ColumnHeaderFilter/index.mjs.map +1 -1
- package/dist/jsx/components/ColumnHeaderFilter/index.native.js +97 -51
- package/dist/jsx/components/ColumnHeaderFilter/index.native.js.map +1 -1
- package/dist/jsx/components/DataTable/DataTableCardControls.mjs +0 -1
- package/dist/jsx/components/DataTable/DataTableCardControls.mjs.map +1 -1
- package/dist/jsx/components/DataTable/DataTableCardControls.native.js +0 -1
- package/dist/jsx/components/DataTable/DataTableCardControls.native.js.map +1 -1
- package/dist/jsx/components/DataTable/DataTableRoot.mjs +33 -6
- package/dist/jsx/components/DataTable/DataTableRoot.mjs.map +1 -1
- package/dist/jsx/components/DataTable/DataTableRoot.native.js +29 -4
- package/dist/jsx/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/jsx/components/DataTable/DataTableStateContext.mjs.map +1 -1
- package/dist/jsx/components/DataTable/DataTableStateContext.native.js.map +1 -1
- package/dist/jsx/components/DataTableBulkActions/index.mjs +0 -4
- package/dist/jsx/components/DataTableBulkActions/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableBulkActions/index.native.js +0 -4
- package/dist/jsx/components/DataTableBulkActions/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableCell/index.mjs +17 -2
- package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableCell/index.native.js +16 -1
- package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableDateFilter/index.mjs +0 -7
- package/dist/jsx/components/DataTableDateFilter/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableDateFilter/index.native.js +0 -7
- package/dist/jsx/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableFilterButton/index.mjs +0 -1
- package/dist/jsx/components/DataTableFilterButton/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableFilterButton/index.native.js +0 -1
- package/dist/jsx/components/DataTableFilterChips/FilterChip.mjs +3 -2
- package/dist/jsx/components/DataTableFilterChips/FilterChip.mjs.map +1 -1
- package/dist/jsx/components/DataTableFilterChips/FilterChip.native.js +4 -1
- package/dist/jsx/components/DataTableFilterChips/FilterChip.native.js.map +1 -1
- package/dist/jsx/components/DataTableFilters/index.mjs +0 -1
- package/dist/jsx/components/DataTableFilters/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableFilters/index.native.js +0 -1
- package/dist/jsx/components/DataTableGrouping/index.mjs +0 -8
- package/dist/jsx/components/DataTableGrouping/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableGrouping/index.native.js +0 -8
- package/dist/jsx/components/DataTableGrouping/index.native.js.map +1 -1
- package/dist/jsx/components/DataTablePagination/index.mjs +30 -14
- package/dist/jsx/components/DataTablePagination/index.mjs.map +1 -1
- package/dist/jsx/components/DataTablePagination/index.native.js +30 -14
- package/dist/jsx/components/DataTablePagination/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableSearch/index.mjs +0 -3
- package/dist/jsx/components/DataTableSearch/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableSearch/index.native.js +0 -3
- package/dist/jsx/components/DataTableSearch/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableToolbar/index.mjs +0 -1
- package/dist/jsx/components/DataTableToolbar/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableToolbar/index.native.js +0 -1
- package/dist/jsx/components/DataTableToolbar/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableViewOptions/index.mjs +0 -5
- package/dist/jsx/components/DataTableViewOptions/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableViewOptions/index.native.js +0 -5
- package/dist/jsx/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/jsx/components/FilterManager.mjs +0 -1
- package/dist/jsx/components/FilterManager.mjs.map +1 -1
- package/dist/jsx/components/FilterManager.native.js +0 -1
- package/dist/jsx/components/FilterManager.native.js.map +1 -1
- package/dist/jsx/components/FilterOperatorValueEditor/index.mjs +2 -2
- package/dist/jsx/components/FilterOperatorValueEditor/index.mjs.map +1 -1
- package/dist/jsx/components/FilterOperatorValueEditor/index.native.js +2 -2
- package/dist/jsx/components/FilterOperatorValueEditor/index.native.js.map +1 -1
- package/dist/jsx/components/TableInput/index.mjs +3 -2
- package/dist/jsx/components/TableInput/index.mjs.map +1 -1
- package/dist/jsx/components/TableInput/index.native.js +4 -2
- package/dist/jsx/components/TableInput/index.native.js.map +1 -1
- package/dist/jsx/hooks/useDataTable.mjs +24 -8
- package/dist/jsx/hooks/useDataTable.mjs.map +1 -1
- package/dist/jsx/hooks/useDataTable.native.js +28 -8
- package/dist/jsx/hooks/useDataTable.native.js.map +1 -1
- package/dist/jsx/index.js +3 -1
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +3 -1
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +12 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/shared/devWarn.mjs +43 -0
- package/dist/jsx/shared/devWarn.mjs.map +1 -0
- package/dist/jsx/shared/devWarn.native.js +84 -0
- package/dist/jsx/shared/devWarn.native.js.map +1 -0
- package/dist/jsx/utils/columnAlign.mjs +4 -1
- package/dist/jsx/utils/columnAlign.mjs.map +1 -1
- package/dist/jsx/utils/columnAlign.native.js +4 -1
- package/dist/jsx/utils/columnAlign.native.js.map +1 -1
- package/dist/jsx/utils/filterFields.mjs +5 -0
- package/dist/jsx/utils/filterFields.mjs.map +1 -1
- package/dist/jsx/utils/filterFields.native.js +5 -0
- package/dist/jsx/utils/filterFields.native.js.map +1 -1
- package/dist/jsx/utils/filterUtils.mjs +30 -0
- package/dist/jsx/utils/filterUtils.mjs.map +1 -1
- package/dist/jsx/utils/filterUtils.native.js +50 -0
- package/dist/jsx/utils/filterUtils.native.js.map +1 -1
- package/dist/jsx/utils/money.mjs +104 -0
- package/dist/jsx/utils/money.mjs.map +1 -0
- package/dist/jsx/utils/money.native.js +165 -0
- package/dist/jsx/utils/money.native.js.map +1 -0
- package/dist/jsx/utils/rowAccumulator.mjs +23 -0
- package/dist/jsx/utils/rowAccumulator.mjs.map +1 -0
- package/dist/jsx/utils/rowAccumulator.native.js +51 -0
- package/dist/jsx/utils/rowAccumulator.native.js.map +1 -0
- package/package.json +12 -12
- package/src/components/ColumnHeaderFilter/ColumnHeaderFilter.spec.tsx +142 -7
- package/src/components/ColumnHeaderFilter/ColumnHeaderFilter.stories.tsx +46 -9
- package/src/components/ColumnHeaderFilter/index.tsx +104 -47
- package/src/components/DataTable/DataTable.stories.tsx +186 -14
- package/src/components/DataTable/DataTableCardControls.tsx +11 -13
- package/src/components/DataTable/DataTableRoot.tsx +40 -14
- package/src/components/DataTable/DataTableStateContext.ts +7 -0
- package/src/components/DataTable/columnWidthLayout.spec.tsx +195 -0
- package/src/components/DataTable/headerSorting.spec.tsx +25 -0
- package/src/components/DataTable/moneyColumn.spec.tsx +151 -0
- package/src/components/DataTable/rowAccumulator.spec.tsx +171 -0
- package/src/components/DataTableBulkActions/index.tsx +0 -4
- package/src/components/DataTableCell/index.tsx +28 -2
- package/src/components/DataTableColumnPinning/DataTableColumnPinning.stories.tsx +0 -4
- package/src/components/DataTableDateFilter/index.tsx +0 -7
- package/src/components/DataTableFilterButton/index.tsx +1 -1
- package/src/components/DataTableFilterChips/FilterChip.spec.tsx +6 -0
- package/src/components/DataTableFilterChips/FilterChip.tsx +3 -2
- package/src/components/DataTableFilters/index.tsx +1 -1
- package/src/components/DataTableGrouping/DataTableGrouping.stories.tsx +0 -4
- package/src/components/DataTableGrouping/index.tsx +1 -13
- package/src/components/DataTablePagination/DataTablePagination.stories.tsx +14 -1
- package/src/components/DataTablePagination/DataTablePagination.width.spec.tsx +51 -0
- package/src/components/DataTablePagination/index.tsx +60 -16
- package/src/components/DataTableSearch/index.tsx +2 -3
- package/src/components/DataTableToolbar/DataTableToolbar.stories.tsx +2 -4
- package/src/components/DataTableToolbar/index.tsx +1 -5
- package/src/components/DataTableViewOptions/index.tsx +2 -13
- package/src/components/FilterManager.tsx +1 -3
- package/src/components/FilterOperatorValueEditor/FilterOperatorValueEditor.spec.tsx +1 -1
- package/src/components/FilterOperatorValueEditor/FilterOperatorValueEditor.stories.tsx +1 -0
- package/src/components/FilterOperatorValueEditor/index.tsx +10 -2
- package/src/components/TableInput/index.native.tsx +3 -2
- package/src/components/TableInput/index.tsx +3 -2
- package/src/hooks/useDataTable.ts +43 -7
- package/src/index.ts +13 -0
- package/src/shared/devWarn.ts +97 -0
- package/src/types.ts +71 -2
- package/src/utils/columnAlign.ts +9 -1
- package/src/utils/filterFields.ts +23 -1
- package/src/utils/filterUtils.ts +50 -1
- package/src/utils/money.spec.ts +156 -0
- package/src/utils/money.ts +202 -0
- package/src/utils/rowAccumulator.ts +136 -0
- package/types/components/ColumnHeaderFilter/index.d.ts +9 -2
- package/types/components/ColumnHeaderFilter/index.d.ts.map +1 -1
- package/types/components/DataTable/DataTableCardControls.d.ts.map +1 -1
- package/types/components/DataTable/DataTableRoot.d.ts +1 -1
- package/types/components/DataTable/DataTableRoot.d.ts.map +1 -1
- package/types/components/DataTable/DataTableStateContext.d.ts +7 -0
- package/types/components/DataTable/DataTableStateContext.d.ts.map +1 -1
- package/types/components/DataTableBulkActions/index.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/DataTableFilterChips/FilterChip.d.ts.map +1 -1
- package/types/components/DataTableGrouping/index.d.ts.map +1 -1
- package/types/components/DataTablePagination/index.d.ts.map +1 -1
- package/types/components/DataTableSearch/index.d.ts.map +1 -1
- package/types/components/DataTableToolbar/index.d.ts.map +1 -1
- package/types/components/DataTableViewOptions/index.d.ts.map +1 -1
- package/types/components/FilterManager.d.ts.map +1 -1
- package/types/components/FilterOperatorValueEditor/index.d.ts.map +1 -1
- package/types/components/TableInput/index.d.ts.map +1 -1
- package/types/components/TableInput/index.native.d.ts.map +1 -1
- package/types/hooks/useDataTable.d.ts +1 -1
- package/types/hooks/useDataTable.d.ts.map +1 -1
- package/types/index.d.ts +3 -0
- package/types/index.d.ts.map +1 -1
- package/types/shared/devWarn.d.ts +50 -0
- package/types/shared/devWarn.d.ts.map +1 -0
- package/types/types.d.ts +71 -3
- package/types/types.d.ts.map +1 -1
- package/types/utils/columnAlign.d.ts.map +1 -1
- package/types/utils/filterFields.d.ts.map +1 -1
- package/types/utils/filterUtils.d.ts +1 -1
- package/types/utils/filterUtils.d.ts.map +1 -1
- package/types/utils/money.d.ts +94 -0
- package/types/utils/money.d.ts.map +1 -0
- package/types/utils/rowAccumulator.d.ts +107 -0
- package/types/utils/rowAccumulator.d.ts.map +1 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPO-236 — per-column width under `columnLayout: "equal"`.
|
|
3
|
+
*
|
|
4
|
+
* `equal` is the default, and it flexes every cell to `flexGrow: 1,
|
|
5
|
+
* flexBasis: 0`. A column's `width` / `size` therefore never reaches the cell
|
|
6
|
+
* box: the render comes out identical to one that declared no widths at all,
|
|
7
|
+
* which reads as "the widths ARE applied and are not enough" and sends the
|
|
8
|
+
* reader looking in the wrong place.
|
|
9
|
+
*
|
|
10
|
+
* The default is not changed here — that would move every existing table's
|
|
11
|
+
* render. The combination warns instead, naming `columnLayout` as the reason.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
15
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
16
|
+
import type { DataTableColumn } from "../../types";
|
|
17
|
+
import { __resetTableDevWarnSeen, warnColumnWidthIgnored } from "../../shared/devWarn";
|
|
18
|
+
import { DataTable } from "./index";
|
|
19
|
+
|
|
20
|
+
type Pokemon = { id: number; name: string; xp: number };
|
|
21
|
+
|
|
22
|
+
const sampleData: Pokemon[] = [
|
|
23
|
+
{ id: 1, name: "Bulbasaur", xp: 64 },
|
|
24
|
+
{ id: 2, name: "Charmander", xp: 62 },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const plainColumns: DataTableColumn<Pokemon>[] = [
|
|
28
|
+
{ id: "name", accessorKey: "name", header: "Name" },
|
|
29
|
+
{ id: "xp", accessorKey: "xp", header: "XP" },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const widthColumns: DataTableColumn<Pokemon>[] = [
|
|
33
|
+
{ id: "name", accessorKey: "name", header: "Name", width: 320 },
|
|
34
|
+
{ id: "xp", accessorKey: "xp", header: "XP", width: 80 },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
let warnings: string[] = [];
|
|
38
|
+
let warnSpy: ReturnType<typeof vi.spyOn>;
|
|
39
|
+
|
|
40
|
+
function startCapture() {
|
|
41
|
+
warnings = [];
|
|
42
|
+
__resetTableDevWarnSeen();
|
|
43
|
+
warnSpy = vi.spyOn(console, "warn").mockImplementation((...args) => {
|
|
44
|
+
warnings.push(args.map((arg) => String(arg)).join(" "));
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function stopCapture() {
|
|
49
|
+
warnSpy.mockRestore();
|
|
50
|
+
__resetTableDevWarnSeen();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Only this rule's warnings — other table chrome may warn for its own reasons. */
|
|
54
|
+
function widthWarnings(): string[] {
|
|
55
|
+
return warnings.filter((line) => line.includes("column-width-ignored"));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* React 19's client `useId` counter is module-global (`_r_<base32>_`), so two
|
|
60
|
+
* renders in one process never share generated ids. Normalize them out before
|
|
61
|
+
* comparing markup.
|
|
62
|
+
*/
|
|
63
|
+
function stripGeneratedIds(html: string): string {
|
|
64
|
+
return html.replace(/_r_[0-9a-z]+_/g, "_r_id_");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** `columnLayout` omitted on purpose in most calls: "equal" is the default. */
|
|
68
|
+
function renderTable(
|
|
69
|
+
columns: DataTableColumn<Pokemon>[],
|
|
70
|
+
columnLayout?: "equal" | "auto" | "scroll",
|
|
71
|
+
) {
|
|
72
|
+
return renderWithProviders(
|
|
73
|
+
<DataTable
|
|
74
|
+
data={sampleData}
|
|
75
|
+
columns={columns as DataTableColumn<Record<string, any>>[]}
|
|
76
|
+
layout="table"
|
|
77
|
+
columnLayout={columnLayout}
|
|
78
|
+
enablePagination={false}
|
|
79
|
+
readOnly
|
|
80
|
+
/>,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
describe("warnColumnWidthIgnored", () => {
|
|
85
|
+
let prevEnv: string | undefined;
|
|
86
|
+
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
prevEnv = process.env.NODE_ENV;
|
|
89
|
+
process.env.NODE_ENV = "development";
|
|
90
|
+
startCapture();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
afterEach(() => {
|
|
94
|
+
stopCapture();
|
|
95
|
+
process.env.NODE_ENV = prevEnv;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("names columnLayout and the offending columns", () => {
|
|
99
|
+
warnColumnWidthIgnored({ columnLayout: "equal", columns: widthColumns });
|
|
100
|
+
expect(widthWarnings()).toHaveLength(1);
|
|
101
|
+
expect(widthWarnings()[0]).toContain("[table] column-width-ignored");
|
|
102
|
+
expect(widthWarnings()[0]).toContain("columnLayout=equal");
|
|
103
|
+
expect(widthWarnings()[0]).toContain("columns=name,xp");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("catches the TanStack `size` spelling too", () => {
|
|
107
|
+
warnColumnWidthIgnored({ columnLayout: "equal", columns: [{ id: "avatar", size: 60 }] });
|
|
108
|
+
expect(widthWarnings()).toHaveLength(1);
|
|
109
|
+
expect(widthWarnings()[0]).toContain("columns=avatar");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("falls back to accessorKey, then position, for the column name", () => {
|
|
113
|
+
warnColumnWidthIgnored({
|
|
114
|
+
columnLayout: "equal",
|
|
115
|
+
columns: [{ accessorKey: "xp", width: 80 }, { size: 60 }],
|
|
116
|
+
});
|
|
117
|
+
expect(widthWarnings()[0]).toContain("columns=xp,#1");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("stays quiet when no column declares a width", () => {
|
|
121
|
+
warnColumnWidthIgnored({ columnLayout: "equal", columns: plainColumns });
|
|
122
|
+
expect(widthWarnings()).toHaveLength(0);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it.each(["auto", "scroll"] as const)("stays quiet under columnLayout=%s", (columnLayout) => {
|
|
126
|
+
warnColumnWidthIgnored({ columnLayout, columns: widthColumns });
|
|
127
|
+
expect(widthWarnings()).toHaveLength(0);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("stays quiet with enableColumnResizing, where the width does survive", () => {
|
|
131
|
+
warnColumnWidthIgnored({
|
|
132
|
+
columnLayout: "equal",
|
|
133
|
+
enableColumnResizing: true,
|
|
134
|
+
columns: widthColumns,
|
|
135
|
+
});
|
|
136
|
+
expect(widthWarnings()).toHaveLength(0);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("no-ops in production", () => {
|
|
140
|
+
process.env.NODE_ENV = "production";
|
|
141
|
+
warnColumnWidthIgnored({ columnLayout: "equal", columns: widthColumns });
|
|
142
|
+
expect(widthWarnings()).toHaveLength(0);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("warns once per offending column set, not once per call", () => {
|
|
146
|
+
warnColumnWidthIgnored({ columnLayout: "equal", columns: widthColumns });
|
|
147
|
+
warnColumnWidthIgnored({ columnLayout: "equal", columns: widthColumns });
|
|
148
|
+
warnColumnWidthIgnored({ columnLayout: "equal", columns: widthColumns });
|
|
149
|
+
expect(widthWarnings()).toHaveLength(1);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe("DataTable columnLayout=equal with per-column widths", () => {
|
|
154
|
+
beforeEach(startCapture);
|
|
155
|
+
afterEach(stopCapture);
|
|
156
|
+
|
|
157
|
+
it("discards the widths: the markup matches a table that declared none", () => {
|
|
158
|
+
const withWidths = renderTable(widthColumns);
|
|
159
|
+
const widthMarkup = stripGeneratedIds(withWidths.container.innerHTML);
|
|
160
|
+
withWidths.unmount();
|
|
161
|
+
|
|
162
|
+
const withoutWidths = renderTable(plainColumns);
|
|
163
|
+
expect(stripGeneratedIds(withoutWidths.container.innerHTML)).toBe(widthMarkup);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("warns, so the discard is not silent", () => {
|
|
167
|
+
renderTable(widthColumns);
|
|
168
|
+
expect(widthWarnings()).toHaveLength(1);
|
|
169
|
+
expect(widthWarnings()[0]).toContain("component=DataTable");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("warns once per table, not once per render", () => {
|
|
173
|
+
const { rerender } = renderTable(widthColumns);
|
|
174
|
+
rerender(
|
|
175
|
+
<DataTable
|
|
176
|
+
data={[...sampleData, { id: 3, name: "Squirtle", xp: 63 }]}
|
|
177
|
+
columns={widthColumns as DataTableColumn<Record<string, any>>[]}
|
|
178
|
+
layout="table"
|
|
179
|
+
enablePagination={false}
|
|
180
|
+
readOnly
|
|
181
|
+
/>,
|
|
182
|
+
);
|
|
183
|
+
expect(widthWarnings()).toHaveLength(1);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("does not warn under columnLayout=auto", () => {
|
|
187
|
+
renderTable(widthColumns, "auto");
|
|
188
|
+
expect(widthWarnings()).toHaveLength(0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("does not warn for a table with no per-column widths", () => {
|
|
192
|
+
renderTable(plainColumns);
|
|
193
|
+
expect(widthWarnings()).toHaveLength(0);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -130,6 +130,31 @@ describe("DataTable header sorting (web grid)", () => {
|
|
|
130
130
|
await waitFor(() => expect(header().getAttribute("aria-sort")).toBe("none"));
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
+
it("gives Phosphor resolved CSS colors in every sort state", async () => {
|
|
134
|
+
const { header } = renderTable();
|
|
135
|
+
for (const direction of ["none", "ascending", "descending"]) {
|
|
136
|
+
await waitFor(() => expect(header().getAttribute("aria-sort")).toBe(direction));
|
|
137
|
+
const icon = header().querySelector("svg");
|
|
138
|
+
expect(icon).not.toBeNull();
|
|
139
|
+
const ink = icon?.getAttribute("fill") || icon?.getAttribute("color") || icon?.style.color;
|
|
140
|
+
expect(ink).toBeTruthy();
|
|
141
|
+
expect(ink).not.toMatch(/\$/);
|
|
142
|
+
fireEvent.click(header());
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("plain click replaces another column sort and Shift+click adds to it", async () => {
|
|
147
|
+
const { header, container, states } = renderTable();
|
|
148
|
+
const ageHeader = container.querySelector("#age-header") as HTMLElement;
|
|
149
|
+
fireEvent.click(header());
|
|
150
|
+
fireEvent.click(ageHeader);
|
|
151
|
+
await waitFor(() => expect(states.at(-1)?.sorting.map((sort) => sort.id)).toEqual(["age"]));
|
|
152
|
+
fireEvent.click(header(), { shiftKey: true });
|
|
153
|
+
await waitFor(() =>
|
|
154
|
+
expect(states.at(-1)?.sorting.map((sort) => sort.id)).toEqual(["age", "firstName"]),
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
133
158
|
it("non-sortable headers get no press affordance, no tab stop, and no aria-sort", () => {
|
|
134
159
|
const { container } = renderWithProviders(
|
|
135
160
|
<DataTable
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The money column type end to end (MPO-252).
|
|
3
|
+
*
|
|
4
|
+
* A Frappe Currency field arrives as a decimal STRING, and before this the
|
|
5
|
+
* kit had no type for that, so it fell to "text". Three consequences, all
|
|
6
|
+
* asserted here: sorting was silently wrong ("9.00" above "10.00"), the
|
|
7
|
+
* filter offered `contains` instead of `isGreaterThan`, and formatting could
|
|
8
|
+
* only read a column-level currency constant even though a ledger mixes
|
|
9
|
+
* currencies row by row.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
13
|
+
import type React from "react";
|
|
14
|
+
import { screen, waitFor } from "@testing-library/react";
|
|
15
|
+
import userEvent from "@testing-library/user-event";
|
|
16
|
+
import { describe, expect, it } from "vitest";
|
|
17
|
+
import type { DataTableColumn } from "../../types";
|
|
18
|
+
import { applyFilterToValue } from "../../utils/filterUtils";
|
|
19
|
+
import { mapFieldToFilterType, operatorsForFilterType } from "../../utils/filterFields";
|
|
20
|
+
import { getDefaultColumnAlign } from "../../utils/columnAlign";
|
|
21
|
+
import { DataTable } from "./index";
|
|
22
|
+
|
|
23
|
+
interface Posting {
|
|
24
|
+
id: string;
|
|
25
|
+
payee: string;
|
|
26
|
+
amount: string;
|
|
27
|
+
currency: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const postings: Posting[] = [
|
|
31
|
+
{ id: "P-1", payee: "Nine", amount: "9.00", currency: "USD" },
|
|
32
|
+
{ id: "P-2", payee: "Ten", amount: "10.00", currency: "USD" },
|
|
33
|
+
{ id: "P-3", payee: "Rupees", amount: "250.75", currency: "INR" },
|
|
34
|
+
{ id: "P-4", payee: "Refund", amount: "-455.87", currency: "USD" },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const columns: DataTableColumn<Posting>[] = [
|
|
38
|
+
{ id: "payee", accessorKey: "payee", header: "Payee" },
|
|
39
|
+
{
|
|
40
|
+
id: "amount",
|
|
41
|
+
accessorKey: "amount",
|
|
42
|
+
header: "Amount",
|
|
43
|
+
field: "money",
|
|
44
|
+
// Safe to turn on now: the money type carries an exact comparator.
|
|
45
|
+
sortable: true,
|
|
46
|
+
fieldProps: { currencyField: "currency", locale: "en-US" },
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
describe("money column type", () => {
|
|
51
|
+
it("maps field money → filter type money, right-aligned by default", () => {
|
|
52
|
+
expect(mapFieldToFilterType("money")).toBe("money");
|
|
53
|
+
expect(
|
|
54
|
+
getDefaultColumnAlign({ id: "amount", field: "money" }),
|
|
55
|
+
"a money column right-aligns without a column-level currency constant",
|
|
56
|
+
).toBe("right");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("offers the numeric filter operators, range included", () => {
|
|
60
|
+
const operators = operatorsForFilterType("money");
|
|
61
|
+
expect(operators).toContain("isLessThan");
|
|
62
|
+
expect(operators).toContain("isGreaterThan");
|
|
63
|
+
expect(operators).toContain("isBetween");
|
|
64
|
+
expect(operators, "text operators are what a money column used to get").not.toContain(
|
|
65
|
+
"contains",
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("filters numerically on decimal strings", () => {
|
|
70
|
+
const gt = { operator: "isGreaterThan" as const, value: "9.5" };
|
|
71
|
+
expect(applyFilterToValue("10.00", gt, "money")).toBe(true);
|
|
72
|
+
expect(applyFilterToValue("9.00", gt, "money")).toBe(false);
|
|
73
|
+
// The same rows through the "text" fallback are the defect: "10.00" is
|
|
74
|
+
// greater than "9.5" as a number and LESS as a string, so the row that
|
|
75
|
+
// should match is the one that gets dropped.
|
|
76
|
+
expect(applyFilterToValue("10.00", gt, "text")).toBe(false);
|
|
77
|
+
|
|
78
|
+
const between = { operator: "isBetween" as const, value: null, values: ["0", "100"] };
|
|
79
|
+
expect(applyFilterToValue("10.00", between, "money")).toBe(true);
|
|
80
|
+
expect(applyFilterToValue("250.75", between, "money")).toBe(false);
|
|
81
|
+
expect(applyFilterToValue("-455.87", between, "money")).toBe(false);
|
|
82
|
+
|
|
83
|
+
const is = { operator: "is" as const, value: "10" };
|
|
84
|
+
expect(applyFilterToValue("10.00", is, "money"), "scale does not change equality").toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("sorts numerically in the table, not lexically", async () => {
|
|
88
|
+
const user = userEvent.setup();
|
|
89
|
+
const { container } = renderWithProviders(
|
|
90
|
+
<DataTable
|
|
91
|
+
data={postings}
|
|
92
|
+
columns={columns}
|
|
93
|
+
layout="table"
|
|
94
|
+
enablePagination={false}
|
|
95
|
+
readOnly
|
|
96
|
+
/>,
|
|
97
|
+
);
|
|
98
|
+
await waitFor(() => expect(screen.getByText("$9.00")).toBeTruthy());
|
|
99
|
+
|
|
100
|
+
await user.click(screen.getByText("Amount"));
|
|
101
|
+
await waitFor(() => {
|
|
102
|
+
const payees = Array.from(container.querySelectorAll("tbody tr"))
|
|
103
|
+
.map((row) => row.querySelector("td")?.textContent)
|
|
104
|
+
.filter((text) => !!text);
|
|
105
|
+
// Ascending: -455.87, 9.00, 10.00, 250.75. Lexically it would have been
|
|
106
|
+
// -455.87, 10.00, 250.75, 9.00 — with 9.00 LAST.
|
|
107
|
+
expect(payees).toEqual(["Refund", "Nine", "Ten", "Rupees"]);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("formats from the ROW's currency, not only a column-level constant", async () => {
|
|
112
|
+
renderWithProviders(
|
|
113
|
+
<DataTable
|
|
114
|
+
data={postings}
|
|
115
|
+
columns={columns}
|
|
116
|
+
layout="table"
|
|
117
|
+
enablePagination={false}
|
|
118
|
+
readOnly
|
|
119
|
+
/>,
|
|
120
|
+
);
|
|
121
|
+
await waitFor(() => expect(screen.getByText("$9.00")).toBeTruthy());
|
|
122
|
+
expect(screen.getByText("$10.00")).toBeTruthy();
|
|
123
|
+
expect(screen.getByText("-$455.87")).toBeTruthy();
|
|
124
|
+
// One row carries INR and renders in INR, in the same column, at the same
|
|
125
|
+
// time — two currencies are two figures and neither converts.
|
|
126
|
+
expect(screen.getByText("₹250.75")).toBeTruthy();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("the MoneyColumn story", () => {
|
|
131
|
+
it("renders mixed currencies and a per-currency footer total", async () => {
|
|
132
|
+
// The story is the documented example. Rendering it here means it cannot
|
|
133
|
+
// rot into something that formats one currency for every row.
|
|
134
|
+
const { MoneyColumn } = await import("./DataTable.stories");
|
|
135
|
+
renderWithProviders(<>{(MoneyColumn.render as () => React.ReactNode)()}</>);
|
|
136
|
+
|
|
137
|
+
await waitFor(() => expect(screen.getByText("Coffee roaster")).toBeTruthy());
|
|
138
|
+
expect(screen.getByText("$9.00")).toBeTruthy();
|
|
139
|
+
expect(screen.getByText("₹250.75")).toBeTruthy();
|
|
140
|
+
// Two totals, not one: USD 9.00 + 10.00 - 455.87 + 9.99, INR 250.75 +
|
|
141
|
+
// 1200.00. Nothing converts between them.
|
|
142
|
+
expect(screen.getByText("-$426.88")).toBeTruthy();
|
|
143
|
+
expect(screen.getByText("₹1,450.75")).toBeTruthy();
|
|
144
|
+
for (const total of ["-$426.88", "₹1,450.75"]) {
|
|
145
|
+
expect(screen.getByText(total).closest("td")?.getAttribute("data-align")).toBe("right");
|
|
146
|
+
const content = screen.getByText(total).closest('[data-cell-align="right"]') as HTMLElement;
|
|
147
|
+
expect(content).toBeTruthy();
|
|
148
|
+
expect(content.style.textAlign).toBe("right");
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Row accumulator — a running balance across pages (MPO-250).
|
|
3
|
+
*
|
|
4
|
+
* The claim under test: a column can be a function of the row AND every row
|
|
5
|
+
* before it, the fold runs BEFORE pagination, and page two therefore
|
|
6
|
+
* continues rather than restarting from zero. That is what no table in the
|
|
7
|
+
* kit could do — `data` is the only channel and a `cell` renderer sees one
|
|
8
|
+
* row — and it is the default column in every register, statement and ledger.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
12
|
+
import { screen, waitFor } from "@testing-library/react";
|
|
13
|
+
import userEvent from "@testing-library/user-event";
|
|
14
|
+
import { describe, expect, it } from "vitest";
|
|
15
|
+
import type { DataTableColumn, RowAccumulator } from "../../types";
|
|
16
|
+
import { accumulateRows } from "../../utils/rowAccumulator";
|
|
17
|
+
import { DataTable } from "./index";
|
|
18
|
+
|
|
19
|
+
interface Posting {
|
|
20
|
+
id: string;
|
|
21
|
+
date: string;
|
|
22
|
+
payee: string;
|
|
23
|
+
/** Cents — integers, so this spec asserts the FOLD, not decimal arithmetic. */
|
|
24
|
+
amount: number;
|
|
25
|
+
/** Derived by the accumulator. Declared here so a column can name it. */
|
|
26
|
+
running?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Twelve postings of +100 each: the running balance is 100, 200, … 1200. */
|
|
30
|
+
const postings: Posting[] = Array.from({ length: 12 }, (_, i) => ({
|
|
31
|
+
id: `P-${String(i + 1).padStart(2, "0")}`,
|
|
32
|
+
date: `2026-01-${String(i + 1).padStart(2, "0")}`,
|
|
33
|
+
payee: `Payee ${String.fromCharCode(90 - i)}`, // Z, Y, X … — reverse of date order
|
|
34
|
+
amount: 100,
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
const runningBalance: RowAccumulator<Posting, number> = {
|
|
38
|
+
initial: 0,
|
|
39
|
+
reduce: (carried, row) => carried + row.amount,
|
|
40
|
+
derive: (carried) => ({ running: carried }),
|
|
41
|
+
order: (a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0),
|
|
42
|
+
total: (carried) => carried,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const columns: DataTableColumn<Posting>[] = [
|
|
46
|
+
{ id: "id", accessorKey: "id", header: "ID" },
|
|
47
|
+
{ id: "payee", accessorKey: "payee", header: "Payee", sortable: true },
|
|
48
|
+
{ id: "amount", accessorKey: "amount", header: "Amount" },
|
|
49
|
+
// Not sortable: sorting a fold column reorders it without refolding, so the
|
|
50
|
+
// figures stop reading down the column as a progression. Rendered with a
|
|
51
|
+
// marker so an assertion can tell a BALANCE of 100 from an AMOUNT of 100.
|
|
52
|
+
{
|
|
53
|
+
id: "running",
|
|
54
|
+
accessorKey: "running",
|
|
55
|
+
header: "Balance",
|
|
56
|
+
sortable: false,
|
|
57
|
+
cell: ({ row }) => `bal ${row.original.running}`,
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
describe("accumulateRows", () => {
|
|
62
|
+
it("folds in the declared order and returns the rows in it", () => {
|
|
63
|
+
const shuffled = [...postings].reverse();
|
|
64
|
+
const { rows, total } = accumulateRows(shuffled, runningBalance);
|
|
65
|
+
expect(rows.map((r) => r.id)).toEqual(postings.map((p) => p.id));
|
|
66
|
+
expect(rows.map((r) => r.running)).toEqual([
|
|
67
|
+
100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200,
|
|
68
|
+
]);
|
|
69
|
+
expect(total, "the fold read once at the end is the footer total").toBe(1200);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("keeps the input array identity when there is no accumulator", () => {
|
|
73
|
+
const { rows, total } = accumulateRows(postings);
|
|
74
|
+
expect(rows).toBe(postings);
|
|
75
|
+
expect(total).toBeUndefined();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("calls a factory seed once per fold, so a mutable seed cannot leak", () => {
|
|
79
|
+
const perCurrency: RowAccumulator<Posting, Record<string, number>> = {
|
|
80
|
+
initial: () => ({}),
|
|
81
|
+
reduce: (carried, row) => {
|
|
82
|
+
carried["USD"] = (carried["USD"] ?? 0) + row.amount;
|
|
83
|
+
return carried;
|
|
84
|
+
},
|
|
85
|
+
derive: (carried) => ({ running: carried["USD"] }),
|
|
86
|
+
};
|
|
87
|
+
const first = accumulateRows(postings, perCurrency);
|
|
88
|
+
const second = accumulateRows(postings, perCurrency);
|
|
89
|
+
expect(first.rows[11]!.running).toBe(1200);
|
|
90
|
+
expect(second.rows[11]!.running, "the second fold starts from a fresh seed").toBe(1200);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("DataTable rowAccumulator", () => {
|
|
95
|
+
it("continues the running balance onto page two instead of restarting", async () => {
|
|
96
|
+
const user = userEvent.setup();
|
|
97
|
+
renderWithProviders(
|
|
98
|
+
<DataTable
|
|
99
|
+
data={postings}
|
|
100
|
+
columns={columns}
|
|
101
|
+
rowAccumulator={runningBalance}
|
|
102
|
+
layout="table"
|
|
103
|
+
initialState={{ pagination: { pageIndex: 0, pageSize: 5 } }}
|
|
104
|
+
/>,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// Page one: the fold's first five values.
|
|
108
|
+
await waitFor(() => expect(screen.getByText("P-01")).toBeTruthy());
|
|
109
|
+
expect(screen.getByText("bal 100")).toBeTruthy();
|
|
110
|
+
expect(screen.getByText("bal 500")).toBeTruthy();
|
|
111
|
+
expect(screen.queryByText("bal 600")).toBeNull();
|
|
112
|
+
|
|
113
|
+
// Page two CONTINUES. Before MPO-250 the only way to get a balance column
|
|
114
|
+
// was a per-row cell renderer, which would have restarted at 100 here.
|
|
115
|
+
await user.click(screen.getByLabelText("Go to next page"));
|
|
116
|
+
await waitFor(() => expect(screen.getByText("P-06")).toBeTruthy());
|
|
117
|
+
expect(screen.getByText("bal 600"), "page two starts where page one ended").toBeTruthy();
|
|
118
|
+
expect(screen.getByText("bal 1000")).toBeTruthy();
|
|
119
|
+
expect(screen.queryByText("bal 100"), "page one's first balance is gone").toBeNull();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("does not refold when the user sorts on another column", async () => {
|
|
123
|
+
const user = userEvent.setup();
|
|
124
|
+
renderWithProviders(
|
|
125
|
+
<DataTable
|
|
126
|
+
data={postings}
|
|
127
|
+
columns={columns}
|
|
128
|
+
rowAccumulator={runningBalance}
|
|
129
|
+
layout="table"
|
|
130
|
+
initialState={{ pagination: { pageIndex: 0, pageSize: 12 } }}
|
|
131
|
+
/>,
|
|
132
|
+
);
|
|
133
|
+
await waitFor(() => expect(screen.getByText("P-01")).toBeTruthy());
|
|
134
|
+
|
|
135
|
+
// Payee runs backwards against date, so a payee sort puts the LAST
|
|
136
|
+
// posting first. Its balance stays 1200 — the fold order is the date
|
|
137
|
+
// order it was computed in, and sorting reorders rows for display only.
|
|
138
|
+
await user.click(screen.getByText("Payee"));
|
|
139
|
+
await waitFor(() => expect(screen.getByText("bal 1200")).toBeTruthy());
|
|
140
|
+
const rendered = screen.getAllByText(/^bal \d+$/).map((el) => el.textContent);
|
|
141
|
+
expect(rendered, "every folded figure is still on screen, attached to its own row").toContain(
|
|
142
|
+
"bal 1200",
|
|
143
|
+
);
|
|
144
|
+
expect(rendered).toContain("bal 100");
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe("the RunningBalance story", () => {
|
|
149
|
+
it("renders the register across two pages with figures that continue", async () => {
|
|
150
|
+
// The story is the documented example (AC: MPO-250). Rendering it here
|
|
151
|
+
// means it cannot rot into something that does not paginate.
|
|
152
|
+
const { RunningBalance } = await import("./DataTable.stories");
|
|
153
|
+
const user = userEvent.setup();
|
|
154
|
+
renderWithProviders(<>{(RunningBalance.render as () => React.ReactNode)()}</>);
|
|
155
|
+
|
|
156
|
+
await waitFor(() => expect(screen.getByText("Opening deposit")).toBeTruthy());
|
|
157
|
+
// Page one is P-01..P-05 and ends on Utilities: 2500.00 - 1800.00 - 84.50
|
|
158
|
+
// + 4123.00 - 146.00.
|
|
159
|
+
expect(screen.getByText("$4,592.50")).toBeTruthy();
|
|
160
|
+
expect(screen.queryByText("$4,573.75"), "page two's first balance is not here yet").toBeNull();
|
|
161
|
+
|
|
162
|
+
await user.click(screen.getByLabelText("Go to next page"));
|
|
163
|
+
await waitFor(() => expect(screen.getByText("Coffee roaster")).toBeTruthy());
|
|
164
|
+
// Page two picks the fold up rather than restarting: -18.75 on 4592.50.
|
|
165
|
+
expect(screen.getByText("$4,573.75"), "page two continues the fold").toBeTruthy();
|
|
166
|
+
expect(screen.getByText("$5,005.85")).toBeTruthy();
|
|
167
|
+
// The footer total is the same fold, read once at the end — every posting,
|
|
168
|
+
// not just this page.
|
|
169
|
+
expect(screen.getAllByText("$6,078.85").length).toBeGreaterThan(0);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -159,7 +159,6 @@ export function DataTableBulkActions<T = any>(props: DataTableBulkActionsProps<T
|
|
|
159
159
|
{showSelectAllMatching && (
|
|
160
160
|
<Button
|
|
161
161
|
size={knobProps.sizeToken}
|
|
162
|
-
variant="outlined"
|
|
163
162
|
onPress={() => ctx?.table.toggleAllRowsSelected(true)}
|
|
164
163
|
aria-label={`Select all ${matchingCount} matching rows`}
|
|
165
164
|
{...knobProps.borderRadius}
|
|
@@ -173,7 +172,6 @@ export function DataTableBulkActions<T = any>(props: DataTableBulkActionsProps<T
|
|
|
173
172
|
<Button
|
|
174
173
|
key={action.id}
|
|
175
174
|
size={knobProps.sizeToken}
|
|
176
|
-
variant="outlined"
|
|
177
175
|
theme={action.variant === "destructive" ? "red" : undefined}
|
|
178
176
|
disabled={action.disabled}
|
|
179
177
|
// DG-ST-03: reason renders via the Button explain affordance;
|
|
@@ -199,7 +197,6 @@ export function DataTableBulkActions<T = any>(props: DataTableBulkActionsProps<T
|
|
|
199
197
|
|
|
200
198
|
<Button
|
|
201
199
|
size={knobProps.sizeToken}
|
|
202
|
-
variant="outlined"
|
|
203
200
|
onPress={onClearSelection}
|
|
204
201
|
aria-label="Clear selection"
|
|
205
202
|
{...knobProps.borderRadius}
|
|
@@ -265,7 +262,6 @@ function BulkActionsOverflowMenu<T = any>({
|
|
|
265
262
|
>
|
|
266
263
|
<Button
|
|
267
264
|
size={knobProps.sizeToken}
|
|
268
|
-
variant="outlined"
|
|
269
265
|
icon={<DotsThreeIcon size={iconPx} />}
|
|
270
266
|
aria-label="More bulk actions"
|
|
271
267
|
{...knobProps.borderRadius}
|
|
@@ -53,9 +53,10 @@ import { Star } from "@phosphor-icons/react";
|
|
|
53
53
|
import { RichTextEditor } from "@multiplatform.one/rich-text";
|
|
54
54
|
import { useResolvedKnobs, ensureCompositeFocusRing } from "@multiplatform.one/theme";
|
|
55
55
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
56
|
-
import { View, isWeb } from "tamagui";
|
|
56
|
+
import { Text, View, isWeb } from "tamagui";
|
|
57
57
|
import type { DataTableFieldProps, DataTableField } from "../../types";
|
|
58
58
|
import { alignToFlex, getDefaultColumnAlign } from "../../utils/columnAlign";
|
|
59
|
+
import { formatMoney } from "../../utils/money";
|
|
59
60
|
import { t } from "../../shared/t";
|
|
60
61
|
|
|
61
62
|
export interface DataTableCellProps {
|
|
@@ -125,7 +126,9 @@ export function DataTableCell({
|
|
|
125
126
|
max,
|
|
126
127
|
step,
|
|
127
128
|
precision,
|
|
128
|
-
currency
|
|
129
|
+
currency,
|
|
130
|
+
currencyField,
|
|
131
|
+
locale,
|
|
129
132
|
defaultCountry,
|
|
130
133
|
otpLength,
|
|
131
134
|
mentions,
|
|
@@ -171,6 +174,29 @@ export function DataTableCell({
|
|
|
171
174
|
);
|
|
172
175
|
}
|
|
173
176
|
|
|
177
|
+
// A money cell is DISPLAY, in both modes. A Currency field is a decimal
|
|
178
|
+
// STRING (a float would round it) and a grid row is not where a ledger
|
|
179
|
+
// figure gets retyped — money is edited through the form surfaces (MPO-252).
|
|
180
|
+
//
|
|
181
|
+
// Formatting reads the ROW's own currency first, because a ledger genuinely
|
|
182
|
+
// mixes currencies row by row; the column-level `currency` constant is the
|
|
183
|
+
// fallback for rows carrying none. Nothing here converts between codes —
|
|
184
|
+
// two currencies are two figures. See utils/money.ts.
|
|
185
|
+
if (field === "money") {
|
|
186
|
+
const rowCurrency = currencyField
|
|
187
|
+
? (rowData as Record<string, unknown> | undefined)?.[currencyField]
|
|
188
|
+
: undefined;
|
|
189
|
+
const resolvedCurrency =
|
|
190
|
+
typeof rowCurrency === "string" && rowCurrency ? rowCurrency : currency;
|
|
191
|
+
return (
|
|
192
|
+
<CellWrapper justify={cellJustify}>
|
|
193
|
+
<Text numberOfLines={1} aria-label={cellName}>
|
|
194
|
+
{formatMoney(value, resolvedCurrency, locale)}
|
|
195
|
+
</Text>
|
|
196
|
+
</CellWrapper>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
174
200
|
// Read-only cells route through FieldDisplay (lightweight, view-only) rather
|
|
175
201
|
// than mounting the full Edit component with disabled={true}. This keeps
|
|
176
202
|
// large tables performant and gives heavy fields a purpose-built compact
|
|
@@ -227,7 +227,6 @@ function BasicPinningExample() {
|
|
|
227
227
|
onPress={() => pinning.togglePin(header.column.id, "left")}
|
|
228
228
|
disabled={!isPinned && position !== "left" && !pinning.canPin("left")}
|
|
229
229
|
circular
|
|
230
|
-
variant={position === "left" ? "outlined" : undefined}
|
|
231
230
|
aria-label={
|
|
232
231
|
isPinned && position === "left"
|
|
233
232
|
? `Unpin ${headerLabel} column`
|
|
@@ -240,7 +239,6 @@ function BasicPinningExample() {
|
|
|
240
239
|
onPress={() => pinning.togglePin(header.column.id, "right")}
|
|
241
240
|
disabled={!isPinned && position !== "right" && !pinning.canPin("right")}
|
|
242
241
|
circular
|
|
243
|
-
variant={position === "right" ? "outlined" : undefined}
|
|
244
242
|
aria-label={
|
|
245
243
|
isPinned && position === "right"
|
|
246
244
|
? `Unpin ${headerLabel} column`
|
|
@@ -483,7 +481,6 @@ export const MaxPinnedColumns: Story = {
|
|
|
483
481
|
size="$2"
|
|
484
482
|
onPress={() => pinning.togglePin(column.id, "left")}
|
|
485
483
|
disabled={!pinning.isPinned(column.id) && !pinning.canPin("left")}
|
|
486
|
-
variant={pinning.getPinPosition(column.id) === "left" ? "outlined" : undefined}
|
|
487
484
|
>
|
|
488
485
|
{pinning.getPinPosition(column.id) === "left" ? "Unpin" : "Pin Left"}
|
|
489
486
|
</Button>
|
|
@@ -491,7 +488,6 @@ export const MaxPinnedColumns: Story = {
|
|
|
491
488
|
size="$2"
|
|
492
489
|
onPress={() => pinning.togglePin(column.id, "right")}
|
|
493
490
|
disabled={!pinning.isPinned(column.id) && !pinning.canPin("right")}
|
|
494
|
-
variant={pinning.getPinPosition(column.id) === "right" ? "outlined" : undefined}
|
|
495
491
|
>
|
|
496
492
|
{pinning.getPinPosition(column.id) === "right" ? "Unpin" : "Pin Right"}
|
|
497
493
|
</Button>
|