@rickcedwhat/playwright-smart-table 6.20.1-next.720a2e5 → 6.20.1-next.b5aa4bd
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/engine/rowFinder.js +17 -2
- package/dist/engine/tableIteration.js +11 -0
- package/dist/filterEngine.d.ts +4 -0
- package/dist/filterEngine.js +15 -4
- package/dist/index.d.ts +6 -2
- package/dist/index.js +5 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/plugins/index.d.ts +51 -10
- package/dist/plugins/index.js +14 -6
- package/dist/presets/glide/index.d.ts +1 -1
- package/dist/presets/glide/index.js +2 -2
- package/dist/presets/rdg.d.ts +1 -1
- package/dist/presets/rdg.js +4 -4
- package/dist/smartRow.js +87 -32
- package/dist/strategies/columns.d.ts +5 -9
- package/dist/strategies/columns.js +0 -10
- package/dist/strategies/contentReady.d.ts +20 -0
- package/dist/strategies/contentReady.js +62 -0
- package/dist/strategies/filter.d.ts +0 -4
- package/dist/strategies/filter.js +0 -16
- package/dist/strategies/index.d.ts +23 -15
- package/dist/strategies/index.js +9 -5
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +29 -1
- package/dist/types.d.ts +27 -1
- package/dist/useTable.js +9 -0
- package/dist/utils/resolveCellLocator.d.ts +35 -0
- package/dist/utils/resolveCellLocator.js +52 -0
- package/package.json +1 -1
package/dist/engine/rowFinder.js
CHANGED
|
@@ -7,6 +7,7 @@ const elementTracker_1 = require("../utils/elementTracker");
|
|
|
7
7
|
const sentinel_1 = require("../utils/sentinel");
|
|
8
8
|
const navigationBarrier_1 = require("../utils/navigationBarrier");
|
|
9
9
|
const rowResolution_1 = require("./rowResolution");
|
|
10
|
+
const resolveCellLocator_1 = require("../utils/resolveCellLocator");
|
|
10
11
|
class RowFinder {
|
|
11
12
|
constructor(rootLocator, config, resolve, filterEngine, tableMapper, makeSmartRow, tableState = { currentPageIndex: 0 }, advancePage = async () => false) {
|
|
12
13
|
this.rootLocator = rootLocator;
|
|
@@ -56,12 +57,26 @@ class RowFinder {
|
|
|
56
57
|
if (colIndex === undefined)
|
|
57
58
|
continue;
|
|
58
59
|
const override = this.config.columnOverrides[colName];
|
|
59
|
-
const cell =
|
|
60
|
+
const cell = (0, resolveCellLocator_1.resolveCellLocator)({
|
|
61
|
+
config: this.config,
|
|
62
|
+
resolve: this.resolve,
|
|
63
|
+
row: rowLocator,
|
|
64
|
+
root: this.rootLocator,
|
|
65
|
+
columnName: colName,
|
|
66
|
+
columnIndex: colIndex,
|
|
67
|
+
});
|
|
60
68
|
const getCell = (name) => {
|
|
61
69
|
const idx = map.get(name);
|
|
62
70
|
if (idx === undefined)
|
|
63
71
|
throw new Error(`Column "${name}" not found`);
|
|
64
|
-
return
|
|
72
|
+
return (0, resolveCellLocator_1.resolveCellLocator)({
|
|
73
|
+
config: this.config,
|
|
74
|
+
resolve: this.resolve,
|
|
75
|
+
row: rowLocator,
|
|
76
|
+
root: this.rootLocator,
|
|
77
|
+
columnName: name,
|
|
78
|
+
columnIndex: idx,
|
|
79
|
+
});
|
|
65
80
|
};
|
|
66
81
|
const context = {
|
|
67
82
|
row: this.makeSmartRow(rowLocator, map, undefined),
|
|
@@ -37,6 +37,7 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
37
37
|
const tracker = new elementTracker_1.ElementTracker(label);
|
|
38
38
|
log(env.config, `${label}: starting (maxPages=${effectiveMaxPages}, mode=${concurrency}, dedupe=${!!dedupeStrategy})`);
|
|
39
39
|
const results = [];
|
|
40
|
+
const seenLogicalIndices = new Set();
|
|
40
41
|
try {
|
|
41
42
|
let rowIndex = 0;
|
|
42
43
|
let stopped = false;
|
|
@@ -114,6 +115,13 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
114
115
|
if (stopped && enumIndex > stoppedIndex) {
|
|
115
116
|
return SKIP;
|
|
116
117
|
}
|
|
118
|
+
// When resolveRowIndex is configured, identical logical indices across pages
|
|
119
|
+
// indicate the same record (e.g. re-created DOM elements in recycling
|
|
120
|
+
// virtualizers whose WeakMap entry was GC'd). Skip without processing.
|
|
121
|
+
if (row.rowIndex !== undefined && seenLogicalIndices.has(row.rowIndex)) {
|
|
122
|
+
log(env.config, `${label}: skipping duplicate logical row ${row.rowIndex}`);
|
|
123
|
+
return SKIP;
|
|
124
|
+
}
|
|
117
125
|
// Wait for the row to finish loading BEFORE evaluating its dedupe key (Bug #355).
|
|
118
126
|
// map/forEach/filter process a still-loading row when no timeout is set (unlike
|
|
119
127
|
// findRows, which skips) — see resolveRowLoading's `noTimeoutAction`.
|
|
@@ -144,6 +152,9 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
144
152
|
if (dedupeKeys && dedupeKey !== undefined && result !== SKIP) {
|
|
145
153
|
dedupeKeys.add(dedupeKey);
|
|
146
154
|
}
|
|
155
|
+
if (row.rowIndex !== undefined && result !== SKIP) {
|
|
156
|
+
seenLogicalIndices.add(row.rowIndex);
|
|
157
|
+
}
|
|
147
158
|
return result;
|
|
148
159
|
}
|
|
149
160
|
finally {
|
package/dist/filterEngine.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export declare class FilterEngine {
|
|
|
10
10
|
* Note: `rootLocator` is optional for backward compatibility in call sites that already
|
|
11
11
|
* pass only the page. When strategies.filter is present we construct a TableContext
|
|
12
12
|
* using the provided `rootLocator`.
|
|
13
|
+
*
|
|
14
|
+
* When `strategies.getCellLocator` is set, cells are resolved through that strategy
|
|
15
|
+
* (via a `:scope` row stub) so column-virtualized grids that use `aria-colindex` / etc.
|
|
16
|
+
* are not filtered with fragile `.nth(colIndex)` DOM order.
|
|
13
17
|
*/
|
|
14
18
|
applyFilters(baseRows: Locator, filters: Record<string, FilterValue>, map: Map<string, number>, exact: boolean, page: Page, rootLocator?: Locator): Locator;
|
|
15
19
|
}
|
package/dist/filterEngine.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FilterEngine = void 0;
|
|
4
4
|
const stringUtils_1 = require("./utils/stringUtils");
|
|
5
|
+
const resolveCellLocator_1 = require("./utils/resolveCellLocator");
|
|
5
6
|
class FilterEngine {
|
|
6
7
|
constructor(config, resolve) {
|
|
7
8
|
this.config = config;
|
|
@@ -13,6 +14,10 @@ class FilterEngine {
|
|
|
13
14
|
* Note: `rootLocator` is optional for backward compatibility in call sites that already
|
|
14
15
|
* pass only the page. When strategies.filter is present we construct a TableContext
|
|
15
16
|
* using the provided `rootLocator`.
|
|
17
|
+
*
|
|
18
|
+
* When `strategies.getCellLocator` is set, cells are resolved through that strategy
|
|
19
|
+
* (via a `:scope` row stub) so column-virtualized grids that use `aria-colindex` / etc.
|
|
20
|
+
* are not filtered with fragile `.nth(colIndex)` DOM order.
|
|
16
21
|
*/
|
|
17
22
|
applyFilters(baseRows, filters, map, exact, page, rootLocator) {
|
|
18
23
|
var _a;
|
|
@@ -41,10 +46,16 @@ class FilterEngine {
|
|
|
41
46
|
});
|
|
42
47
|
continue;
|
|
43
48
|
}
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
// Prefer getCellLocator (column-virtualized presets); else cellSelector.nth.
|
|
50
|
+
// Playwright re-bases the `has` locator into each candidate row.
|
|
51
|
+
const targetCell = (0, resolveCellLocator_1.resolveCellLocatorForFilter)({
|
|
52
|
+
config: this.config,
|
|
53
|
+
resolve: this.resolve,
|
|
54
|
+
page,
|
|
55
|
+
root: rootLocator,
|
|
56
|
+
columnName: colName,
|
|
57
|
+
columnIndex: colIndex,
|
|
58
|
+
});
|
|
48
59
|
if (typeof filterVal === 'function') {
|
|
49
60
|
// Locator-based filter: (cell) => cell.locator(...)
|
|
50
61
|
filtered = filtered.filter({
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export { PLAYWRIGHT_SMART_TABLE_VERSION } from './packageVersion';
|
|
2
2
|
export { useTable } from './useTable';
|
|
3
|
-
export type { TableConfig, TableResult, SmartRow, SmartCell, Selector, FilterValue, PaginationPrimitives, SortingStrategy, FillOptions, RowIterationContext, RowIterationOptions, TableContext, StrategyContext, BeforeCellReadFn, GetCellLocatorFn, GetActiveCellFn, DebugConfig, SyntheticColumnDef, ColumnOverride, ColumnOverrideReadContext, } from './types';
|
|
3
|
+
export type { TableConfig, TableResult, SmartRow, SmartCell, Selector, FilterValue, PaginationPrimitives, SortingStrategy, FillOptions, RowIterationContext, RowIterationOptions, TableContext, StrategyContext, BeforeCellReadFn, GetCellLocatorFn, GetActiveCellFn, DebugConfig, SyntheticColumnDef, ColumnOverride, ColumnOverrideReadContext, TableStrategies, LoadingStrategy, ViewportStrategy, FilterStrategy, NavigationPrimitives, DedupeStrategy, ContentReadyStrategy, FillStrategy, PaginationStrategy, HeaderStrategy, } from './types';
|
|
4
4
|
export { Strategies } from './strategies';
|
|
5
5
|
export * as presets from './presets';
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* @deprecated Use `presets` instead (`presets.muiDataGrid`, `presets.rdg`, `presets.glide`, …).
|
|
8
|
+
* `Plugins` will be removed in v7.0.0.
|
|
9
|
+
* Note: `Plugins.MUI` is the **DataGrid** preset only — for MUI Table use `presets.muiTable`.
|
|
10
|
+
*/
|
|
7
11
|
export { Plugins } from './plugins';
|
|
8
12
|
export { mergeTableConfig } from './utils/mergeTableConfig';
|
package/dist/index.js
CHANGED
|
@@ -42,7 +42,11 @@ Object.defineProperty(exports, "useTable", { enumerable: true, get: function ()
|
|
|
42
42
|
var strategies_1 = require("./strategies");
|
|
43
43
|
Object.defineProperty(exports, "Strategies", { enumerable: true, get: function () { return strategies_1.Strategies; } });
|
|
44
44
|
exports.presets = __importStar(require("./presets"));
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated Use `presets` instead (`presets.muiDataGrid`, `presets.rdg`, `presets.glide`, …).
|
|
47
|
+
* `Plugins` will be removed in v7.0.0.
|
|
48
|
+
* Note: `Plugins.MUI` is the **DataGrid** preset only — for MUI Table use `presets.muiTable`.
|
|
49
|
+
*/
|
|
46
50
|
var plugins_1 = require("./plugins");
|
|
47
51
|
Object.defineProperty(exports, "Plugins", { enumerable: true, get: function () { return plugins_1.Plugins; } });
|
|
48
52
|
var mergeTableConfig_1 = require("./utils/mergeTableConfig");
|
package/dist/packageVersion.d.ts
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* Generated by scripts/embed-version.mjs from package.json
|
|
4
4
|
*/
|
|
5
5
|
/** Semver of this package. Use in logs or diagnostics to confirm the installed build. */
|
|
6
|
-
export declare const PLAYWRIGHT_SMART_TABLE_VERSION: "6.20.1-next.
|
|
6
|
+
export declare const PLAYWRIGHT_SMART_TABLE_VERSION: "6.20.1-next.b5aa4bd";
|
package/dist/packageVersion.js
CHANGED
|
@@ -6,4 +6,4 @@ exports.PLAYWRIGHT_SMART_TABLE_VERSION = void 0;
|
|
|
6
6
|
* Generated by scripts/embed-version.mjs from package.json
|
|
7
7
|
*/
|
|
8
8
|
/** Semver of this package. Use in logs or diagnostics to confirm the installed build. */
|
|
9
|
-
exports.PLAYWRIGHT_SMART_TABLE_VERSION = "6.20.1-next.
|
|
9
|
+
exports.PLAYWRIGHT_SMART_TABLE_VERSION = "6.20.1-next.b5aa4bd";
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @deprecated Use `presets` instead. Plugins will be removed in v7.0.0.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* - Plugins.
|
|
3
|
+
*
|
|
4
|
+
* Compatibility shim over official presets:
|
|
5
|
+
* - `Plugins.MUI` → `presets.muiDataGrid` (**DataGrid only** — not `presets.muiTable`)
|
|
6
|
+
* - `Plugins.RDG` → `presets.rdg`
|
|
7
|
+
* - `Plugins.Glide` → `presets.glide`
|
|
8
|
+
*
|
|
9
|
+
* Prefer: `useTable(loc, { ...presets.muiDataGrid, maxPages: 5 })`.
|
|
10
|
+
* Strategies only: `useTable(loc, { rowSelector: '...', strategies: presets.muiDataGrid.strategies })`.
|
|
6
11
|
*/
|
|
7
12
|
export declare const Plugins: {
|
|
8
13
|
/** @deprecated Use `presets.rdg` */
|
|
9
14
|
RDG: {
|
|
10
|
-
Strategies:
|
|
15
|
+
Strategies: {
|
|
16
|
+
header: (context: import("..").TableContext) => Promise<string[]>;
|
|
17
|
+
getCellLocator: ({ row, columnIndex }: {
|
|
18
|
+
row: import("@playwright/test").Locator;
|
|
19
|
+
columnIndex: number;
|
|
20
|
+
}) => import("@playwright/test").Locator;
|
|
21
|
+
navigation: {
|
|
22
|
+
goRight: ({ root }: import("..").StrategyContext) => Promise<void>;
|
|
23
|
+
goLeft: ({ root }: import("..").StrategyContext) => Promise<void>;
|
|
24
|
+
goDown: ({ root }: import("..").StrategyContext) => Promise<void>;
|
|
25
|
+
goUp: ({ root }: import("..").StrategyContext) => Promise<void>;
|
|
26
|
+
goHome: ({ root }: import("..").StrategyContext) => Promise<void>;
|
|
27
|
+
};
|
|
28
|
+
pagination: import("..").PaginationPrimitives;
|
|
29
|
+
};
|
|
11
30
|
headerSelector?: string | ((root: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
12
31
|
rowSelector?: string | undefined;
|
|
13
32
|
cellSelector?: string | ((row: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
@@ -22,14 +41,33 @@ export declare const Plugins: {
|
|
|
22
41
|
autoScroll?: boolean | undefined;
|
|
23
42
|
debug?: import("..").DebugConfig | undefined;
|
|
24
43
|
onReset?: ((context: import("..").TableContext) => Promise<void>) | undefined;
|
|
25
|
-
strategies?: import("
|
|
44
|
+
strategies?: import("..").TableStrategies | undefined;
|
|
26
45
|
columnOverrides?: Partial<Record<string | number | symbol, import("..").ColumnOverride<any>>> | undefined;
|
|
27
46
|
syntheticColumns?: Record<string, import("..").SyntheticColumnDef<any>> | undefined;
|
|
28
47
|
emptyState?: import("@playwright/test").Locator | undefined;
|
|
29
48
|
};
|
|
30
49
|
/** @deprecated Use `presets.glide` */
|
|
31
50
|
Glide: {
|
|
32
|
-
Strategies:
|
|
51
|
+
Strategies: {
|
|
52
|
+
fillSimple: import("..").FillStrategy;
|
|
53
|
+
fill: import("..").FillStrategy;
|
|
54
|
+
pagination: import("..").PaginationPrimitives;
|
|
55
|
+
header: (context: import("..").StrategyContext, options?: {
|
|
56
|
+
limit?: number;
|
|
57
|
+
selector?: string;
|
|
58
|
+
scrollAmount?: number;
|
|
59
|
+
}) => Promise<string[]>;
|
|
60
|
+
viewport: import("..").ViewportStrategy;
|
|
61
|
+
loading: {
|
|
62
|
+
isHeaderLoading: () => Promise<boolean>;
|
|
63
|
+
};
|
|
64
|
+
getCellLocator: ({ row, columnIndex }: any) => any;
|
|
65
|
+
getActiveCell: ({ page }: any) => Promise<{
|
|
66
|
+
rowIndex: number;
|
|
67
|
+
columnIndex: number;
|
|
68
|
+
locator: any;
|
|
69
|
+
} | null>;
|
|
70
|
+
};
|
|
33
71
|
headerSelector?: string | ((root: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
34
72
|
rowSelector?: string | undefined;
|
|
35
73
|
cellSelector?: string | ((row: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
@@ -44,14 +82,17 @@ export declare const Plugins: {
|
|
|
44
82
|
autoScroll?: boolean | undefined;
|
|
45
83
|
debug?: import("..").DebugConfig | undefined;
|
|
46
84
|
onReset?: ((context: import("..").TableContext) => Promise<void>) | undefined;
|
|
47
|
-
strategies?: import("
|
|
85
|
+
strategies?: import("..").TableStrategies | undefined;
|
|
48
86
|
columnOverrides?: Partial<Record<string | number | symbol, import("..").ColumnOverride<any>>> | undefined;
|
|
49
87
|
syntheticColumns?: Record<string, import("..").SyntheticColumnDef<any>> | undefined;
|
|
50
88
|
emptyState?: import("@playwright/test").Locator | undefined;
|
|
51
89
|
};
|
|
52
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* @deprecated Use `presets.muiDataGrid`.
|
|
92
|
+
* DataGrid only — for MUI `<Table>` use `presets.muiTable`.
|
|
93
|
+
*/
|
|
53
94
|
MUI: {
|
|
54
|
-
Strategies: import("
|
|
95
|
+
Strategies: import("..").TableStrategies | undefined;
|
|
55
96
|
headerSelector?: string | ((root: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
56
97
|
rowSelector?: string | undefined;
|
|
57
98
|
cellSelector?: string | ((row: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
@@ -66,7 +107,7 @@ export declare const Plugins: {
|
|
|
66
107
|
autoScroll?: boolean | undefined;
|
|
67
108
|
debug?: import("..").DebugConfig | undefined;
|
|
68
109
|
onReset?: ((context: import("..").TableContext) => Promise<void>) | undefined;
|
|
69
|
-
strategies?: import("
|
|
110
|
+
strategies?: import("..").TableStrategies | undefined;
|
|
70
111
|
columnOverrides?: Partial<Record<string | number | symbol, import("..").ColumnOverride<any>>> | undefined;
|
|
71
112
|
syntheticColumns?: Record<string, import("..").SyntheticColumnDef<any>> | undefined;
|
|
72
113
|
emptyState?: import("@playwright/test").Locator | undefined;
|
package/dist/plugins/index.js
CHANGED
|
@@ -4,15 +4,23 @@ exports.Plugins = void 0;
|
|
|
4
4
|
const presets_1 = require("../presets");
|
|
5
5
|
/**
|
|
6
6
|
* @deprecated Use `presets` instead. Plugins will be removed in v7.0.0.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* - Plugins.
|
|
7
|
+
*
|
|
8
|
+
* Compatibility shim over official presets:
|
|
9
|
+
* - `Plugins.MUI` → `presets.muiDataGrid` (**DataGrid only** — not `presets.muiTable`)
|
|
10
|
+
* - `Plugins.RDG` → `presets.rdg`
|
|
11
|
+
* - `Plugins.Glide` → `presets.glide`
|
|
12
|
+
*
|
|
13
|
+
* Prefer: `useTable(loc, { ...presets.muiDataGrid, maxPages: 5 })`.
|
|
14
|
+
* Strategies only: `useTable(loc, { rowSelector: '...', strategies: presets.muiDataGrid.strategies })`.
|
|
10
15
|
*/
|
|
11
16
|
exports.Plugins = {
|
|
12
17
|
/** @deprecated Use `presets.rdg` */
|
|
13
|
-
RDG: Object.assign(Object.assign({}, presets_1.rdg), { Strategies: presets_1.rdg.
|
|
18
|
+
RDG: Object.assign(Object.assign({}, presets_1.rdg), { Strategies: presets_1.rdg.Strategies }),
|
|
14
19
|
/** @deprecated Use `presets.glide` */
|
|
15
|
-
Glide: Object.assign(Object.assign({}, presets_1.glide), { Strategies: presets_1.glide.
|
|
16
|
-
/**
|
|
20
|
+
Glide: Object.assign(Object.assign({}, presets_1.glide), { Strategies: presets_1.glide.Strategies }),
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated Use `presets.muiDataGrid`.
|
|
23
|
+
* DataGrid only — for MUI `<Table>` use `presets.muiTable`.
|
|
24
|
+
*/
|
|
17
25
|
MUI: Object.assign(Object.assign({}, presets_1.muiDataGrid), { Strategies: presets_1.muiDataGrid.strategies }),
|
|
18
26
|
};
|
|
@@ -12,7 +12,7 @@ export declare const GlideStrategies: {
|
|
|
12
12
|
selector?: string;
|
|
13
13
|
scrollAmount?: number;
|
|
14
14
|
}) => Promise<string[]>;
|
|
15
|
-
viewport: import("
|
|
15
|
+
viewport: import("../..").ViewportStrategy;
|
|
16
16
|
loading: {
|
|
17
17
|
isHeaderLoading: () => Promise<boolean>;
|
|
18
18
|
};
|
|
@@ -123,8 +123,8 @@ function createGlide(options = {}) {
|
|
|
123
123
|
}
|
|
124
124
|
/**
|
|
125
125
|
* Full preset for Glide Data Grid (selectors + default strategies only).
|
|
126
|
-
* Spread: useTable(loc, { ...
|
|
127
|
-
* Strategies only (including fillSimple): useTable(loc, { rowSelector: '...', strategies:
|
|
126
|
+
* Spread: `useTable(loc, { ...presets.glide, maxPages: 5 })`.
|
|
127
|
+
* Strategies only (including fillSimple): `useTable(loc, { rowSelector: '...', strategies: presets.glide.Strategies })`.
|
|
128
128
|
* For a non-64-column grid, use `createGlide({ columnCount })` instead.
|
|
129
129
|
*/
|
|
130
130
|
const GlidePreset = {
|
package/dist/presets/rdg.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Locator } from '@playwright/test';
|
|
2
2
|
import { TableContext, TableConfig, ViewportStrategy, StrategyContext } from '../types';
|
|
3
|
-
/** Full strategies for React Data Grid. Use when you want to supply your own selectors: strategies:
|
|
3
|
+
/** Full strategies for React Data Grid. Use when you want to supply your own selectors: `strategies: presets.rdg.Strategies` */
|
|
4
4
|
export declare const RDGStrategies: {
|
|
5
5
|
header: (context: TableContext) => Promise<string[]>;
|
|
6
6
|
getCellLocator: ({ row, columnIndex }: {
|
package/dist/presets/rdg.js
CHANGED
|
@@ -92,20 +92,20 @@ const rdgNavigation = {
|
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
};
|
|
95
|
-
/** Default strategies for the RDG preset (used when you spread
|
|
95
|
+
/** Default strategies for the RDG preset (used when you spread `presets.rdg`). */
|
|
96
96
|
const RDGDefaultStrategies = {
|
|
97
97
|
header: scrollRightHeaderRDG,
|
|
98
98
|
getCellLocator: rdgGetCellLocator,
|
|
99
99
|
navigation: rdgNavigation,
|
|
100
100
|
pagination: rdgPaginationStrategy
|
|
101
101
|
};
|
|
102
|
-
/** Full strategies for React Data Grid. Use when you want to supply your own selectors: strategies:
|
|
102
|
+
/** Full strategies for React Data Grid. Use when you want to supply your own selectors: `strategies: presets.rdg.Strategies` */
|
|
103
103
|
// fallow-ignore-next-line unused-export
|
|
104
104
|
exports.RDGStrategies = RDGDefaultStrategies;
|
|
105
105
|
/**
|
|
106
106
|
* Full preset for React Data Grid (selectors + default strategies).
|
|
107
|
-
* Spread: useTable(loc, { ...
|
|
108
|
-
* Strategies only: useTable(loc, { rowSelector: '...', strategies:
|
|
107
|
+
* Spread: `useTable(loc, { ...presets.rdg, maxPages: 5 })`.
|
|
108
|
+
* Strategies only: `useTable(loc, { rowSelector: '...', strategies: presets.rdg.Strategies })`.
|
|
109
109
|
*/
|
|
110
110
|
const RDGPreset = {
|
|
111
111
|
rowSelector: '[role="row"].rdg-row',
|
package/dist/smartRow.js
CHANGED
|
@@ -196,6 +196,8 @@ const _navigateToCell = async (params) => {
|
|
|
196
196
|
currCol = ac.columnIndex;
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
|
+
// Column-0 snap is owned by the navigation primitive (scrollLeft=0, Home, etc.).
|
|
200
|
+
// Core must not special-case canvas/Home — that was pre-viewport Glide glue.
|
|
199
201
|
if (index === 0 && nav.snapFirstColumnIntoView) {
|
|
200
202
|
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: snapFirstColumnIntoView for column index 0');
|
|
201
203
|
await nav.snapFirstColumnIntoView(context);
|
|
@@ -206,27 +208,6 @@ const _navigateToCell = async (params) => {
|
|
|
206
208
|
currCol = ac.columnIndex;
|
|
207
209
|
}
|
|
208
210
|
}
|
|
209
|
-
// Home moves a11y focus within the current row to column 0. If focus row !== target row
|
|
210
|
-
// (e.g. still on previous row), Home jumps to grid origin and breaks `tr.nth(k)` reads.
|
|
211
|
-
if (typeof rowIndex === 'number' && currRow === rowIndex) {
|
|
212
|
-
await rootLocator.evaluate((el) => {
|
|
213
|
-
var _a;
|
|
214
|
-
const canvas = el.closest('canvas') || ((_a = el.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector('canvas'));
|
|
215
|
-
if (canvas instanceof HTMLCanvasElement) {
|
|
216
|
-
canvas.tabIndex = 0;
|
|
217
|
-
canvas.focus();
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
await page.keyboard.press('Home');
|
|
221
|
-
await page.waitForTimeout(120);
|
|
222
|
-
if (config.strategies.getActiveCell) {
|
|
223
|
-
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
224
|
-
if (ac) {
|
|
225
|
-
currRow = ac.rowIndex;
|
|
226
|
-
currCol = ac.columnIndex;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
211
|
}
|
|
231
212
|
let cDiff = index - currCol;
|
|
232
213
|
if (Math.abs(cDiff) > 12 && nav.seekColumnIndex) {
|
|
@@ -253,8 +234,8 @@ const _navigateToCell = async (params) => {
|
|
|
253
234
|
const settleMs = (Number.isFinite(nav.settleMs) && nav.settleMs >= 0) ? nav.settleMs : computed;
|
|
254
235
|
await page.waitForTimeout(settleMs);
|
|
255
236
|
}
|
|
256
|
-
//
|
|
257
|
-
//
|
|
237
|
+
// After horizontal steps, poll until getActiveCell / DOM presence confirms the target
|
|
238
|
+
// (a11y trees and virtual mounts often lag the scroll). Tune via navigation.maxWaitMs.
|
|
258
239
|
const pollIntervalMs = 10;
|
|
259
240
|
const computedMax = Math.min(6000, 250 + horizontalSteps * 25);
|
|
260
241
|
const maxWaitMs = (Number.isFinite(nav.maxWaitMs) && nav.maxWaitMs >= 0) ? nav.maxWaitMs : computedMax;
|
|
@@ -424,7 +405,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
424
405
|
return (await cell.innerText()).trim();
|
|
425
406
|
};
|
|
426
407
|
smart.toJSON = async (options) => {
|
|
427
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
408
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
428
409
|
// Atomic mode: snapshot the row in a single evaluate, then apply column overrides
|
|
429
410
|
// against a frozen reconstruction. Zero inter-column stagger — even in-place React
|
|
430
411
|
// re-renders can't affect it because overrides read from a detached clone.
|
|
@@ -434,10 +415,84 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
434
415
|
throw new Error('[SmartTable] toJSON({ atomic: true }) requires cellSelector to be a CSS string, not a function.');
|
|
435
416
|
}
|
|
436
417
|
const page = rootLocator.page();
|
|
418
|
+
// Re-pin: the row locator is a positional nth-child from the iteration loop.
|
|
419
|
+
// On recycling virtualizers, elements can shift between SmartRow creation and
|
|
420
|
+
// this clone — the nth element may now show a different row's content.
|
|
421
|
+
// Verify via resolveRowIndex and rescan if drifted.
|
|
422
|
+
let cloneTarget = rowLocator;
|
|
423
|
+
const resolveRI = config.strategies.resolveRowIndex;
|
|
424
|
+
const isSelfHealing = smart._selfHealing === true;
|
|
425
|
+
const rescanForRow = resolveRI && typeof rowIndex === 'number' && !isSelfHealing
|
|
426
|
+
? async () => {
|
|
427
|
+
const rows = await resolve(config.rowSelector, rootLocator).all();
|
|
428
|
+
for (const r of rows) {
|
|
429
|
+
try {
|
|
430
|
+
const rResult = await resolveRI(r);
|
|
431
|
+
if (rResult !== undefined && (0, rowResolution_1.normalizeRowIndexResult)(rResult).index === rowIndex)
|
|
432
|
+
return r;
|
|
433
|
+
}
|
|
434
|
+
catch ( /* element may have detached during scan */_a) { /* element may have detached during scan */ }
|
|
435
|
+
}
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
: null;
|
|
439
|
+
if (rescanForRow) {
|
|
440
|
+
let drifted = false;
|
|
441
|
+
try {
|
|
442
|
+
const curResult = await resolveRI(rowLocator);
|
|
443
|
+
const curIndex = curResult !== undefined
|
|
444
|
+
? (0, rowResolution_1.normalizeRowIndexResult)(curResult).index
|
|
445
|
+
: undefined;
|
|
446
|
+
drifted = curIndex !== rowIndex;
|
|
447
|
+
}
|
|
448
|
+
catch (_k) {
|
|
449
|
+
drifted = true;
|
|
450
|
+
}
|
|
451
|
+
if (drifted) {
|
|
452
|
+
const inBatch = smart._inBatch === true || !!smart._barrier;
|
|
453
|
+
let recovered = await rescanForRow();
|
|
454
|
+
if (!recovered && !inBatch && ((_a = config.strategies.viewport) === null || _a === void 0 ? void 0 : _a.scrollToRow)) {
|
|
455
|
+
await config.strategies.viewport.scrollToRow({ root: rootLocator, config, page, resolve }, rowIndex);
|
|
456
|
+
const deadline = Date.now() + 500;
|
|
457
|
+
while (!recovered && Date.now() < deadline) {
|
|
458
|
+
recovered = await rescanForRow();
|
|
459
|
+
if (!recovered)
|
|
460
|
+
await page.waitForTimeout(50);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (!recovered) {
|
|
464
|
+
throw new Error(`[SmartTable] toJSON({ atomic: true }): row ${rowIndex} recycled out of the DOM and could not be recovered. ` +
|
|
465
|
+
(inBatch
|
|
466
|
+
? `(During a map/forEach batch, scroll-back recovery is disabled to avoid disrupting sibling rows.)`
|
|
467
|
+
: `Ensure a viewport.scrollToRow strategy can restore the row.`));
|
|
468
|
+
}
|
|
469
|
+
cloneTarget = recovered;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// Wait for async content renders to settle before cloning.
|
|
473
|
+
if (config.strategies.contentReady) {
|
|
474
|
+
await config.strategies.contentReady(cloneTarget, page);
|
|
475
|
+
// Revalidate: the element may have recycled during the await.
|
|
476
|
+
if (rescanForRow) {
|
|
477
|
+
let postIdx;
|
|
478
|
+
try {
|
|
479
|
+
const r = await resolveRI(cloneTarget);
|
|
480
|
+
postIdx = r !== undefined ? (0, rowResolution_1.normalizeRowIndexResult)(r).index : undefined;
|
|
481
|
+
}
|
|
482
|
+
catch (_l) {
|
|
483
|
+
postIdx = undefined;
|
|
484
|
+
}
|
|
485
|
+
if (postIdx !== rowIndex) {
|
|
486
|
+
const reFound = await rescanForRow();
|
|
487
|
+
if (reFound)
|
|
488
|
+
cloneTarget = reFound;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
437
492
|
// Step 1: clone row in browser memory (NOT in the DOM), extract text + row HTML.
|
|
438
493
|
// Uses textContent (not innerText) because the clone is detached — innerText
|
|
439
494
|
// falls back to textContent on detached nodes per HTML spec, so be explicit.
|
|
440
|
-
const cloneHandle = await
|
|
495
|
+
const cloneHandle = await cloneTarget.evaluateHandle(el => el.cloneNode(true));
|
|
441
496
|
let snapshot;
|
|
442
497
|
try {
|
|
443
498
|
snapshot = await cloneHandle.evaluate((row, sel) => {
|
|
@@ -491,7 +546,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
491
546
|
await materializeRow();
|
|
492
547
|
const result = {};
|
|
493
548
|
for (const [col, idx] of columnsToProcess) {
|
|
494
|
-
const columnOverride = (
|
|
549
|
+
const columnOverride = (_b = config.columnOverrides) === null || _b === void 0 ? void 0 : _b[col];
|
|
495
550
|
const mapper = columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.read;
|
|
496
551
|
if (mapper) {
|
|
497
552
|
const cell = page.locator(`#${snapId}`).locator(cellSel).nth(idx);
|
|
@@ -508,7 +563,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
508
563
|
result[col] = snapshot.cells[idx].text;
|
|
509
564
|
}
|
|
510
565
|
}
|
|
511
|
-
for (const [name, def] of Object.entries((
|
|
566
|
+
for (const [name, def] of Object.entries((_c = config.syntheticColumns) !== null && _c !== void 0 ? _c : {})) {
|
|
512
567
|
if ((options === null || options === void 0 ? void 0 : options.columns) && !options.columns.includes(name))
|
|
513
568
|
continue;
|
|
514
569
|
const snapshotRow = Object.create(smart);
|
|
@@ -607,7 +662,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
607
662
|
// Re-pin to the correct logical row before reading this column (#366).
|
|
608
663
|
const stableRow = await resolveStableRow();
|
|
609
664
|
// Check if we have a column override for this column
|
|
610
|
-
const columnOverride = (
|
|
665
|
+
const columnOverride = (_d = config.columnOverrides) === null || _d === void 0 ? void 0 : _d[col];
|
|
611
666
|
const mapper = columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.read;
|
|
612
667
|
// --- Navigation Logic Start ---
|
|
613
668
|
const cell = config.strategies.getCellLocator
|
|
@@ -654,13 +709,13 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
654
709
|
});
|
|
655
710
|
}
|
|
656
711
|
// Cell loading wait (if configured)
|
|
657
|
-
const isCellLoading = (
|
|
658
|
-
const rawCellTimeout = (
|
|
712
|
+
const isCellLoading = (_e = config.strategies.loading) === null || _e === void 0 ? void 0 : _e.isCellLoading;
|
|
713
|
+
const rawCellTimeout = (_f = config.strategies.loading) === null || _f === void 0 ? void 0 : _f.cellLoadingTimeout;
|
|
659
714
|
// undefined = unset (read as-is immediately); 0 = no wait (immediate check); >0 = wait up to N ms
|
|
660
715
|
const cellLoadingTimeout = rawCellTimeout !== undefined && Number.isFinite(rawCellTimeout) && rawCellTimeout >= 0
|
|
661
716
|
? rawCellTimeout
|
|
662
717
|
: undefined;
|
|
663
|
-
const onCellLoadingTimeout = (
|
|
718
|
+
const onCellLoadingTimeout = (_h = (_g = config.strategies.loading) === null || _g === void 0 ? void 0 : _g.onCellLoadingTimeout) !== null && _h !== void 0 ? _h : 'read-as-is';
|
|
664
719
|
if (isCellLoading && await isCellLoading(targetCell, col, smart)) {
|
|
665
720
|
if (cellLoadingTimeout !== undefined) {
|
|
666
721
|
(0, debugUtils_1.logDebug)(config, 'verbose', `toJSON: cell "${col}" — waiting up to ${cellLoadingTimeout}ms`);
|
|
@@ -699,7 +754,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
699
754
|
result[col] = (text || '').trim();
|
|
700
755
|
}
|
|
701
756
|
}
|
|
702
|
-
for (const [name, def] of Object.entries((
|
|
757
|
+
for (const [name, def] of Object.entries((_j = config.syntheticColumns) !== null && _j !== void 0 ? _j : {})) {
|
|
703
758
|
if ((options === null || options === void 0 ? void 0 : options.columns) && !options.columns.includes(name))
|
|
704
759
|
continue;
|
|
705
760
|
const snapshotRow = Object.create(smart);
|
|
@@ -12,7 +12,9 @@ export interface NavigationPrimitives {
|
|
|
12
12
|
goHome?: (context: StrategyContext) => Promise<void>;
|
|
13
13
|
/**
|
|
14
14
|
* After vertical moves, run before horizontal steps when the target column index is 0.
|
|
15
|
-
* Use for horizontally virtualized
|
|
15
|
+
* Use for horizontally virtualized tables so the first column exists in the DOM again
|
|
16
|
+
* (e.g. set scrollLeft = 0). If the table needs keyboard `Home` or canvas focus, put that
|
|
17
|
+
* here — the core orchestrator does not special-case those behaviors.
|
|
16
18
|
* Do not reset vertical scroll here — RDG-style `goHome` is often unsuitable.
|
|
17
19
|
*/
|
|
18
20
|
snapFirstColumnIntoView?: (context: StrategyContext) => Promise<void>;
|
|
@@ -33,7 +35,8 @@ export interface NavigationPrimitives {
|
|
|
33
35
|
maxWaitMs?: number;
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
|
-
* @deprecated Use NavigationPrimitives instead.
|
|
38
|
+
* @deprecated Use NavigationPrimitives (`strategies.navigation`) instead. Removed from
|
|
39
|
+
* the public `Strategies` namespace in #428; this type remains for transitional typings.
|
|
37
40
|
* Defines the contract for a cell navigation strategy.
|
|
38
41
|
*/
|
|
39
42
|
export type CellNavigationStrategy = (context: StrategyContext & {
|
|
@@ -46,10 +49,3 @@ export type CellNavigationStrategy = (context: StrategyContext & {
|
|
|
46
49
|
locator: Locator;
|
|
47
50
|
} | null;
|
|
48
51
|
}) => Promise<void>;
|
|
49
|
-
export declare const CellNavigationStrategies: {
|
|
50
|
-
/**
|
|
51
|
-
* Default strategy: Assumes column is accessible or standard scrolling works.
|
|
52
|
-
* No specific action taken other than what Playwright's default locator handling does.
|
|
53
|
-
*/
|
|
54
|
-
default: () => Promise<void>;
|
|
55
|
-
};
|
|
@@ -1,12 +1,2 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CellNavigationStrategies = void 0;
|
|
4
|
-
exports.CellNavigationStrategies = {
|
|
5
|
-
/**
|
|
6
|
-
* Default strategy: Assumes column is accessible or standard scrolling works.
|
|
7
|
-
* No specific action taken other than what Playwright's default locator handling does.
|
|
8
|
-
*/
|
|
9
|
-
default: async () => {
|
|
10
|
-
// No-op
|
|
11
|
-
}
|
|
12
|
-
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ContentReadyStrategy } from '../types';
|
|
2
|
+
export type { ContentReadyStrategy };
|
|
3
|
+
export declare const ContentReadyStrategies: {
|
|
4
|
+
/**
|
|
5
|
+
* Polls the row's text content until two consecutive reads match.
|
|
6
|
+
*/
|
|
7
|
+
textStable: (options?: {
|
|
8
|
+
timeout?: number;
|
|
9
|
+
interval?: number;
|
|
10
|
+
}) => ContentReadyStrategy;
|
|
11
|
+
/**
|
|
12
|
+
* Uses MutationObserver to wait for DOM mutations on the row's subtree
|
|
13
|
+
* to settle. Resolves when no mutations occur for `quietPeriod` ms, or
|
|
14
|
+
* when `timeout` expires.
|
|
15
|
+
*/
|
|
16
|
+
mutationSettled: (options?: {
|
|
17
|
+
timeout?: number;
|
|
18
|
+
quietPeriod?: number;
|
|
19
|
+
}) => ContentReadyStrategy;
|
|
20
|
+
};
|