@multiplatform.one/table 7.28.1 → 7.29.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/DataTable/CellContent.cjs +77 -0
- package/dist/cjs/components/DataTable/CellContent.native.js +88 -0
- package/dist/cjs/components/DataTable/CellContent.native.js.map +1 -0
- package/dist/cjs/components/DataTable/DataTableInfiniteTail.cjs +90 -0
- package/dist/cjs/components/DataTable/DataTableInfiniteTail.native.js +99 -0
- package/dist/cjs/components/DataTable/DataTableInfiniteTail.native.js.map +1 -0
- package/dist/cjs/components/DataTable/DataTableRoot.cjs +17 -47
- package/dist/cjs/components/DataTable/DataTableRoot.native.js +17 -56
- package/dist/cjs/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/cjs/components/DataTable/NativeInfiniteSentinel.cjs +62 -0
- package/dist/cjs/components/DataTable/NativeInfiniteSentinel.native.js +67 -0
- package/dist/cjs/components/DataTable/NativeInfiniteSentinel.native.js.map +1 -0
- package/dist/cjs/components/DataTable/index.native.js +75 -15
- package/dist/cjs/components/DataTable/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableBulkActions/index.cjs +70 -51
- package/dist/cjs/components/DataTableBulkActions/index.native.js +84 -63
- package/dist/cjs/components/DataTableBulkActions/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableCell/index.cjs +3 -1
- package/dist/cjs/components/DataTableCell/index.native.js +3 -1
- package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableDateFilter/index.cjs +2 -1
- package/dist/cjs/components/DataTableDateFilter/index.native.js +2 -1
- package/dist/cjs/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableExport/index.cjs +2 -1
- package/dist/cjs/components/DataTableExport/index.native.js +2 -1
- package/dist/cjs/components/DataTableExport/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableGrouping/index.cjs +2 -2
- package/dist/cjs/components/DataTableGrouping/index.native.js +2 -2
- package/dist/cjs/components/DataTableViewOptions/index.cjs +1 -1
- package/dist/cjs/components/DataTableViewOptions/index.native.js +1 -1
- package/dist/cjs/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/cjs/components/KnobDialog.cjs +2 -1
- package/dist/cjs/components/KnobDialog.native.js +4 -2
- package/dist/cjs/components/KnobDialog.native.js.map +1 -1
- package/dist/cjs/components/TableInput/RowDetailEditor.cjs +1 -1
- package/dist/cjs/components/TableInput/index.native.js +1 -0
- package/dist/cjs/components/TableInput/index.native.js.map +1 -1
- package/dist/esm/components/DataTable/CellContent.mjs +52 -0
- package/dist/esm/components/DataTable/CellContent.mjs.map +1 -0
- package/dist/esm/components/DataTable/CellContent.native.js +60 -0
- package/dist/esm/components/DataTable/CellContent.native.js.map +1 -0
- package/dist/esm/components/DataTable/DataTableInfiniteTail.mjs +65 -0
- package/dist/esm/components/DataTable/DataTableInfiniteTail.mjs.map +1 -0
- package/dist/esm/components/DataTable/DataTableInfiniteTail.native.js +71 -0
- package/dist/esm/components/DataTable/DataTableInfiniteTail.native.js.map +1 -0
- package/dist/esm/components/DataTable/DataTableRoot.mjs +18 -48
- package/dist/esm/components/DataTable/DataTableRoot.mjs.map +1 -1
- package/dist/esm/components/DataTable/DataTableRoot.native.js +18 -57
- package/dist/esm/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/esm/components/DataTable/NativeInfiniteSentinel.mjs +36 -0
- package/dist/esm/components/DataTable/NativeInfiniteSentinel.mjs.map +1 -0
- package/dist/esm/components/DataTable/NativeInfiniteSentinel.native.js +38 -0
- package/dist/esm/components/DataTable/NativeInfiniteSentinel.native.js.map +1 -0
- package/dist/esm/components/DataTable/index.native.js +77 -17
- package/dist/esm/components/DataTable/index.native.js.map +1 -1
- package/dist/esm/components/DataTableBulkActions/index.mjs +71 -52
- package/dist/esm/components/DataTableBulkActions/index.mjs.map +1 -1
- package/dist/esm/components/DataTableBulkActions/index.native.js +85 -64
- package/dist/esm/components/DataTableBulkActions/index.native.js.map +1 -1
- package/dist/esm/components/DataTableCell/index.mjs +3 -1
- package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
- package/dist/esm/components/DataTableCell/index.native.js +3 -1
- package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
- package/dist/esm/components/DataTableDateFilter/index.mjs +2 -1
- package/dist/esm/components/DataTableDateFilter/index.mjs.map +1 -1
- package/dist/esm/components/DataTableDateFilter/index.native.js +2 -1
- package/dist/esm/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/esm/components/DataTableExport/index.mjs +2 -1
- package/dist/esm/components/DataTableExport/index.mjs.map +1 -1
- package/dist/esm/components/DataTableExport/index.native.js +2 -1
- package/dist/esm/components/DataTableExport/index.native.js.map +1 -1
- package/dist/esm/components/DataTableGrouping/index.mjs +2 -2
- package/dist/esm/components/DataTableGrouping/index.native.js +2 -2
- package/dist/esm/components/DataTableViewOptions/index.mjs +2 -2
- package/dist/esm/components/DataTableViewOptions/index.mjs.map +1 -1
- package/dist/esm/components/DataTableViewOptions/index.native.js +2 -2
- package/dist/esm/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/esm/components/KnobDialog.mjs +2 -1
- package/dist/esm/components/KnobDialog.mjs.map +1 -1
- package/dist/esm/components/KnobDialog.native.js +4 -2
- package/dist/esm/components/KnobDialog.native.js.map +1 -1
- package/dist/esm/components/TableInput/RowDetailEditor.mjs +2 -2
- package/dist/esm/components/TableInput/RowDetailEditor.mjs.map +1 -1
- package/dist/esm/components/TableInput/index.native.js +1 -0
- package/dist/esm/components/TableInput/index.native.js.map +1 -1
- package/dist/jsx/components/DataTable/CellContent.mjs +52 -0
- package/dist/jsx/components/DataTable/CellContent.mjs.map +1 -0
- package/dist/jsx/components/DataTable/CellContent.native.js +88 -0
- package/dist/jsx/components/DataTable/CellContent.native.js.map +1 -0
- package/dist/jsx/components/DataTable/DataTableInfiniteTail.mjs +65 -0
- package/dist/jsx/components/DataTable/DataTableInfiniteTail.mjs.map +1 -0
- package/dist/jsx/components/DataTable/DataTableInfiniteTail.native.js +99 -0
- package/dist/jsx/components/DataTable/DataTableInfiniteTail.native.js.map +1 -0
- package/dist/jsx/components/DataTable/DataTableRoot.mjs +18 -48
- package/dist/jsx/components/DataTable/DataTableRoot.mjs.map +1 -1
- package/dist/jsx/components/DataTable/DataTableRoot.native.js +17 -56
- package/dist/jsx/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/jsx/components/DataTable/NativeInfiniteSentinel.mjs +36 -0
- package/dist/jsx/components/DataTable/NativeInfiniteSentinel.mjs.map +1 -0
- package/dist/jsx/components/DataTable/NativeInfiniteSentinel.native.js +67 -0
- package/dist/jsx/components/DataTable/NativeInfiniteSentinel.native.js.map +1 -0
- package/dist/jsx/components/DataTable/index.native.js +75 -15
- package/dist/jsx/components/DataTable/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableBulkActions/index.mjs +71 -52
- package/dist/jsx/components/DataTableBulkActions/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableBulkActions/index.native.js +84 -63
- package/dist/jsx/components/DataTableBulkActions/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableCell/index.mjs +3 -1
- package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableCell/index.native.js +3 -1
- package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableDateFilter/index.mjs +2 -1
- package/dist/jsx/components/DataTableDateFilter/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableDateFilter/index.native.js +2 -1
- package/dist/jsx/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableExport/index.mjs +2 -1
- package/dist/jsx/components/DataTableExport/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableExport/index.native.js +2 -1
- package/dist/jsx/components/DataTableExport/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableGrouping/index.mjs +2 -2
- package/dist/jsx/components/DataTableGrouping/index.native.js +2 -2
- package/dist/jsx/components/DataTableViewOptions/index.mjs +2 -2
- package/dist/jsx/components/DataTableViewOptions/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableViewOptions/index.native.js +1 -1
- package/dist/jsx/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/jsx/components/KnobDialog.mjs +2 -1
- package/dist/jsx/components/KnobDialog.mjs.map +1 -1
- package/dist/jsx/components/KnobDialog.native.js +4 -2
- package/dist/jsx/components/KnobDialog.native.js.map +1 -1
- package/dist/jsx/components/TableInput/RowDetailEditor.mjs +2 -2
- package/dist/jsx/components/TableInput/RowDetailEditor.mjs.map +1 -1
- package/dist/jsx/components/TableInput/index.native.js +1 -0
- package/dist/jsx/components/TableInput/index.native.js.map +1 -1
- package/package.json +12 -12
- package/src/components/DataTable/CellContent.tsx +73 -0
- package/src/components/DataTable/DataTableInfiniteTail.tsx +94 -0
- package/src/components/DataTable/DataTableRoot.tsx +37 -81
- package/src/components/DataTable/NativeInfiniteSentinel.tsx +45 -0
- package/src/components/DataTable/cellWeight.native.spec.tsx +82 -0
- package/src/components/DataTable/cellWeight.spec.tsx +147 -0
- package/src/components/DataTable/index.native.tsx +108 -19
- package/src/components/DataTable/nativeRoot.spec.tsx +180 -0
- package/src/components/DataTableBulkActions/index.tsx +100 -71
- package/src/components/DataTableCell/index.tsx +3 -2
- package/src/components/DataTableDateFilter/index.tsx +2 -10
- package/src/components/DataTableExport/index.tsx +2 -1
- package/src/components/DataTableFilters/DataTableFilters.stories.tsx +7 -19
- package/src/components/DataTableGrouping/index.tsx +2 -2
- package/src/components/DataTablePagination/DataTablePagination.stories.tsx +12 -46
- package/src/components/DataTableViewOptions/index.tsx +2 -2
- package/src/components/KnobDialog.tsx +2 -0
- package/src/components/TableInput/RowDetailEditor.tsx +2 -2
- package/src/components/TableInput/index.native.tsx +1 -0
- package/types/components/DataTable/CellContent.d.ts +19 -0
- package/types/components/DataTable/CellContent.d.ts.map +1 -0
- package/types/components/DataTable/DataTableInfiniteTail.d.ts +22 -0
- package/types/components/DataTable/DataTableInfiniteTail.d.ts.map +1 -0
- package/types/components/DataTable/DataTableRoot.d.ts.map +1 -1
- package/types/components/DataTable/NativeInfiniteSentinel.d.ts +17 -0
- package/types/components/DataTable/NativeInfiniteSentinel.d.ts.map +1 -0
- 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/DataTableExport/index.d.ts.map +1 -1
- package/types/components/KnobDialog.d.ts.map +1 -1
- package/types/components/TableInput/index.native.d.ts.map +1 -1
|
@@ -14,19 +14,21 @@ import {
|
|
|
14
14
|
TrashIcon,
|
|
15
15
|
} from "@phosphor-icons/react";
|
|
16
16
|
import { FlashList, type FlashListRef } from "@shopify/flash-list";
|
|
17
|
-
import { AsyncBoundary, EmptyState, useDirection } from "@multiplatform.one/components";
|
|
17
|
+
import { AsyncBoundary, EmptyState, useDirection, ScrollView } from "@multiplatform.one/components";
|
|
18
18
|
import { Button, Field, useFormField } from "@multiplatform.one/forms";
|
|
19
19
|
import { flexRender } from "@tanstack/react-table";
|
|
20
20
|
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
21
21
|
import React, { useRef, useMemo, useCallback } from "react";
|
|
22
|
-
import {
|
|
22
|
+
import type { FontSizeTokens } from "tamagui";
|
|
23
|
+
import { Text, View, useMedia, withStaticProperties } from "tamagui";
|
|
23
24
|
import { useTranslation } from "react-i18next";
|
|
24
25
|
import { useDataTable } from "../../hooks/useDataTable";
|
|
25
|
-
import type { DataTableColumn, DataTableField, SelectAllScope } from "../../types";
|
|
26
|
-
import { DEFAULT_PAGE_SIZE_OPTIONS } from "../../utils/paginationDefaults";
|
|
26
|
+
import type { BulkAction, DataTableColumn, DataTableField, SelectAllScope } from "../../types";
|
|
27
|
+
import { DEFAULT_PAGE_SIZE_OPTIONS, INFINITE_TRIGGER_ROWS } from "../../utils/paginationDefaults";
|
|
27
28
|
import { withInterp } from "../../utils/withInterp";
|
|
28
29
|
import { DataTableColumnPinning } from "../DataTableColumnPinning";
|
|
29
30
|
import { DataTablePagination } from "../DataTablePagination";
|
|
31
|
+
import { CellContent } from "./CellContent";
|
|
30
32
|
import { DataTableToolbar } from "../DataTableToolbar";
|
|
31
33
|
import { DataTableFilters } from "../DataTableFilters";
|
|
32
34
|
import { DataTableBulkActions } from "../DataTableBulkActions";
|
|
@@ -38,8 +40,10 @@ import { DataTableCell } from "../DataTableCell";
|
|
|
38
40
|
import { Table } from "@multiplatform.one/table-primitives";
|
|
39
41
|
import { DataTableContext } from "./DataTableContext";
|
|
40
42
|
import { DataTableDocumentList } from "./DataTableDocumentList";
|
|
43
|
+
import { DataTableInfiniteTail } from "./DataTableInfiniteTail";
|
|
41
44
|
import { DataTableMobileCards } from "./DataTableMobileCards";
|
|
42
45
|
import { dataTableChrome, resolveDataTableIntent, rowIntentProps } from "./dataTableChrome";
|
|
46
|
+
import { NativeInfiniteSentinel } from "./NativeInfiniteSentinel";
|
|
43
47
|
import { tableRowStateProps } from "./rowStateChrome";
|
|
44
48
|
import { DataTableStateContext } from "./DataTableStateContext";
|
|
45
49
|
import type { DataTableStateContextValue } from "./DataTableStateContext";
|
|
@@ -110,6 +114,7 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
110
114
|
data,
|
|
111
115
|
columns,
|
|
112
116
|
initialState,
|
|
117
|
+
controlledState,
|
|
113
118
|
onStateChange,
|
|
114
119
|
onRetry,
|
|
115
120
|
isLoading: isLoadingProp,
|
|
@@ -121,24 +126,32 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
121
126
|
manualPagination = false,
|
|
122
127
|
manualSorting = false,
|
|
123
128
|
enableFiltering = true,
|
|
129
|
+
filterableFields,
|
|
124
130
|
enableSorting = true,
|
|
125
131
|
enablePagination = true,
|
|
126
132
|
pageSizeOptions,
|
|
133
|
+
paginationMode = "pages",
|
|
134
|
+
loadPage,
|
|
135
|
+
rowAccumulator,
|
|
127
136
|
enableColumnVisibility = true,
|
|
128
137
|
enableColumnOrdering = false,
|
|
129
138
|
enableColumnResizing = false,
|
|
130
139
|
enableGlobalSearch = true,
|
|
131
140
|
enableViews = false,
|
|
141
|
+
columnLayout = "equal",
|
|
132
142
|
layout = "auto",
|
|
133
143
|
enableVirtualization = false,
|
|
134
144
|
virtualizationHeight,
|
|
135
145
|
virtualizationOverscan = 5,
|
|
136
146
|
enableRowExpansion = false,
|
|
137
147
|
renderSubComponent,
|
|
148
|
+
getSubRows,
|
|
138
149
|
enableRowSelection = false,
|
|
139
150
|
selectAllScope: selectAllScopeProp,
|
|
151
|
+
bulkActions,
|
|
140
152
|
enableColumnPinning = false,
|
|
141
153
|
maxPinnedColumns = 3,
|
|
154
|
+
enableGrouping = false,
|
|
142
155
|
enableKeyboardNavigation: _enableKeyboardNavigation = false,
|
|
143
156
|
onRowPress,
|
|
144
157
|
rowLabel,
|
|
@@ -184,6 +197,7 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
184
197
|
compact,
|
|
185
198
|
});
|
|
186
199
|
const chrome = dataTableChrome(String(knobProps.sizeToken));
|
|
200
|
+
const bulkBarPlacement = knobProps.bulkBarPlacement === "bottom" ? "bottom" : "top";
|
|
187
201
|
// House selectAllScope knob (DG-TBL-03): prop overrides knob; default page.
|
|
188
202
|
const selectAllScope: SelectAllScope =
|
|
189
203
|
selectAllScopeProp ?? (knobProps.selectAllScope === "filtered" ? "filtered" : "page");
|
|
@@ -202,6 +216,7 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
202
216
|
data,
|
|
203
217
|
columns,
|
|
204
218
|
initialState,
|
|
219
|
+
controlledState,
|
|
205
220
|
onStateChange,
|
|
206
221
|
syncWithUrl,
|
|
207
222
|
urlDebounceMs,
|
|
@@ -210,21 +225,28 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
210
225
|
manualPagination,
|
|
211
226
|
manualSorting,
|
|
212
227
|
enableFiltering,
|
|
228
|
+
filterableFields,
|
|
213
229
|
enableSorting,
|
|
214
230
|
enablePagination,
|
|
231
|
+
paginationMode,
|
|
232
|
+
loadPage,
|
|
233
|
+
rowAccumulator,
|
|
215
234
|
enableColumnVisibility,
|
|
216
235
|
enableColumnOrdering,
|
|
217
236
|
enableColumnResizing,
|
|
237
|
+
columnLayout,
|
|
218
238
|
enableGlobalSearch,
|
|
219
239
|
enableViews,
|
|
220
240
|
enableVirtualization,
|
|
221
241
|
virtualizationHeight,
|
|
222
242
|
virtualizationOverscan,
|
|
223
243
|
enableRowExpansion,
|
|
244
|
+
getSubRows,
|
|
224
245
|
renderSubComponent,
|
|
225
246
|
enableRowSelection,
|
|
226
247
|
enableColumnPinning,
|
|
227
248
|
maxPinnedColumns,
|
|
249
|
+
enableGrouping,
|
|
228
250
|
enableKeyboardNavigation: false,
|
|
229
251
|
rowCount,
|
|
230
252
|
pageCount,
|
|
@@ -232,6 +254,8 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
232
254
|
isFetching: isFetchingProp,
|
|
233
255
|
});
|
|
234
256
|
|
|
257
|
+
const resolvedBulkActions = (bulkActions ?? []) as BulkAction<TData>[];
|
|
258
|
+
|
|
235
259
|
const selectAllChecked =
|
|
236
260
|
enableRowSelection &&
|
|
237
261
|
(selectAllScope === "filtered"
|
|
@@ -257,12 +281,22 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
257
281
|
const cellHeight = resolvedSize === "sm" ? "$6" : resolvedSize === "lg" ? "$10" : "$8";
|
|
258
282
|
|
|
259
283
|
const headerGroups = table.getHeaderGroups();
|
|
260
|
-
const
|
|
284
|
+
const rowModelRows = table.getRowModel().rows;
|
|
285
|
+
// Infinite mode reveals accumulated pages only; loader mode's row model is
|
|
286
|
+
// already just the accumulated rows, so the slice is a no-op there.
|
|
287
|
+
const infinite = computed.infinite;
|
|
288
|
+
const tableRows = infinite ? rowModelRows.slice(0, infinite.visibleRowCount) : rowModelRows;
|
|
261
289
|
const heldRowsRef = useRef(tableRows);
|
|
262
290
|
if (tableRows.length > 0) {
|
|
263
291
|
heldRowsRef.current = tableRows;
|
|
264
292
|
}
|
|
265
|
-
const queryInFlight = Boolean(
|
|
293
|
+
const queryInFlight = Boolean(
|
|
294
|
+
state.isFetching ||
|
|
295
|
+
state.isPlaceholderData ||
|
|
296
|
+
infinite?.isAppending ||
|
|
297
|
+
state.isLoading ||
|
|
298
|
+
infinite?.isInitialLoading,
|
|
299
|
+
);
|
|
266
300
|
const displayRows =
|
|
267
301
|
tableRows.length > 0
|
|
268
302
|
? tableRows
|
|
@@ -307,6 +341,8 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
307
341
|
|
|
308
342
|
const isVirtualized =
|
|
309
343
|
enableVirtualization && virtualizationHeight != null && virtualizationHeight > 0;
|
|
344
|
+
// Only the grid virtualizes on native; list and cards render in flow.
|
|
345
|
+
const isVirtualGrid = isVirtualized && !showList && !showCards;
|
|
310
346
|
|
|
311
347
|
// DataTableColumnPinning is a hook-style helper (it calls useState/useMemo/
|
|
312
348
|
// useCallback internally), so it must run unconditionally at the top level —
|
|
@@ -369,16 +405,11 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
369
405
|
const value = cell.getValue();
|
|
370
406
|
|
|
371
407
|
if (columnDef.cell && !columnDef.field) {
|
|
372
|
-
// TanStack injects a default cell renderer that returns the raw value,
|
|
373
|
-
// and story/consumer columns commonly do `cell: (info) => info.getValue()`;
|
|
374
|
-
// raw strings/numbers outside <Text> are dropped on native. flexRender
|
|
375
|
-
// hides function-cell primitives inside an element, so NativeCellContent
|
|
376
|
-
// calls the renderer itself and wraps primitive output.
|
|
377
408
|
return (
|
|
378
|
-
<
|
|
409
|
+
<CellContent
|
|
379
410
|
cellDef={columnDef.cell}
|
|
380
411
|
ctx={cell.getContext()}
|
|
381
|
-
fontSize={knobProps.
|
|
412
|
+
fontSize={knobProps.label.fontSize as FontSizeTokens}
|
|
382
413
|
/>
|
|
383
414
|
);
|
|
384
415
|
}
|
|
@@ -398,7 +429,7 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
398
429
|
/>
|
|
399
430
|
);
|
|
400
431
|
},
|
|
401
|
-
[handleCellChange, readOnly, disabled, knobProps.
|
|
432
|
+
[handleCellChange, readOnly, disabled, knobProps.label.fontSize],
|
|
402
433
|
);
|
|
403
434
|
|
|
404
435
|
const renderRowContent = useCallback(
|
|
@@ -647,8 +678,8 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
647
678
|
};
|
|
648
679
|
|
|
649
680
|
const stateContextValue = useMemo<DataTableStateContextValue<TData>>(
|
|
650
|
-
() => ({ table, state, actions, computed }),
|
|
651
|
-
[table, state, actions, computed],
|
|
681
|
+
() => ({ table, state, actions, computed, selectAllScope }),
|
|
682
|
+
[table, state, actions, computed, selectAllScope],
|
|
652
683
|
);
|
|
653
684
|
|
|
654
685
|
const renderTableInner = useCallback(
|
|
@@ -676,7 +707,12 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
676
707
|
/>
|
|
677
708
|
);
|
|
678
709
|
}
|
|
679
|
-
if (
|
|
710
|
+
if (
|
|
711
|
+
(state.isLoading || infinite?.isInitialLoading) &&
|
|
712
|
+
displayRows.length === 0 &&
|
|
713
|
+
!state.isFetching &&
|
|
714
|
+
!state.isPlaceholderData
|
|
715
|
+
) {
|
|
680
716
|
return <AsyncBoundary loading layout="table" aria-label={ariaLabel || "Data table"} />;
|
|
681
717
|
}
|
|
682
718
|
|
|
@@ -711,6 +747,16 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
711
747
|
{renderToolbarComponent()}
|
|
712
748
|
</View>
|
|
713
749
|
)}
|
|
750
|
+
{enableRowSelection && bulkBarPlacement === "top" ? (
|
|
751
|
+
<View width="100%" {...(disabled ? disabledState.chromeKnobProps : {})}>
|
|
752
|
+
<DataTableBulkActions
|
|
753
|
+
actions={resolvedBulkActions}
|
|
754
|
+
maxVisibleActions={5}
|
|
755
|
+
showSelectionCount
|
|
756
|
+
placement="top"
|
|
757
|
+
/>
|
|
758
|
+
</View>
|
|
759
|
+
) : null}
|
|
714
760
|
{children}
|
|
715
761
|
{renderFilters?.({
|
|
716
762
|
filters: state.filters || [],
|
|
@@ -759,7 +805,7 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
759
805
|
queryInFlight={queryInFlight}
|
|
760
806
|
{...(rowPressActive ? { getRowPressProps } : {})}
|
|
761
807
|
/>
|
|
762
|
-
{enablePagination && (
|
|
808
|
+
{enablePagination && !infinite && (
|
|
763
809
|
<View width="100%" {...(disabled ? disabledState.chromeKnobProps : {})}>
|
|
764
810
|
{renderPaginationComponent()}
|
|
765
811
|
</View>
|
|
@@ -932,11 +978,19 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
932
978
|
) : isVirtualized ? (
|
|
933
979
|
<View style={{ height: virtualizationHeight }}>
|
|
934
980
|
<FlashList
|
|
981
|
+
keyboardShouldPersistTaps="handled"
|
|
935
982
|
ref={flashListRef}
|
|
936
983
|
data={displayRows}
|
|
937
984
|
drawDistance={rowHeightPx * virtualizationOverscan}
|
|
938
985
|
keyExtractor={(row) => row.id}
|
|
939
986
|
renderItem={({ item, index }) => renderRow(item, index)}
|
|
987
|
+
{...(infinite && !infinite.exhausted
|
|
988
|
+
? {
|
|
989
|
+
onEndReached: infinite.loadMore,
|
|
990
|
+
onEndReachedThreshold:
|
|
991
|
+
(INFINITE_TRIGGER_ROWS * rowHeightPx) / virtualizationHeight!,
|
|
992
|
+
}
|
|
993
|
+
: {})}
|
|
940
994
|
/>
|
|
941
995
|
</View>
|
|
942
996
|
) : (
|
|
@@ -993,6 +1047,36 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
993
1047
|
</ScrollView>
|
|
994
1048
|
)}
|
|
995
1049
|
|
|
1050
|
+
{infinite ? (
|
|
1051
|
+
<DataTableInfiniteTail
|
|
1052
|
+
infinite={infinite}
|
|
1053
|
+
visibleCount={tableRows.length}
|
|
1054
|
+
showAppendSkeleton={!isVirtualGrid}
|
|
1055
|
+
rowHeightPx={rowHeightPx}
|
|
1056
|
+
fontSize={knobProps.sizeToken}
|
|
1057
|
+
gapProps={knobProps.gap}
|
|
1058
|
+
sentinel={
|
|
1059
|
+
!isVirtualGrid && !infinite.exhausted ? (
|
|
1060
|
+
<NativeInfiniteSentinel
|
|
1061
|
+
onVisible={infinite.loadMore}
|
|
1062
|
+
leadPx={INFINITE_TRIGGER_ROWS * rowHeightPx}
|
|
1063
|
+
/>
|
|
1064
|
+
) : null
|
|
1065
|
+
}
|
|
1066
|
+
/>
|
|
1067
|
+
) : null}
|
|
1068
|
+
|
|
1069
|
+
{enableRowSelection && bulkBarPlacement === "bottom" ? (
|
|
1070
|
+
<View width="100%" {...(disabled ? disabledState.chromeKnobProps : {})}>
|
|
1071
|
+
<DataTableBulkActions
|
|
1072
|
+
actions={resolvedBulkActions}
|
|
1073
|
+
maxVisibleActions={5}
|
|
1074
|
+
showSelectionCount
|
|
1075
|
+
placement="bottom"
|
|
1076
|
+
/>
|
|
1077
|
+
</View>
|
|
1078
|
+
) : null}
|
|
1079
|
+
|
|
996
1080
|
{showAddRow && !readOnly && (
|
|
997
1081
|
<View>
|
|
998
1082
|
<Button
|
|
@@ -1006,7 +1090,7 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
1006
1090
|
</View>
|
|
1007
1091
|
)}
|
|
1008
1092
|
|
|
1009
|
-
{!showCards && enablePagination && (
|
|
1093
|
+
{!showCards && enablePagination && !infinite && (
|
|
1010
1094
|
<View width="100%" {...(disabled ? disabledState.chromeKnobProps : {})}>
|
|
1011
1095
|
{renderPaginationComponent()}
|
|
1012
1096
|
</View>
|
|
@@ -1076,6 +1160,11 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
|
|
|
1076
1160
|
deriveSelectRowLabel,
|
|
1077
1161
|
displayRows,
|
|
1078
1162
|
queryInFlight,
|
|
1163
|
+
infinite,
|
|
1164
|
+
isVirtualGrid,
|
|
1165
|
+
rowHeightPx,
|
|
1166
|
+
bulkBarPlacement,
|
|
1167
|
+
resolvedBulkActions,
|
|
1079
1168
|
t,
|
|
1080
1169
|
],
|
|
1081
1170
|
);
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The native root honours the props the web root does (MPO-454).
|
|
3
|
+
*
|
|
4
|
+
* index.native.tsx handed useDataTable a subset of the web root's options
|
|
5
|
+
* and never drew a bulk bar, so on iOS the backoffice list (infinite server
|
|
6
|
+
* mode, rows only through `loadPage`) showed "No data available", and
|
|
7
|
+
* checked rows offered no Export CSV. These specs render the native root
|
|
8
|
+
* itself: the suite runs on web, so `react-native` resolves to
|
|
9
|
+
* react-native-web and FlashList, which the list layout never mounts, is
|
|
10
|
+
* stubbed.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
14
|
+
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
|
15
|
+
import { readFileSync } from "node:fs";
|
|
16
|
+
import { resolve } from "node:path";
|
|
17
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
18
|
+
import type { BulkAction, DataTableColumn, PageLoadQuery, PageLoadResult } from "../../types";
|
|
19
|
+
import { DataTable } from "./index.native";
|
|
20
|
+
|
|
21
|
+
vi.mock("@shopify/flash-list", () => ({ FlashList: () => null }));
|
|
22
|
+
|
|
23
|
+
type Row = { id: number; name: string };
|
|
24
|
+
|
|
25
|
+
const rows: Row[] = Array.from({ length: 50 }, (_, i) => ({ id: i + 1, name: `Row ${i + 1}` }));
|
|
26
|
+
|
|
27
|
+
const columns: DataTableColumn<Row>[] = [
|
|
28
|
+
{ accessorKey: "name", header: "Name", meta: { role: "title" } },
|
|
29
|
+
{ accessorKey: "id", header: "ID" },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
function pageLoader() {
|
|
33
|
+
return vi.fn(async (q: PageLoadQuery): Promise<PageLoadResult<Row>> => {
|
|
34
|
+
const start = q.pageIndex * q.pageSize;
|
|
35
|
+
return { rows: rows.slice(start, start + q.pageSize), totalCount: rows.length };
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function renderInfinite(loadPage: ReturnType<typeof pageLoader>) {
|
|
40
|
+
return renderWithProviders(
|
|
41
|
+
<DataTable
|
|
42
|
+
data={[]}
|
|
43
|
+
columns={columns}
|
|
44
|
+
layout="list"
|
|
45
|
+
readOnly
|
|
46
|
+
paginationMode="infinite"
|
|
47
|
+
loadPage={loadPage}
|
|
48
|
+
initialState={{ pagination: { pageIndex: 0, pageSize: 10 } }}
|
|
49
|
+
/>,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Where the tail sentinel reports itself on screen, via measureInWindow. */
|
|
54
|
+
function placeSentinel(top: number) {
|
|
55
|
+
const real = HTMLElement.prototype.getBoundingClientRect;
|
|
56
|
+
vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockImplementation(
|
|
57
|
+
function (this: HTMLElement) {
|
|
58
|
+
if (this.getAttribute("data-testid") !== "infinite-sentinel") return real.call(this);
|
|
59
|
+
return {
|
|
60
|
+
x: 0,
|
|
61
|
+
y: top,
|
|
62
|
+
top,
|
|
63
|
+
left: 0,
|
|
64
|
+
right: 320,
|
|
65
|
+
bottom: top + 1,
|
|
66
|
+
width: 320,
|
|
67
|
+
height: 1,
|
|
68
|
+
} as DOMRect;
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
vi.restoreAllMocks();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("native DataTable infinite mode", () => {
|
|
78
|
+
it("renders page 0 from loadPage instead of the empty state", async () => {
|
|
79
|
+
const loadPage = pageLoader();
|
|
80
|
+
renderInfinite(loadPage);
|
|
81
|
+
expect(await screen.findByText("Row 1")).toBeDefined();
|
|
82
|
+
expect(screen.getByText("Row 10")).toBeDefined();
|
|
83
|
+
expect(screen.queryByText("Row 11")).toBeNull();
|
|
84
|
+
expect(screen.queryByText("No data available")).toBeNull();
|
|
85
|
+
expect(loadPage.mock.calls[0]![0].pageIndex).toBe(0);
|
|
86
|
+
expect(screen.getByTestId("infinite-status").textContent).toContain("50");
|
|
87
|
+
expect(screen.queryByLabelText("Go to next page")).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("appends the next page once the tail sentinel comes on screen", async () => {
|
|
91
|
+
placeSentinel(200);
|
|
92
|
+
const loadPage = pageLoader();
|
|
93
|
+
renderInfinite(loadPage);
|
|
94
|
+
expect(await screen.findByText("Row 11", undefined, { timeout: 3000 })).toBeDefined();
|
|
95
|
+
expect(screen.getByText("Row 20")).toBeDefined();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("holds the next page while the tail sentinel is below the fold", async () => {
|
|
99
|
+
placeSentinel(100_000);
|
|
100
|
+
const loadPage = pageLoader();
|
|
101
|
+
renderInfinite(loadPage);
|
|
102
|
+
expect(await screen.findByText("Row 10")).toBeDefined();
|
|
103
|
+
await waitFor(() => expect(loadPage.mock.calls.map(([q]) => q.pageIndex)).toEqual([0, 1]));
|
|
104
|
+
await new Promise((settle) => setTimeout(settle, 800));
|
|
105
|
+
expect(screen.queryByText("Row 11")).toBeNull();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe("native DataTable bulk actions", () => {
|
|
110
|
+
it("checking rows shows the bulk bar, and an action receives the checked rows", async () => {
|
|
111
|
+
const exportCsv = vi.fn();
|
|
112
|
+
const bulkActions: BulkAction<Row>[] = [
|
|
113
|
+
{ id: "assign", label: "Assign To", onExecute: vi.fn() },
|
|
114
|
+
{ id: "export", label: "Export CSV", onExecute: exportCsv },
|
|
115
|
+
];
|
|
116
|
+
renderWithProviders(
|
|
117
|
+
<DataTable
|
|
118
|
+
data={rows.slice(0, 3)}
|
|
119
|
+
columns={columns}
|
|
120
|
+
layout="list"
|
|
121
|
+
readOnly
|
|
122
|
+
enablePagination={false}
|
|
123
|
+
enableRowSelection
|
|
124
|
+
bulkActions={bulkActions}
|
|
125
|
+
/>,
|
|
126
|
+
);
|
|
127
|
+
expect(screen.queryByText("Export CSV")).toBeNull();
|
|
128
|
+
|
|
129
|
+
fireEvent.click(screen.getByRole("checkbox", { name: "Select row Row 1" }));
|
|
130
|
+
fireEvent.click(screen.getByRole("checkbox", { name: "Select row Row 3" }));
|
|
131
|
+
|
|
132
|
+
expect(await screen.findByText("2 selected")).toBeDefined();
|
|
133
|
+
expect(screen.getByRole("button", { name: "Assign To" })).toBeDefined();
|
|
134
|
+
fireEvent.click(screen.getByRole("button", { name: "Export CSV" }));
|
|
135
|
+
await waitFor(() => expect(exportCsv).toHaveBeenCalledTimes(1));
|
|
136
|
+
expect(exportCsv.mock.calls[0]![0].map((row: Row) => row.name)).toEqual(["Row 1", "Row 3"]);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("native DataTable root parity with web", () => {
|
|
141
|
+
const read = (file: string) => readFileSync(resolve(__dirname, file), "utf-8");
|
|
142
|
+
|
|
143
|
+
/** Option names a root hands useDataTable. */
|
|
144
|
+
function useDataTableOptions(source: string): Set<string> {
|
|
145
|
+
const start = source.indexOf("useDataTable({");
|
|
146
|
+
const body = source.slice(start, source.indexOf("});", start));
|
|
147
|
+
return new Set([...body.matchAll(/^\s*([A-Za-z]+)\s*[,:]/gm)].map((match) => match[1]!));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Prop names every `<Component ... />` call site in a root passes. */
|
|
151
|
+
function propNames(source: string, component: string): Set<string> {
|
|
152
|
+
const names = new Set<string>();
|
|
153
|
+
for (const site of source.split(`<${component}`).slice(1)) {
|
|
154
|
+
// Stop at the element's end or at a nested element, whichever is first.
|
|
155
|
+
const end = site.search(/\/>|</);
|
|
156
|
+
const element = end === -1 ? site : site.slice(0, end);
|
|
157
|
+
for (const match of element.matchAll(/(?:^|\s)([A-Za-z][A-Za-z0-9]*)=[{"]/g)) {
|
|
158
|
+
names.add(match[1]!);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return names;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
it("hands useDataTable every option the web root does", () => {
|
|
165
|
+
const web = useDataTableOptions(read("DataTableRoot.tsx"));
|
|
166
|
+
const native = useDataTableOptions(read("index.native.tsx"));
|
|
167
|
+
expect(web.size).toBeGreaterThan(0);
|
|
168
|
+
expect([...web].filter((option) => !native.has(option)).sort()).toEqual([]);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it.each(["DataTableBulkActions", "DataTableInfiniteTail"])(
|
|
172
|
+
"renders %s with every prop web passes",
|
|
173
|
+
(component) => {
|
|
174
|
+
const web = propNames(read("DataTableRoot.tsx"), component);
|
|
175
|
+
const native = propNames(read("index.native.tsx"), component);
|
|
176
|
+
expect(web.size).toBeGreaterThan(0);
|
|
177
|
+
expect([...web].filter((prop) => !native.has(prop)).sort()).toEqual([]);
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
});
|