@multiplatform.one/table 7.7.6 → 7.10.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/DataTableMobileCards.cjs +100 -33
- package/dist/cjs/components/DataTable/DataTableMobileCards.native.js +139 -38
- package/dist/cjs/components/DataTable/DataTableMobileCards.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/DataTable/index.native.js +59 -93
- package/dist/cjs/components/DataTable/index.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/DataTableMobileCards.mjs +99 -34
- package/dist/esm/components/DataTable/DataTableMobileCards.mjs.map +1 -1
- package/dist/esm/components/DataTable/DataTableMobileCards.native.js +138 -39
- package/dist/esm/components/DataTable/DataTableMobileCards.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/DataTable/index.native.js +60 -94
- package/dist/esm/components/DataTable/index.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/DataTableMobileCards.mjs +99 -34
- package/dist/jsx/components/DataTable/DataTableMobileCards.mjs.map +1 -1
- package/dist/jsx/components/DataTable/DataTableMobileCards.native.js +139 -38
- package/dist/jsx/components/DataTable/DataTableMobileCards.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/DataTable/index.native.js +59 -93
- package/dist/jsx/components/DataTable/index.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.spec.tsx +5 -3
- package/src/components/DataTable/DataTable.stories.tsx +186 -14
- package/src/components/DataTable/DataTableCardControls.tsx +11 -13
- package/src/components/DataTable/DataTableMobileCards.tsx +162 -39
- package/src/components/DataTable/DataTableRoot.tsx +40 -14
- package/src/components/DataTable/DataTableStateContext.ts +7 -0
- package/src/components/DataTable/cardLayout.spec.tsx +19 -8
- package/src/components/DataTable/columnWidthLayout.spec.tsx +195 -0
- package/src/components/DataTable/dataTableMobileCards.spec.tsx +50 -2
- package/src/components/DataTable/headerSorting.spec.tsx +25 -0
- package/src/components/DataTable/index.native.tsx +80 -124
- package/src/components/DataTable/moneyColumn.spec.tsx +151 -0
- package/src/components/DataTable/nativeCardParity.spec.tsx +95 -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/DataTableMobileCards.d.ts +42 -8
- package/types/components/DataTable/DataTableMobileCards.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/DataTable/index.native.d.ts +1 -1
- package/types/components/DataTable/index.native.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,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,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One card renderer, two platforms.
|
|
3
|
+
*
|
|
4
|
+
* DataTableRoot.tsx (web) and index.native.tsx (native) are separate roots
|
|
5
|
+
* because native virtualizes through FlashList. The CARD LIST is not part
|
|
6
|
+
* of that split — a card has no platform-specific anatomy — so both roots
|
|
7
|
+
* render the same DataTableMobileCards rather than each drawing a card.
|
|
8
|
+
*
|
|
9
|
+
* MPO-285 shared the field RULE (classifyCardField, getColumnLabel) while
|
|
10
|
+
* leaving two copies of the JSX, and the two had already drifted: the
|
|
11
|
+
* native copy hardcoded its surface, and shipped without zebra striping,
|
|
12
|
+
* the selection checkbox, the empty states, and the TableCellContext that
|
|
13
|
+
* makes a field render chromeless. Sharing the rule alone does not stop
|
|
14
|
+
* that, because a new slot still has to be added twice.
|
|
15
|
+
*
|
|
16
|
+
* These assertions read source rather than render: the native root pulls in
|
|
17
|
+
* FlashList and the suite runs under happy-dom, so index.native.tsx is
|
|
18
|
+
* never the module a spec resolves. What the card DOES on native is covered
|
|
19
|
+
* by dataTableMobileCards.spec.tsx, which now describes both platforms.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
23
|
+
import { resolve } from "node:path";
|
|
24
|
+
import { describe, expect, it } from "vitest";
|
|
25
|
+
|
|
26
|
+
const DIR = resolve(__dirname);
|
|
27
|
+
|
|
28
|
+
function read(file: string): string {
|
|
29
|
+
return readFileSync(resolve(DIR, file), "utf-8");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A `<Component ... />` element at a call site, as text. */
|
|
33
|
+
function callSite(source: string, component: string): string {
|
|
34
|
+
const start = source.indexOf(`<${component}`);
|
|
35
|
+
if (start === -1) return "";
|
|
36
|
+
const end = source.indexOf("/>", start);
|
|
37
|
+
return end === -1 ? "" : source.slice(start, end);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Prop names that call site passes. */
|
|
41
|
+
function propNames(source: string, component: string): Set<string> {
|
|
42
|
+
const names = new Set<string>();
|
|
43
|
+
for (const match of callSite(source, component).matchAll(/(?:^|\s)([A-Za-z][A-Za-z0-9]*)=\{/g)) {
|
|
44
|
+
names.add(match[1]!);
|
|
45
|
+
}
|
|
46
|
+
return names;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const cardCallSite = (source: string) => callSite(source, "DataTableMobileCards");
|
|
50
|
+
|
|
51
|
+
describe("DataTable card renderer parity", () => {
|
|
52
|
+
it("the native root renders DataTableMobileCards and carries no card of its own", () => {
|
|
53
|
+
const native = read("index.native.tsx");
|
|
54
|
+
expect(native).toMatch(
|
|
55
|
+
/import \{[^}]*DataTableMobileCards[^}]*\} from "\.\/DataTableMobileCards"/,
|
|
56
|
+
);
|
|
57
|
+
expect(cardCallSite(native)).not.toBe("");
|
|
58
|
+
expect(native).not.toMatch(/renderMobileCard/);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("the native root reaches the card field rule through the component, not directly", () => {
|
|
62
|
+
// MPO-285 left index.native.tsx importing classifyCardField/getColumnLabel
|
|
63
|
+
// to feed its own JSX. With the JSX shared there is nothing left to feed:
|
|
64
|
+
// an import here again would mean a second card had grown back.
|
|
65
|
+
const native = read("index.native.tsx");
|
|
66
|
+
expect(native).not.toMatch(/\bclassifyCardField\b/);
|
|
67
|
+
expect(native).not.toMatch(/\bgetColumnLabel\b/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("exactly one file in the component draws a card", () => {
|
|
71
|
+
const drawers = readdirSync(DIR)
|
|
72
|
+
.filter(
|
|
73
|
+
(file) => file.endsWith(".tsx") && !file.includes(".spec.") && !file.includes(".stories."),
|
|
74
|
+
)
|
|
75
|
+
.filter((file) => read(file).includes('testID="data-table-card"'));
|
|
76
|
+
expect(drawers).toEqual(["DataTableMobileCards.tsx"]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Both roots hand their row views the same contract. A prop web passes and
|
|
80
|
+
// native does not is how the card lost selection and the empty states in
|
|
81
|
+
// the first place, so it is the drift worth failing a build over.
|
|
82
|
+
it.each(["DataTableMobileCards", "DataTableDocumentList"])(
|
|
83
|
+
"native passes every %s prop web passes",
|
|
84
|
+
(component) => {
|
|
85
|
+
const web = propNames(read("DataTableRoot.tsx"), component);
|
|
86
|
+
const native = propNames(read("index.native.tsx"), component);
|
|
87
|
+
expect(web.size).toBeGreaterThan(0);
|
|
88
|
+
expect([...web].filter((prop) => !native.has(prop)).sort()).toEqual([]);
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
it("native hands the card list its press props, so a card is pressable there too", () => {
|
|
93
|
+
expect(cardCallSite(read("index.native.tsx"))).toMatch(/getRowPressProps/);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -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>
|
|
@@ -326,7 +326,6 @@ export function DataTableDateFilter({
|
|
|
326
326
|
<Popover.Trigger asChild>
|
|
327
327
|
<Button
|
|
328
328
|
size={knobProps.sizeToken}
|
|
329
|
-
variant="outlined"
|
|
330
329
|
disabled={disabled}
|
|
331
330
|
icon={<CalendarIcon size={iconPx} />}
|
|
332
331
|
justifyContent="flex-start"
|
|
@@ -370,7 +369,6 @@ export function DataTableDateFilter({
|
|
|
370
369
|
{value && (
|
|
371
370
|
<Button
|
|
372
371
|
size={knobProps.sizeToken}
|
|
373
|
-
variant="outlined"
|
|
374
372
|
onPress={handleClear}
|
|
375
373
|
icon={<XIcon size={iconPx} />}
|
|
376
374
|
>
|
|
@@ -391,7 +389,6 @@ export function DataTableDateFilter({
|
|
|
391
389
|
<Button
|
|
392
390
|
key={preset.id}
|
|
393
391
|
size={knobProps.sizeToken}
|
|
394
|
-
variant="outlined"
|
|
395
392
|
justifyContent="flex-start"
|
|
396
393
|
onPress={() => handlePresetSelect(preset)}
|
|
397
394
|
{...(activePresetId === preset.id
|
|
@@ -418,7 +415,6 @@ export function DataTableDateFilter({
|
|
|
418
415
|
<Button
|
|
419
416
|
flex={1}
|
|
420
417
|
size={knobProps.sizeToken}
|
|
421
|
-
variant="outlined"
|
|
422
418
|
onPress={() => handleCustomDateSelect(new Date().toISOString(), "is")}
|
|
423
419
|
>
|
|
424
420
|
{t("Today")}
|
|
@@ -426,7 +422,6 @@ export function DataTableDateFilter({
|
|
|
426
422
|
<Button
|
|
427
423
|
flex={1}
|
|
428
424
|
size={knobProps.sizeToken}
|
|
429
|
-
variant="outlined"
|
|
430
425
|
onPress={() =>
|
|
431
426
|
handleCustomDateSelect(
|
|
432
427
|
new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),
|
|
@@ -447,7 +442,6 @@ export function DataTableDateFilter({
|
|
|
447
442
|
<Button
|
|
448
443
|
flex={1}
|
|
449
444
|
size={knobProps.sizeToken}
|
|
450
|
-
variant="outlined"
|
|
451
445
|
onPress={() =>
|
|
452
446
|
handleDateRangeSelect(
|
|
453
447
|
new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
@@ -460,7 +454,6 @@ export function DataTableDateFilter({
|
|
|
460
454
|
<Button
|
|
461
455
|
flex={1}
|
|
462
456
|
size={knobProps.sizeToken}
|
|
463
|
-
variant="outlined"
|
|
464
457
|
onPress={() =>
|
|
465
458
|
handleDateRangeSelect(
|
|
466
459
|
new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(),
|
|
@@ -348,7 +348,7 @@ export function DataTableFilterButton() {
|
|
|
348
348
|
|
|
349
349
|
{editingExisting && (
|
|
350
350
|
<XStack justifyContent="flex-end">
|
|
351
|
-
<Button size="$2"
|
|
351
|
+
<Button size="$2" onPress={handleRemoveCurrent}>
|
|
352
352
|
{t("Remove filter")}
|
|
353
353
|
</Button>
|
|
354
354
|
</XStack>
|
|
@@ -18,6 +18,12 @@ describe("FilterChip", () => {
|
|
|
18
18
|
renderWithProviders(<FilterChip label="Email: contains corp" onRemove={vi.fn()} />);
|
|
19
19
|
expect(screen.getByText("Email: contains corp")).toBeTruthy();
|
|
20
20
|
expect(screen.getByRole("button", { name: "Remove" })).toBeTruthy();
|
|
21
|
+
const fill = screen
|
|
22
|
+
.getByRole("button", { name: "Remove" })
|
|
23
|
+
.querySelector("svg")
|
|
24
|
+
?.getAttribute("fill");
|
|
25
|
+
expect(fill).toBeTruthy();
|
|
26
|
+
expect(fill?.startsWith("$")).toBe(false);
|
|
21
27
|
});
|
|
22
28
|
|
|
23
29
|
it("with onPress the pill is an edit button; both focusables are tab stops (LC-71)", () => {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
pressTargetStyle,
|
|
17
17
|
useResolvedKnobs,
|
|
18
18
|
} from "@multiplatform.one/theme";
|
|
19
|
-
import { View, styled } from "tamagui";
|
|
19
|
+
import { View, styled, useTheme } from "tamagui";
|
|
20
20
|
import { t } from "../../shared/t";
|
|
21
21
|
|
|
22
22
|
export interface FilterChipProps {
|
|
@@ -49,6 +49,7 @@ const DismissRing = styled(View, {
|
|
|
49
49
|
|
|
50
50
|
export function FilterChip({ label, onRemove, onPress }: FilterChipProps) {
|
|
51
51
|
const { knobProps } = useResolvedKnobs();
|
|
52
|
+
const theme = useTheme();
|
|
52
53
|
if (typeof document !== "undefined") ensureCompositeFocusRing();
|
|
53
54
|
|
|
54
55
|
const resolvedSize = (knobProps.sizeToken as ChipSize) || "$3";
|
|
@@ -114,7 +115,7 @@ export function FilterChip({ label, onRemove, onPress }: FilterChipProps) {
|
|
|
114
115
|
}
|
|
115
116
|
>
|
|
116
117
|
<DismissRing className="mp-chip-dismiss-ring" borderRadius={knobProps.pointy ? 0 : 1000}>
|
|
117
|
-
<XIcon size={iconSize} color=
|
|
118
|
+
<XIcon size={iconSize} color={theme.color12?.val ?? "currentColor"} />
|
|
118
119
|
</DismissRing>
|
|
119
120
|
</DismissButton>
|
|
120
121
|
</ChipFrame>
|
|
@@ -255,7 +255,7 @@ function FilterBuilder({
|
|
|
255
255
|
) : null}
|
|
256
256
|
|
|
257
257
|
<XStack gap="$2" justifyContent="flex-end">
|
|
258
|
-
<Button size="$3" compact
|
|
258
|
+
<Button size="$3" compact onPress={onCancel}>
|
|
259
259
|
{t("Cancel")}
|
|
260
260
|
</Button>
|
|
261
261
|
<Button
|