@rickcedwhat/playwright-smart-table 6.18.0 → 6.20.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/engine/rowFinder.d.ts +16 -1
- package/dist/engine/rowFinder.js +156 -83
- package/dist/engine/rowResolution.d.ts +35 -0
- package/dist/engine/rowResolution.js +78 -0
- package/dist/engine/tableIteration.d.ts +4 -2
- package/dist/engine/tableIteration.js +72 -57
- package/dist/engine/tableMapper.js +10 -3
- package/dist/index.d.ts +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/plugins/index.d.ts +9 -3
- package/dist/presets/glide/headers.js +6 -3
- package/dist/presets/mui.js +28 -1
- package/dist/smartRow.js +240 -48
- package/dist/strategies/fill.js +5 -2
- package/dist/strategies/headers.js +6 -3
- package/dist/strategies/viewport.js +46 -3
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +146 -16
- package/dist/types.d.ts +140 -14
- package/dist/useTable.js +202 -26
- package/dist/utils/mergeTableConfig.js +4 -0
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ const elementTracker_1 = require("../utils/elementTracker");
|
|
|
7
7
|
const debugUtils_1 = require("../utils/debugUtils");
|
|
8
8
|
const navigationBarrier_1 = require("../utils/navigationBarrier");
|
|
9
9
|
const mutex_1 = require("../utils/mutex");
|
|
10
|
+
const rowResolution_1 = require("./rowResolution");
|
|
10
11
|
function log(config, msg) {
|
|
11
12
|
(0, debugUtils_1.logDebug)(config, 'verbose', msg);
|
|
12
13
|
}
|
|
@@ -20,7 +21,7 @@ async function runForEach(env, callback, options = {}) {
|
|
|
20
21
|
* Concurrency: `parallel` | `synchronized` | `sequential` (see RowIterationOptions).
|
|
21
22
|
*/
|
|
22
23
|
async function runMap(env, callback, options = {}, label = 'map') {
|
|
23
|
-
var _a, _b, _c, _d, _e, _f
|
|
24
|
+
var _a, _b, _c, _d, _e, _f;
|
|
24
25
|
const map = env.getMap();
|
|
25
26
|
const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : env.config.maxPages;
|
|
26
27
|
const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : env.config.strategies.dedupe;
|
|
@@ -34,21 +35,6 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
34
35
|
const useMutex = concurrency === 'sequential';
|
|
35
36
|
const useBulk = (_e = options.useBulkPagination) !== null && _e !== void 0 ? _e : false;
|
|
36
37
|
const tracker = new elementTracker_1.ElementTracker(label);
|
|
37
|
-
// Row-level loading gate (Bug #355). Must run BEFORE the dedupe strategy so
|
|
38
|
-
// content-based dedupe keys are computed on the row's final loaded state — a key
|
|
39
|
-
// computed on a skeleton changes once the row loads, and the ElementTracker
|
|
40
|
-
// re-flags the row (text signature changed), producing duplicates.
|
|
41
|
-
// Semantics mirror findRows (rowFinder.ts), with one deliberate difference:
|
|
42
|
-
// findRows drops loading rows when no timeout is set (legacy skip), but map/forEach
|
|
43
|
-
// historically visited every row — so without a timeout we process the row as-is.
|
|
44
|
-
const isRowLoading = (_f = env.config.strategies.loading) === null || _f === void 0 ? void 0 : _f.isRowLoading;
|
|
45
|
-
const rawRowTimeout = (_g = env.config.strategies.loading) === null || _g === void 0 ? void 0 : _g.rowLoadingTimeout;
|
|
46
|
-
// undefined = unset (process as-is); 0 = no wait (immediate re-check); >0 = wait up to N ms
|
|
47
|
-
// Non-finite values are treated as unset (defensive guard against Infinity/NaN)
|
|
48
|
-
const rowLoadingTimeout = rawRowTimeout !== undefined && Number.isFinite(rawRowTimeout) && rawRowTimeout >= 0
|
|
49
|
-
? rawRowTimeout
|
|
50
|
-
: undefined;
|
|
51
|
-
const onRowLoadingTimeout = (_j = (_h = env.config.strategies.loading) === null || _h === void 0 ? void 0 : _h.onRowLoadingTimeout) !== null && _j !== void 0 ? _j : 'read-as-is';
|
|
52
38
|
log(env.config, `${label}: starting (maxPages=${effectiveMaxPages}, mode=${concurrency}, dedupe=${!!dedupeStrategy})`);
|
|
53
39
|
const results = [];
|
|
54
40
|
try {
|
|
@@ -56,6 +42,7 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
56
42
|
let stopped = false;
|
|
57
43
|
let stoppedIndex = Infinity;
|
|
58
44
|
let pagesScanned = 1;
|
|
45
|
+
let reachedEnd = false;
|
|
59
46
|
const stop = (idx) => {
|
|
60
47
|
if (!stopped) {
|
|
61
48
|
log(env.config, `${label}: stop() called at row ${idx} — halting`);
|
|
@@ -67,12 +54,31 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
67
54
|
const rowLocators = env.getRowLocators();
|
|
68
55
|
const allIndices = await tracker.peekUnseenIndices(rowLocators);
|
|
69
56
|
const pageRows = await rowLocators.all();
|
|
57
|
+
// A2 (#353 part 2 / #357): when the viewport can report which rows are within the visible
|
|
58
|
+
// bounds, drop overscan rows from collection. They are left uncommitted (still unseen), so
|
|
59
|
+
// they're picked up on a later page once scrolled into view — preventing overscan from
|
|
60
|
+
// being ingested (#353) or double-collected via node recycling (#357). No-op when no
|
|
61
|
+
// viewport reports visible rows.
|
|
62
|
+
//
|
|
63
|
+
// #384: overscan rows ABOVE the visible range have already scrolled past and will never
|
|
64
|
+
// re-enter the viewport — collect them now. Only defer rows BELOW the visible range
|
|
65
|
+
// (they'll scroll into view on a later page).
|
|
66
|
+
let candidateIndices = allIndices;
|
|
67
|
+
const getVisibleRowIndices = (_f = env.config.strategies.viewport) === null || _f === void 0 ? void 0 : _f.getVisibleRowIndices;
|
|
68
|
+
if (getVisibleRowIndices) {
|
|
69
|
+
const visible = new Set(await getVisibleRowIndices(env.getContext()));
|
|
70
|
+
const minVisible = visible.size > 0 ? Math.min(...visible) : Infinity;
|
|
71
|
+
candidateIndices = allIndices.filter(idx => visible.has(idx) || idx < minVisible);
|
|
72
|
+
if (candidateIndices.length !== allIndices.length) {
|
|
73
|
+
log(env.config, `${label}: viewport visible filter — ${candidateIndices.length}/${allIndices.length} row(s) in view`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
70
76
|
// In synchronized mode, overscan rows rendered beyond the visible viewport by virtual
|
|
71
77
|
// scrollers can be evicted when the horizontal barrier fires snapFirstColumnIntoView.
|
|
72
78
|
// Filter them out before committing so they stay unseen and are picked up on the next page.
|
|
73
79
|
const newIndices = concurrency === 'synchronized'
|
|
74
|
-
? (await Promise.all(
|
|
75
|
-
:
|
|
80
|
+
? (await Promise.all(candidateIndices.map(async (idx) => ({ idx, present: pageRows[idx] != null && (await pageRows[idx].count()) > 0 })))).filter(r => r.present).map(r => r.idx)
|
|
81
|
+
: candidateIndices;
|
|
76
82
|
await tracker.commitIndices(rowLocators, newIndices);
|
|
77
83
|
const batchSize = newIndices.length;
|
|
78
84
|
if (batchSize === 0) {
|
|
@@ -82,52 +88,60 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
82
88
|
log(env.config, `${label}: scanning page ${pagesScanned} — ${batchSize} new row(s)`);
|
|
83
89
|
const barrier = useBarrier ? new navigationBarrier_1.NavigationBarrier(batchSize) : undefined;
|
|
84
90
|
const actionMutex = useMutex ? new mutex_1.Mutex() : null;
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
// `index` (ctx.index) is the enumeration counter — the order rows are visited,
|
|
92
|
+
// contiguous and monotonic. It drives the internal stop()/ordering logic.
|
|
93
|
+
const positionBase = rowIndex;
|
|
94
|
+
// B-hybrid (#362): the row's rowIndex is its logical/data-model index when a
|
|
95
|
+
// resolveRowIndex strategy is configured (so bringIntoView and position math are
|
|
96
|
+
// correct on virtualized tables); otherwise it equals the enumeration counter.
|
|
97
|
+
const smartRows = await Promise.all(newIndices.map(async (idx, i) => {
|
|
98
|
+
var _a;
|
|
99
|
+
const resolved = await (0, rowResolution_1.resolveLogicalRowIndex)(pageRows[idx], env.config, () => positionBase + i);
|
|
100
|
+
const logicalIndex = (_a = resolved === null || resolved === void 0 ? void 0 : resolved.index) !== null && _a !== void 0 ? _a : (positionBase + i);
|
|
101
|
+
const sr = env.makeSmartRow(pageRows[idx], map, logicalIndex, env.getCurrentPageIndex(), barrier, resolved === null || resolved === void 0 ? void 0 : resolved.selector);
|
|
102
|
+
// Mark as part of an iteration batch so toJSON's #366 re-pin uses rescan-only recovery
|
|
103
|
+
// (no scroll-back) — scrolling here would disrupt sibling rows' positional locators.
|
|
104
|
+
sr._inBatch = true;
|
|
105
|
+
return sr;
|
|
106
|
+
}));
|
|
107
|
+
// enumIndex = visit-order counter (ctx.index); row.rowIndex = logical (ctx.rowIndex).
|
|
108
|
+
const processRow = async (row, enumIndex) => {
|
|
109
|
+
var _a;
|
|
87
110
|
try {
|
|
88
|
-
if (stopped &&
|
|
111
|
+
if (stopped && enumIndex > stoppedIndex) {
|
|
89
112
|
return SKIP;
|
|
90
113
|
}
|
|
91
114
|
// Wait for the row to finish loading BEFORE evaluating its dedupe key (Bug #355).
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
if (!resolved) {
|
|
101
|
-
log(env.config, `${label}: row ${row.rowIndex} — still loading after ${rowLoadingTimeout}ms, action: ${onRowLoadingTimeout}`);
|
|
102
|
-
if (onRowLoadingTimeout === 'skip')
|
|
103
|
-
return SKIP;
|
|
104
|
-
if (onRowLoadingTimeout === 'throw') {
|
|
105
|
-
throw new Error(`[SmartTable] Row ${row.rowIndex} did not finish loading within ${rowLoadingTimeout}ms`);
|
|
106
|
-
}
|
|
107
|
-
// 'read-as-is': fall through and process the row
|
|
108
|
-
}
|
|
115
|
+
// map/forEach/filter process a still-loading row when no timeout is set (unlike
|
|
116
|
+
// findRows, which skips) — see resolveRowLoading's `noTimeoutAction`.
|
|
117
|
+
const loadingOutcome = await (0, rowResolution_1.resolveRowLoading)(row, env.config.strategies.loading, 'process', (msg) => log(env.config, `${label}: ${msg}`));
|
|
118
|
+
if (loadingOutcome === 'skip')
|
|
119
|
+
return SKIP;
|
|
120
|
+
if (loadingOutcome === 'throw') {
|
|
121
|
+
throw new Error(`[SmartTable] Row ${row.rowIndex} did not finish loading within ${(_a = env.config.strategies.loading) === null || _a === void 0 ? void 0 : _a.rowLoadingTimeout}ms`);
|
|
109
122
|
}
|
|
123
|
+
let dedupeKey;
|
|
110
124
|
if (dedupeKeys && dedupeStrategy) {
|
|
111
|
-
|
|
112
|
-
if (dedupeKeys.has(
|
|
113
|
-
log(env.config, `${label}: dedupe skip key="${
|
|
125
|
+
dedupeKey = await dedupeStrategy(row);
|
|
126
|
+
if (dedupeKeys.has(dedupeKey)) {
|
|
127
|
+
log(env.config, `${label}: dedupe skip key="${dedupeKey}"`);
|
|
114
128
|
return SKIP;
|
|
115
129
|
}
|
|
116
|
-
dedupeKeys.add(key);
|
|
117
130
|
}
|
|
118
131
|
// Execute callback (optionally serialized via mutex)
|
|
119
132
|
const runCallback = async () => {
|
|
120
|
-
if (stopped &&
|
|
133
|
+
if (stopped && enumIndex > stoppedIndex)
|
|
121
134
|
return SKIP;
|
|
122
|
-
log(env.config, `${label}: processing row ${row.rowIndex}`);
|
|
123
|
-
return await callback({ row, index:
|
|
135
|
+
log(env.config, `${label}: processing row (index ${enumIndex}, rowIndex ${row.rowIndex})`);
|
|
136
|
+
return await callback({ row, index: enumIndex, rowIndex: row.rowIndex, pageIndex: env.getCurrentPageIndex(), stop: () => stop(enumIndex) });
|
|
124
137
|
};
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
const result = actionMutex
|
|
139
|
+
? await actionMutex.run(runCallback)
|
|
140
|
+
: await runCallback();
|
|
141
|
+
if (dedupeKeys && dedupeKey !== undefined && result !== SKIP) {
|
|
142
|
+
dedupeKeys.add(dedupeKey);
|
|
130
143
|
}
|
|
144
|
+
return result;
|
|
131
145
|
}
|
|
132
146
|
finally {
|
|
133
147
|
// Ensure the barrier is notified once per row, even on error or skip.
|
|
@@ -136,16 +150,16 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
136
150
|
};
|
|
137
151
|
const pageResults = [];
|
|
138
152
|
if (concurrency === 'sequential') {
|
|
139
|
-
for (
|
|
140
|
-
pageResults.push(await processRow(
|
|
153
|
+
for (let i = 0; i < smartRows.length; i++) {
|
|
154
|
+
pageResults.push(await processRow(smartRows[i], positionBase + i));
|
|
141
155
|
}
|
|
142
156
|
}
|
|
143
157
|
else {
|
|
144
|
-
const results = await Promise.all(smartRows.map(processRow));
|
|
158
|
+
const results = await Promise.all(smartRows.map((row, i) => processRow(row, positionBase + i)));
|
|
145
159
|
pageResults.push(...results);
|
|
146
160
|
}
|
|
147
161
|
for (let i = 0; i < pageResults.length; i++) {
|
|
148
|
-
if (
|
|
162
|
+
if (positionBase + i > stoppedIndex)
|
|
149
163
|
break;
|
|
150
164
|
const r = pageResults[i];
|
|
151
165
|
if (r !== SKIP)
|
|
@@ -153,12 +167,13 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
153
167
|
}
|
|
154
168
|
rowIndex += batchSize;
|
|
155
169
|
}
|
|
156
|
-
if (stopped || pagesScanned >= effectiveMaxPages)
|
|
170
|
+
if (stopped || pagesScanned >= effectiveMaxPages || reachedEnd)
|
|
157
171
|
break;
|
|
158
172
|
log(env.config, `${label}: advancing to next page (${pagesScanned} → ${pagesScanned + 1})`);
|
|
159
173
|
if (!await env.advancePage(useBulk)) {
|
|
160
|
-
log(env.config, `${label}:
|
|
161
|
-
|
|
174
|
+
log(env.config, `${label}: pagination returned false — final scan`);
|
|
175
|
+
reachedEnd = true;
|
|
176
|
+
continue;
|
|
162
177
|
}
|
|
163
178
|
pagesScanned++;
|
|
164
179
|
}
|
|
@@ -14,7 +14,7 @@ class TableMapper {
|
|
|
14
14
|
(0, debugUtils_1.logDebug)(this.config, 'verbose', msg);
|
|
15
15
|
}
|
|
16
16
|
async getMap(timeout) {
|
|
17
|
-
var _a;
|
|
17
|
+
var _a, _b;
|
|
18
18
|
if (this._headerMap)
|
|
19
19
|
return this._headerMap;
|
|
20
20
|
this.log('Mapping headers...');
|
|
@@ -56,8 +56,15 @@ class TableMapper {
|
|
|
56
56
|
try {
|
|
57
57
|
const rawHeaders = await strategy(context);
|
|
58
58
|
const entries = await this.processHeaders(rawHeaders);
|
|
59
|
-
// Success
|
|
60
|
-
|
|
59
|
+
// Success — validate before caching so a collision doesn't leave stale state
|
|
60
|
+
const headerMap = new Map(entries);
|
|
61
|
+
const syntheticNames = Object.keys((_b = this.config.syntheticColumns) !== null && _b !== void 0 ? _b : {});
|
|
62
|
+
const collisions = syntheticNames.filter(name => headerMap.has(name));
|
|
63
|
+
if (collisions.length > 0) {
|
|
64
|
+
throw new Error(`Synthetic column name(s) collide with real header(s): ${collisions.join(', ')}. ` +
|
|
65
|
+
`Rename the synthetic column or use columnOverrides for columns that exist in the DOM.`);
|
|
66
|
+
}
|
|
67
|
+
this._headerMap = headerMap;
|
|
61
68
|
this.log(`Mapped ${entries.length} columns: ${JSON.stringify(entries.map(e => e[0]))}`);
|
|
62
69
|
return this._headerMap;
|
|
63
70
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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, } 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, } from './types';
|
|
4
4
|
export { Strategies } from './strategies';
|
|
5
5
|
export * as presets from './presets';
|
|
6
6
|
/** @deprecated Use `presets` instead. Plugins will be removed in v7.0.0. */
|
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.
|
|
6
|
+
export declare const PLAYWRIGHT_SMART_TABLE_VERSION: "6.20.0";
|
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.
|
|
9
|
+
exports.PLAYWRIGHT_SMART_TABLE_VERSION = "6.20.0";
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -23,7 +23,9 @@ export declare const Plugins: {
|
|
|
23
23
|
debug?: import("..").DebugConfig | undefined;
|
|
24
24
|
onReset?: ((context: import("..").TableContext) => Promise<void>) | undefined;
|
|
25
25
|
strategies?: import("../types").TableStrategies | undefined;
|
|
26
|
-
columnOverrides?: Partial<Record<string | number | symbol, import("
|
|
26
|
+
columnOverrides?: Partial<Record<string | number | symbol, import("..").ColumnOverride<any>>> | undefined;
|
|
27
|
+
syntheticColumns?: Record<string, import("..").SyntheticColumnDef<any>> | undefined;
|
|
28
|
+
emptyState?: import("@playwright/test").Locator | undefined;
|
|
27
29
|
};
|
|
28
30
|
/** @deprecated Use `presets.glide` */
|
|
29
31
|
Glide: {
|
|
@@ -43,7 +45,9 @@ export declare const Plugins: {
|
|
|
43
45
|
debug?: import("..").DebugConfig | undefined;
|
|
44
46
|
onReset?: ((context: import("..").TableContext) => Promise<void>) | undefined;
|
|
45
47
|
strategies?: import("../types").TableStrategies | undefined;
|
|
46
|
-
columnOverrides?: Partial<Record<string | number | symbol, import("
|
|
48
|
+
columnOverrides?: Partial<Record<string | number | symbol, import("..").ColumnOverride<any>>> | undefined;
|
|
49
|
+
syntheticColumns?: Record<string, import("..").SyntheticColumnDef<any>> | undefined;
|
|
50
|
+
emptyState?: import("@playwright/test").Locator | undefined;
|
|
47
51
|
};
|
|
48
52
|
/** @deprecated Use `presets.muiDataGrid` */
|
|
49
53
|
MUI: {
|
|
@@ -63,6 +67,8 @@ export declare const Plugins: {
|
|
|
63
67
|
debug?: import("..").DebugConfig | undefined;
|
|
64
68
|
onReset?: ((context: import("..").TableContext) => Promise<void>) | undefined;
|
|
65
69
|
strategies?: import("../types").TableStrategies | undefined;
|
|
66
|
-
columnOverrides?: Partial<Record<string | number | symbol, import("
|
|
70
|
+
columnOverrides?: Partial<Record<string | number | symbol, import("..").ColumnOverride<any>>> | undefined;
|
|
71
|
+
syntheticColumns?: Record<string, import("..").SyntheticColumnDef<any>> | undefined;
|
|
72
|
+
emptyState?: import("@playwright/test").Locator | undefined;
|
|
67
73
|
};
|
|
68
74
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.scrollRightHeader = void 0;
|
|
4
|
+
const debugUtils_1 = require("../../utils/debugUtils");
|
|
4
5
|
/**
|
|
5
6
|
* Scans for headers by finding a scrollable container and setting scrollLeft.
|
|
6
7
|
*/
|
|
@@ -47,10 +48,12 @@ const scrollRightHeader = async (context, options) => {
|
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
else {
|
|
50
|
-
|
|
51
|
+
(0, debugUtils_1.logDebug)(config, 'info', "HeaderStrategies.scrollRight: Could not find scroller. Returning visible headers.");
|
|
52
|
+
}
|
|
53
|
+
if (isScrollerFound) {
|
|
54
|
+
await scrollerHandle.evaluate(el => el.scrollLeft = 0);
|
|
55
|
+
await page.waitForTimeout(200);
|
|
51
56
|
}
|
|
52
|
-
await scrollerHandle.evaluate(el => el.scrollLeft = 0);
|
|
53
|
-
await page.waitForTimeout(200);
|
|
54
57
|
return Array.from(collectedHeaders);
|
|
55
58
|
};
|
|
56
59
|
exports.scrollRightHeader = scrollRightHeader;
|
package/dist/presets/mui.js
CHANGED
|
@@ -200,7 +200,9 @@ function createMuiDataGrid(opts) {
|
|
|
200
200
|
strategies: {
|
|
201
201
|
resolveRowIndex: async (row) => {
|
|
202
202
|
const v = await row.getAttribute('data-rowindex').catch(() => null);
|
|
203
|
-
|
|
203
|
+
if (v === null || isNaN(Number(v)))
|
|
204
|
+
return undefined;
|
|
205
|
+
return { index: Number(v), selector: `[data-rowindex="${v}"]` };
|
|
204
206
|
},
|
|
205
207
|
pagination: {
|
|
206
208
|
goNext: async (context) => {
|
|
@@ -353,6 +355,31 @@ function createMuiDataGrid(opts) {
|
|
|
353
355
|
return { first: Math.min(...indices), last: Math.max(...indices) };
|
|
354
356
|
}, rowSel);
|
|
355
357
|
},
|
|
358
|
+
// Geometry-based visible-row set for the iteration engine (A2, #353/#357). Returns
|
|
359
|
+
// the DOM positions of rows overlapping the virtualScroller's bounds so map/forEach
|
|
360
|
+
// skip the overscan rows MUI keeps mounted above/below the fold. The scroller is a
|
|
361
|
+
// descendant of root, so querySelector (not closest).
|
|
362
|
+
getVisibleRowIndices: async ({ root, config }) => {
|
|
363
|
+
const rowSel = config.rowSelector;
|
|
364
|
+
return root.evaluate((el, rowSel) => {
|
|
365
|
+
const scroller = el.querySelector('.MuiDataGrid-virtualScroller');
|
|
366
|
+
const rows = Array.from(el.querySelectorAll(rowSel));
|
|
367
|
+
const scrollerRect = scroller ? scroller.getBoundingClientRect() : null;
|
|
368
|
+
const visible = [];
|
|
369
|
+
rows.forEach((r, i) => {
|
|
370
|
+
if (!scrollerRect) {
|
|
371
|
+
visible.push(i);
|
|
372
|
+
return;
|
|
373
|
+
} // can't measure → keep all
|
|
374
|
+
const rect = r.getBoundingClientRect();
|
|
375
|
+
if (rect.height === 0)
|
|
376
|
+
return;
|
|
377
|
+
if (rect.bottom > scrollerRect.top && rect.top < scrollerRect.bottom)
|
|
378
|
+
visible.push(i);
|
|
379
|
+
});
|
|
380
|
+
return visible;
|
|
381
|
+
}, rowSel);
|
|
382
|
+
},
|
|
356
383
|
getVisibleColumnRange: async ({ root, config }) => {
|
|
357
384
|
const rowSel = config.rowSelector;
|
|
358
385
|
const cellSel = typeof config.cellSelector === 'string' ? config.cellSelector : '[aria-colindex]';
|