@rickcedwhat/playwright-smart-table 6.9.0 → 6.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine/rowFinder.js +153 -168
- package/dist/engine/tableIteration.js +105 -120
- package/dist/engine/tableMapper.js +88 -103
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/presets/glide/columns.js +23 -32
- package/dist/presets/glide/headers.js +18 -27
- package/dist/presets/glide/index.d.ts +9 -9
- package/dist/presets/glide/index.js +24 -33
- package/dist/presets/mui.js +197 -124
- package/dist/presets/rdg.d.ts +1 -1
- package/dist/presets/rdg.js +32 -41
- package/dist/smartRow.js +162 -96
- package/dist/strategies/columns.js +2 -11
- package/dist/strategies/dedupe.js +3 -12
- package/dist/strategies/fill.js +20 -29
- package/dist/strategies/headers.js +22 -31
- package/dist/strategies/index.d.ts +4 -0
- package/dist/strategies/index.js +3 -0
- package/dist/strategies/loading.js +24 -33
- package/dist/strategies/pagination.js +21 -30
- package/dist/strategies/sorting.js +18 -31
- package/dist/strategies/stabilization.js +28 -37
- package/dist/strategies/viewport.d.ts +52 -0
- package/dist/strategies/viewport.js +123 -0
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +97 -0
- package/dist/types.d.ts +97 -0
- package/dist/useTable.js +98 -76
- package/dist/utils/debugUtils.js +5 -16
- package/dist/utils/elementTracker.js +27 -40
- package/dist/utils/mutex.js +5 -16
- package/dist/utils/navigationBarrier.js +25 -36
- package/dist/utils/paginationPath.js +102 -115
- package/dist/utils/smartRowArray.js +2 -11
- package/dist/utils.js +4 -13
- package/package.json +9 -5
package/dist/presets/mui.js
CHANGED
|
@@ -1,60 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.muiDataGrid = exports.muiTable = void 0;
|
|
13
4
|
/**
|
|
14
5
|
* Safely wait for MUI pagination to stabilize.
|
|
15
6
|
* Polls for displayed row text changes and waits for loading overlays to disappear.
|
|
16
7
|
*/
|
|
17
|
-
function waitForMuiPaginationStabilization(context, displayedRows, oldText) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
yield context.page.waitForTimeout(100);
|
|
29
|
-
}
|
|
8
|
+
async function waitForMuiPaginationStabilization(context, displayedRows, oldText) {
|
|
9
|
+
var _a;
|
|
10
|
+
let retries = 50; // 5 second timeout for client-side render
|
|
11
|
+
while (oldText && retries-- > 0 && await displayedRows.innerText().catch(() => '') === oldText) {
|
|
12
|
+
await context.page.waitForTimeout(100);
|
|
13
|
+
}
|
|
14
|
+
// Standard MUI loading strategy check
|
|
15
|
+
if ((_a = context.config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading) {
|
|
16
|
+
let loadingRetries = 30;
|
|
17
|
+
while (loadingRetries-- > 0 && await context.config.strategies.loading.isTableLoading(context)) {
|
|
18
|
+
await context.page.waitForTimeout(100);
|
|
30
19
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
20
|
+
}
|
|
21
|
+
// DataGrid specific fallback (if not using loading strategy)
|
|
22
|
+
const overlay = context.root.locator('.MuiDataGrid-overlay');
|
|
23
|
+
if (await overlay.count() > 0) {
|
|
24
|
+
let loadingRetries = 30;
|
|
25
|
+
while (loadingRetries-- > 0 && await overlay.isVisible().catch(() => false)) {
|
|
26
|
+
await context.page.waitForTimeout(100);
|
|
38
27
|
}
|
|
39
|
-
}
|
|
28
|
+
}
|
|
40
29
|
}
|
|
41
30
|
/** safely locate the pagination container related to our table context */
|
|
42
|
-
function getPaginationRoot(context) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return context.root.locator('xpath=following::div[contains(@class, "MuiTablePagination-root")][1]').first();
|
|
57
|
-
});
|
|
31
|
+
async function getPaginationRoot(context) {
|
|
32
|
+
// 1. If the user initialized useTable on the layout wrapper itself
|
|
33
|
+
const inside = context.root.locator('.MuiTablePagination-root').first();
|
|
34
|
+
if (await inside.count() > 0)
|
|
35
|
+
return inside;
|
|
36
|
+
// 2. If the user initialized on the Table, look up to the closest Paper wrapper
|
|
37
|
+
const paper = context.root.locator('xpath=ancestor::*[contains(@class, "MuiPaper-root")][1]').first();
|
|
38
|
+
if (await paper.count() > 0) {
|
|
39
|
+
const inPaper = paper.locator('.MuiTablePagination-root').first();
|
|
40
|
+
if (await inPaper.count() > 0)
|
|
41
|
+
return inPaper;
|
|
42
|
+
}
|
|
43
|
+
// 3. Fallback: find the first pagination footer that follows this context in the DOM
|
|
44
|
+
return context.root.locator('xpath=following::div[contains(@class, "MuiTablePagination-root")][1]').first();
|
|
58
45
|
}
|
|
59
46
|
/**
|
|
60
47
|
* Preset configuration for the standard Material UI `<Table />` component.
|
|
@@ -69,56 +56,56 @@ exports.muiTable = {
|
|
|
69
56
|
rowSelector: 'tbody tr, tbody .MuiTableRow-root',
|
|
70
57
|
cellSelector: 'td, th, .MuiTableCell-body',
|
|
71
58
|
headerSelector: 'thead th, .MuiTableCell-head',
|
|
72
|
-
headerTransformer: ({ text }) => text.replace(/\
|
|
59
|
+
headerTransformer: ({ text }) => text.replace(/\bsorted\s+(?:ascending|descending)\b/i, '').trim(),
|
|
73
60
|
strategies: {
|
|
74
61
|
pagination: {
|
|
75
|
-
goNext: (context) =>
|
|
76
|
-
const root =
|
|
62
|
+
goNext: async (context) => {
|
|
63
|
+
const root = await getPaginationRoot(context);
|
|
77
64
|
const nextBtn = root.locator('button[aria-label="Go to next page"]');
|
|
78
|
-
if (
|
|
65
|
+
if (await nextBtn.count() === 0 || await nextBtn.isDisabled())
|
|
79
66
|
return false;
|
|
80
67
|
const displayedRows = root.locator('.MuiTablePagination-displayedRows');
|
|
81
|
-
const oldText =
|
|
68
|
+
const oldText = await displayedRows.innerText().catch(() => '');
|
|
82
69
|
// Force click ignores cookie banners obscuring the footer
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
await nextBtn.click({ force: true });
|
|
71
|
+
await waitForMuiPaginationStabilization(context, displayedRows, oldText);
|
|
85
72
|
return true;
|
|
86
|
-
}
|
|
87
|
-
goPrevious: (context) =>
|
|
88
|
-
const root =
|
|
73
|
+
},
|
|
74
|
+
goPrevious: async (context) => {
|
|
75
|
+
const root = await getPaginationRoot(context);
|
|
89
76
|
const prevBtn = root.locator('button[aria-label="Go to previous page"]');
|
|
90
|
-
if (
|
|
77
|
+
if (await prevBtn.count() === 0 || await prevBtn.isDisabled())
|
|
91
78
|
return false;
|
|
92
79
|
const displayedRows = root.locator('.MuiTablePagination-displayedRows');
|
|
93
|
-
const oldText =
|
|
94
|
-
|
|
95
|
-
|
|
80
|
+
const oldText = await displayedRows.innerText().catch(() => '');
|
|
81
|
+
await prevBtn.click({ force: true });
|
|
82
|
+
await waitForMuiPaginationStabilization(context, displayedRows, oldText);
|
|
96
83
|
return true;
|
|
97
|
-
}
|
|
98
|
-
goToFirst: (context) =>
|
|
99
|
-
const root =
|
|
84
|
+
},
|
|
85
|
+
goToFirst: async (context) => {
|
|
86
|
+
const root = await getPaginationRoot(context);
|
|
100
87
|
const prevBtn = root.locator('button[aria-label="Go to previous page"]');
|
|
101
88
|
const displayedRows = root.locator('.MuiTablePagination-displayedRows');
|
|
102
89
|
let maxRetries = 50;
|
|
103
90
|
let clicked = false;
|
|
104
|
-
while (maxRetries-- > 0 &&
|
|
105
|
-
const oldText =
|
|
106
|
-
|
|
107
|
-
|
|
91
|
+
while (maxRetries-- > 0 && await prevBtn.count() > 0 && !(await prevBtn.isDisabled())) {
|
|
92
|
+
const oldText = await displayedRows.innerText().catch(() => '');
|
|
93
|
+
await prevBtn.click();
|
|
94
|
+
await waitForMuiPaginationStabilization(context, displayedRows, oldText);
|
|
108
95
|
clicked = true;
|
|
109
96
|
}
|
|
110
97
|
return clicked;
|
|
111
|
-
}
|
|
98
|
+
}
|
|
112
99
|
},
|
|
113
100
|
sorting: {
|
|
114
|
-
getSortState:
|
|
101
|
+
getSortState: async ({ columnName, context }) => {
|
|
115
102
|
if (!context.getHeaderCell)
|
|
116
103
|
return 'none';
|
|
117
|
-
const header =
|
|
104
|
+
const header = await context.getHeaderCell(columnName);
|
|
118
105
|
const sortLabel = header.locator('.MuiTableSortLabel-root').first();
|
|
119
|
-
if (!(
|
|
106
|
+
if (!(await sortLabel.isVisible().catch(() => false)))
|
|
120
107
|
return 'none';
|
|
121
|
-
const className =
|
|
108
|
+
const className = await sortLabel.getAttribute('class') || '';
|
|
122
109
|
if (className.includes('directionAsc'))
|
|
123
110
|
return 'asc';
|
|
124
111
|
if (className.includes('directionDesc'))
|
|
@@ -127,34 +114,34 @@ exports.muiTable = {
|
|
|
127
114
|
if (className.includes('active'))
|
|
128
115
|
return 'asc';
|
|
129
116
|
return 'none';
|
|
130
|
-
}
|
|
131
|
-
doSort:
|
|
132
|
-
var
|
|
117
|
+
},
|
|
118
|
+
doSort: async ({ columnName, direction, context }) => {
|
|
119
|
+
var _a, _b;
|
|
133
120
|
if (!context.getHeaderCell)
|
|
134
121
|
return;
|
|
135
|
-
const header =
|
|
122
|
+
const header = await context.getHeaderCell(columnName);
|
|
136
123
|
const sortLabel = header.locator('.MuiTableSortLabel-root').first();
|
|
137
|
-
const target =
|
|
138
|
-
let current =
|
|
124
|
+
const target = await sortLabel.isVisible().catch(() => false) ? sortLabel : header;
|
|
125
|
+
let current = await ((_a = context.config.strategies.sorting) === null || _a === void 0 ? void 0 : _a.getSortState({ columnName, context })) || 'none';
|
|
139
126
|
let attempts = 0;
|
|
140
127
|
// Click until the state matches the target direction, max 3 times (none -> asc -> desc)
|
|
141
128
|
while (current !== direction && attempts < 3) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
current =
|
|
129
|
+
await target.click();
|
|
130
|
+
await context.page.waitForTimeout(100);
|
|
131
|
+
current = await ((_b = context.config.strategies.sorting) === null || _b === void 0 ? void 0 : _b.getSortState({ columnName, context })) || 'none';
|
|
145
132
|
attempts++;
|
|
146
133
|
}
|
|
147
|
-
}
|
|
134
|
+
}
|
|
148
135
|
},
|
|
149
|
-
dedupe: (row) =>
|
|
136
|
+
dedupe: async (row) => {
|
|
150
137
|
// MUI virtualized tables attach data-index to rows
|
|
151
|
-
const dataIndex =
|
|
138
|
+
const dataIndex = await row.getAttribute('data-index');
|
|
152
139
|
if (dataIndex !== null)
|
|
153
140
|
return dataIndex;
|
|
154
141
|
// Unvirtualized tables don't recycle nodes, but if they do duplicate, fallback to first cell's text
|
|
155
|
-
const text =
|
|
142
|
+
const text = await row.locator('td, th').first().innerText().catch(() => '');
|
|
156
143
|
return text || String(Math.random());
|
|
157
|
-
}
|
|
144
|
+
}
|
|
158
145
|
}
|
|
159
146
|
};
|
|
160
147
|
/**
|
|
@@ -173,94 +160,180 @@ exports.muiDataGrid = {
|
|
|
173
160
|
headerSelector: '.MuiDataGrid-columnHeader',
|
|
174
161
|
strategies: {
|
|
175
162
|
pagination: {
|
|
176
|
-
goNext: (context) =>
|
|
163
|
+
goNext: async (context) => {
|
|
177
164
|
const footer = context.root.locator('.MuiDataGrid-footerContainer');
|
|
178
165
|
const nextBtn = footer.locator('button[aria-label="Go to next page"]');
|
|
179
166
|
try {
|
|
180
|
-
|
|
167
|
+
await nextBtn.waitFor({ state: 'visible', timeout: 2000 });
|
|
181
168
|
}
|
|
182
169
|
catch (e) {
|
|
183
170
|
return false;
|
|
184
171
|
}
|
|
185
|
-
if (
|
|
172
|
+
if (await nextBtn.isDisabled())
|
|
186
173
|
return false;
|
|
187
174
|
const displayedRows = footer.locator('.MuiTablePagination-displayedRows');
|
|
188
|
-
const oldText =
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
175
|
+
const oldText = await displayedRows.innerText().catch(() => '');
|
|
176
|
+
await nextBtn.scrollIntoViewIfNeeded().catch(() => { });
|
|
177
|
+
await nextBtn.click({ force: true });
|
|
178
|
+
await waitForMuiPaginationStabilization(context, displayedRows, oldText);
|
|
192
179
|
return true;
|
|
193
|
-
}
|
|
194
|
-
goPrevious: (context) =>
|
|
180
|
+
},
|
|
181
|
+
goPrevious: async (context) => {
|
|
195
182
|
const footer = context.root.locator('.MuiDataGrid-footerContainer');
|
|
196
183
|
const prevBtn = footer.locator('button[aria-label="Go to previous page"]');
|
|
197
184
|
try {
|
|
198
|
-
|
|
185
|
+
await prevBtn.waitFor({ state: 'visible', timeout: 2000 });
|
|
199
186
|
}
|
|
200
187
|
catch (e) {
|
|
201
188
|
return false;
|
|
202
189
|
}
|
|
203
|
-
if (
|
|
190
|
+
if (await prevBtn.isDisabled())
|
|
204
191
|
return false;
|
|
205
192
|
const displayedRows = footer.locator('.MuiTablePagination-displayedRows');
|
|
206
|
-
const oldText =
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
193
|
+
const oldText = await displayedRows.innerText().catch(() => '');
|
|
194
|
+
await prevBtn.scrollIntoViewIfNeeded().catch(() => { });
|
|
195
|
+
await prevBtn.click({ force: true });
|
|
196
|
+
await waitForMuiPaginationStabilization(context, displayedRows, oldText);
|
|
210
197
|
return true;
|
|
211
|
-
}
|
|
198
|
+
}
|
|
212
199
|
},
|
|
213
200
|
sorting: {
|
|
214
|
-
getSortState:
|
|
201
|
+
getSortState: async ({ columnName, context }) => {
|
|
215
202
|
if (!context.getHeaderCell)
|
|
216
203
|
return 'none';
|
|
217
|
-
const header =
|
|
218
|
-
const sortAttr =
|
|
204
|
+
const header = await context.getHeaderCell(columnName);
|
|
205
|
+
const sortAttr = await header.getAttribute('aria-sort');
|
|
219
206
|
if (sortAttr === 'ascending')
|
|
220
207
|
return 'asc';
|
|
221
208
|
if (sortAttr === 'descending')
|
|
222
209
|
return 'desc';
|
|
223
210
|
return 'none';
|
|
224
|
-
}
|
|
225
|
-
doSort:
|
|
226
|
-
var
|
|
211
|
+
},
|
|
212
|
+
doSort: async ({ columnName, direction, context }) => {
|
|
213
|
+
var _a, _b;
|
|
227
214
|
if (!context.getHeaderCell)
|
|
228
215
|
return;
|
|
229
|
-
const header =
|
|
230
|
-
let current =
|
|
216
|
+
const header = await context.getHeaderCell(columnName);
|
|
217
|
+
let current = await ((_a = context.config.strategies.sorting) === null || _a === void 0 ? void 0 : _a.getSortState({ columnName, context })) || 'none';
|
|
231
218
|
let attempts = 0;
|
|
232
219
|
while (current !== direction && attempts < 3) {
|
|
233
220
|
const clickTarget = header.locator('.MuiDataGrid-columnHeaderTitleContainer').first();
|
|
234
|
-
if (
|
|
235
|
-
|
|
221
|
+
if (await clickTarget.isVisible()) {
|
|
222
|
+
await clickTarget.click({ force: true });
|
|
236
223
|
}
|
|
237
224
|
else {
|
|
238
|
-
|
|
225
|
+
await header.click({ force: true });
|
|
239
226
|
}
|
|
240
227
|
// Sorting can trigger large re-renders/virtualization shifts
|
|
241
|
-
|
|
228
|
+
await context.page.waitForTimeout(500);
|
|
242
229
|
// Wait for stabilization (using DataGrid specific overlay check inside helper)
|
|
243
230
|
const displayedRows = context.root.locator('.MuiTablePagination-displayedRows');
|
|
244
|
-
const text =
|
|
245
|
-
|
|
246
|
-
current =
|
|
231
|
+
const text = await displayedRows.innerText().catch(() => '');
|
|
232
|
+
await waitForMuiPaginationStabilization(context, displayedRows, text);
|
|
233
|
+
current = await ((_b = context.config.strategies.sorting) === null || _b === void 0 ? void 0 : _b.getSortState({ columnName, context })) || 'none';
|
|
247
234
|
attempts++;
|
|
248
235
|
}
|
|
249
|
-
}
|
|
236
|
+
}
|
|
250
237
|
},
|
|
251
|
-
dedupe: (row) =>
|
|
238
|
+
dedupe: async (row) => {
|
|
252
239
|
var _a;
|
|
253
240
|
// DataGrid uses data-rowindex for its own virtualization management
|
|
254
|
-
return (_a = (
|
|
255
|
-
}
|
|
241
|
+
return (_a = (await row.getAttribute('data-rowindex').catch(() => null))) !== null && _a !== void 0 ? _a : '';
|
|
242
|
+
},
|
|
256
243
|
getCellLocator: ({ row, columnIndex }) => {
|
|
257
244
|
// Horizontal virtualization uses aria-colindex (1-indexed)
|
|
258
245
|
return row.locator(`[aria-colindex="${columnIndex + 1}"]`);
|
|
259
246
|
},
|
|
247
|
+
// MUI DataGrid virtualizes rows vertically (always) and columns horizontally (when
|
|
248
|
+
// columnBuffer is configured). The virtualScroller is a descendant of root, so
|
|
249
|
+
// scroll methods use querySelector rather than closest().
|
|
250
|
+
viewport: {
|
|
251
|
+
getVisibleRowRange: async ({ root, config }) => {
|
|
252
|
+
const rowSel = config.rowSelector;
|
|
253
|
+
return root.evaluate((el, rowSel) => {
|
|
254
|
+
const indices = Array.from(el.querySelectorAll(rowSel))
|
|
255
|
+
.map(r => Number(r.getAttribute('data-rowindex')))
|
|
256
|
+
.filter(n => !isNaN(n));
|
|
257
|
+
if (!indices.length)
|
|
258
|
+
return { first: 0, last: 0 };
|
|
259
|
+
return { first: Math.min(...indices), last: Math.max(...indices) };
|
|
260
|
+
}, rowSel);
|
|
261
|
+
},
|
|
262
|
+
getVisibleColumnRange: async ({ root, config }) => {
|
|
263
|
+
const rowSel = config.rowSelector;
|
|
264
|
+
const cellSel = typeof config.cellSelector === 'string' ? config.cellSelector : '[aria-colindex]';
|
|
265
|
+
return root.evaluate((el, { rowSel, cellSel }) => {
|
|
266
|
+
const firstRow = el.querySelector(rowSel);
|
|
267
|
+
if (!firstRow)
|
|
268
|
+
return { first: 0, last: 0 };
|
|
269
|
+
const indices = Array.from(firstRow.querySelectorAll(cellSel))
|
|
270
|
+
.map(c => Number(c.getAttribute('aria-colindex')) - 1)
|
|
271
|
+
.filter(n => !isNaN(n));
|
|
272
|
+
if (!indices.length)
|
|
273
|
+
return { first: 0, last: 0 };
|
|
274
|
+
return { first: Math.min(...indices), last: Math.max(...indices) };
|
|
275
|
+
}, { rowSel, cellSel });
|
|
276
|
+
},
|
|
277
|
+
scrollToRow: async ({ root, config }, rowIndex) => {
|
|
278
|
+
const rowSel = config.rowSelector;
|
|
279
|
+
await root.evaluate((el, { rowSel, idx }) => {
|
|
280
|
+
const scroller = el.querySelector('.MuiDataGrid-virtualScroller');
|
|
281
|
+
if (!scroller)
|
|
282
|
+
return;
|
|
283
|
+
const row = el.querySelector(`${rowSel}[data-rowindex="${idx}"]`);
|
|
284
|
+
if (row) {
|
|
285
|
+
row.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const visibleRows = Array.from(el.querySelectorAll(rowSel));
|
|
289
|
+
const heights = visibleRows
|
|
290
|
+
.map(visibleRow => visibleRow.getBoundingClientRect().height)
|
|
291
|
+
.filter(height => height > 0);
|
|
292
|
+
const estimatedHeight = heights.length
|
|
293
|
+
? heights.reduce((sum, height) => sum + height, 0) / heights.length
|
|
294
|
+
: 52;
|
|
295
|
+
scroller.scrollTop = Math.max(0, idx * estimatedHeight - 20);
|
|
296
|
+
}, { rowSel, idx: rowIndex });
|
|
297
|
+
await root.locator(`${rowSel}[data-rowindex="${rowIndex}"]`)
|
|
298
|
+
.waitFor({ state: 'attached', timeout: 3000 });
|
|
299
|
+
},
|
|
300
|
+
scrollToColumn: async ({ root, config }, colIndex) => {
|
|
301
|
+
const headerSel = typeof config.headerSelector === 'string' ? config.headerSelector : null;
|
|
302
|
+
await root.evaluate((el, { headerSel, idx }) => {
|
|
303
|
+
// virtualScroller is a descendant — querySelector, not closest
|
|
304
|
+
const scroller = el.querySelector('.MuiDataGrid-virtualScroller');
|
|
305
|
+
if (!scroller || !headerSel)
|
|
306
|
+
return;
|
|
307
|
+
const headers = Array.from(el.querySelectorAll(headerSel));
|
|
308
|
+
const target = headers[idx];
|
|
309
|
+
if (!target) {
|
|
310
|
+
const widths = headers
|
|
311
|
+
.map(header => header.getBoundingClientRect().width)
|
|
312
|
+
.filter(width => width > 0);
|
|
313
|
+
const estimatedWidth = widths.length
|
|
314
|
+
? widths.reduce((sum, width) => sum + width, 0) / widths.length
|
|
315
|
+
: 100;
|
|
316
|
+
scroller.scrollLeft = Math.max(0, idx * estimatedWidth - 20);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const cRect = scroller.getBoundingClientRect();
|
|
320
|
+
const tRect = target.getBoundingClientRect();
|
|
321
|
+
if (tRect.left < cRect.left) {
|
|
322
|
+
scroller.scrollLeft -= (cRect.left - tRect.left) + 20;
|
|
323
|
+
}
|
|
324
|
+
else if (tRect.right > cRect.right) {
|
|
325
|
+
scroller.scrollLeft += (tRect.right - cRect.right) + 20;
|
|
326
|
+
}
|
|
327
|
+
}, { headerSel, idx: colIndex });
|
|
328
|
+
await root.locator(`${config.rowSelector} [aria-colindex="${colIndex + 1}"]`)
|
|
329
|
+
.first()
|
|
330
|
+
.waitFor({ state: 'attached', timeout: 3000 });
|
|
331
|
+
},
|
|
332
|
+
},
|
|
260
333
|
loading: {
|
|
261
|
-
isTableLoading: (context) =>
|
|
262
|
-
return
|
|
263
|
-
}
|
|
334
|
+
isTableLoading: async (context) => {
|
|
335
|
+
return await context.root.locator('.MuiDataGrid-overlay').isVisible().catch(() => false);
|
|
336
|
+
}
|
|
264
337
|
}
|
|
265
338
|
}
|
|
266
339
|
};
|
package/dist/presets/rdg.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare const RDGStrategies: {
|
|
|
10
10
|
goUp: ({ root, page }: any) => Promise<void>;
|
|
11
11
|
goHome: ({ root, page }: any) => Promise<void>;
|
|
12
12
|
};
|
|
13
|
-
pagination: import("
|
|
13
|
+
pagination: import("..").PaginationPrimitives;
|
|
14
14
|
};
|
|
15
15
|
export declare const RDG: Partial<TableConfig> & {
|
|
16
16
|
Strategies: typeof RDGStrategies;
|
package/dist/presets/rdg.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.RDG = exports.RDGStrategies = void 0;
|
|
13
4
|
const pagination_1 = require("../strategies/pagination");
|
|
@@ -16,43 +7,43 @@ const stabilization_1 = require("../strategies/stabilization");
|
|
|
16
7
|
* Scrolls the grid horizontally to collect all column headers.
|
|
17
8
|
* Handles empty headers by labeling them (e.g. "Checkbox").
|
|
18
9
|
*/
|
|
19
|
-
const scrollRightHeaderRDG = (context) =>
|
|
10
|
+
const scrollRightHeaderRDG = async (context) => {
|
|
20
11
|
const { resolve, config, root, page } = context;
|
|
21
12
|
const collectedHeaders = new Set();
|
|
22
|
-
const gridHandle =
|
|
13
|
+
const gridHandle = await root.evaluateHandle((el) => {
|
|
23
14
|
return el.querySelector('[role="grid"]') || el.closest('[role="grid"]');
|
|
24
15
|
});
|
|
25
|
-
const expectedColumns =
|
|
26
|
-
const getVisible = () =>
|
|
16
|
+
const expectedColumns = await gridHandle.evaluate(el => el ? parseInt(el.getAttribute('aria-colcount') || '0', 10) : 0);
|
|
17
|
+
const getVisible = async () => {
|
|
27
18
|
const headerLoc = resolve(config.headerSelector, root);
|
|
28
|
-
const texts =
|
|
19
|
+
const texts = await headerLoc.allInnerTexts();
|
|
29
20
|
return texts.map(t => {
|
|
30
21
|
const trimmed = t.trim();
|
|
31
22
|
return trimmed.length > 0 ? trimmed : 'Checkbox';
|
|
32
23
|
});
|
|
33
|
-
}
|
|
34
|
-
let currentHeaders =
|
|
24
|
+
};
|
|
25
|
+
let currentHeaders = await getVisible();
|
|
35
26
|
currentHeaders.forEach(h => collectedHeaders.add(h));
|
|
36
|
-
const hasScroll =
|
|
27
|
+
const hasScroll = await gridHandle.evaluate(el => el ? el.scrollWidth > el.clientWidth : false);
|
|
37
28
|
if (hasScroll) {
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
await gridHandle.evaluate(el => el.scrollLeft = 0);
|
|
30
|
+
await page.waitForTimeout(200);
|
|
40
31
|
let iteration = 0;
|
|
41
32
|
while (collectedHeaders.size < expectedColumns && iteration < 30) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const newHeaders =
|
|
33
|
+
await gridHandle.evaluate(el => el.scrollLeft += 500);
|
|
34
|
+
await page.waitForTimeout(300);
|
|
35
|
+
const newHeaders = await getVisible();
|
|
45
36
|
newHeaders.forEach(h => collectedHeaders.add(h));
|
|
46
|
-
const atEnd =
|
|
37
|
+
const atEnd = await gridHandle.evaluate(el => el.scrollLeft >= el.scrollWidth - el.clientWidth - 10);
|
|
47
38
|
iteration++;
|
|
48
39
|
if (atEnd)
|
|
49
40
|
break;
|
|
50
41
|
}
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
await gridHandle.evaluate(el => el.scrollLeft = 0);
|
|
43
|
+
await page.waitForTimeout(200);
|
|
53
44
|
}
|
|
54
45
|
return Array.from(collectedHeaders);
|
|
55
|
-
}
|
|
46
|
+
};
|
|
56
47
|
const rdgGetCellLocator = ({ row, columnIndex }) => {
|
|
57
48
|
const ariaColIndex = columnIndex + 1;
|
|
58
49
|
return row.locator(`[role="gridcell"][aria-colindex="${ariaColIndex}"]`);
|
|
@@ -63,43 +54,43 @@ const rdgPaginationStrategy = pagination_1.PaginationStrategies.infiniteScroll({
|
|
|
63
54
|
stabilization: stabilization_1.StabilizationStrategies.contentChanged({ timeout: 5000 })
|
|
64
55
|
});
|
|
65
56
|
const rdgNavigation = {
|
|
66
|
-
goRight:
|
|
67
|
-
|
|
57
|
+
goRight: async ({ root, page }) => {
|
|
58
|
+
await root.evaluate((el) => {
|
|
68
59
|
const grid = el.querySelector('[role="grid"]') || el.closest('[role="grid"]') || el;
|
|
69
60
|
if (grid)
|
|
70
61
|
grid.scrollLeft += 150;
|
|
71
62
|
});
|
|
72
|
-
}
|
|
73
|
-
goLeft:
|
|
74
|
-
|
|
63
|
+
},
|
|
64
|
+
goLeft: async ({ root, page }) => {
|
|
65
|
+
await root.evaluate((el) => {
|
|
75
66
|
const grid = el.querySelector('[role="grid"]') || el.closest('[role="grid"]') || el;
|
|
76
67
|
if (grid)
|
|
77
68
|
grid.scrollLeft -= 150;
|
|
78
69
|
});
|
|
79
|
-
}
|
|
80
|
-
goDown:
|
|
81
|
-
|
|
70
|
+
},
|
|
71
|
+
goDown: async ({ root, page }) => {
|
|
72
|
+
await root.evaluate((el) => {
|
|
82
73
|
const grid = el.querySelector('[role="grid"]') || el.closest('[role="grid"]') || el;
|
|
83
74
|
if (grid)
|
|
84
75
|
grid.scrollTop += 35;
|
|
85
76
|
});
|
|
86
|
-
}
|
|
87
|
-
goUp:
|
|
88
|
-
|
|
77
|
+
},
|
|
78
|
+
goUp: async ({ root, page }) => {
|
|
79
|
+
await root.evaluate((el) => {
|
|
89
80
|
const grid = el.querySelector('[role="grid"]') || el.closest('[role="grid"]') || el;
|
|
90
81
|
if (grid)
|
|
91
82
|
grid.scrollTop -= 35;
|
|
92
83
|
});
|
|
93
|
-
}
|
|
94
|
-
goHome:
|
|
95
|
-
|
|
84
|
+
},
|
|
85
|
+
goHome: async ({ root, page }) => {
|
|
86
|
+
await root.evaluate((el) => {
|
|
96
87
|
const grid = el.querySelector('[role="grid"]') || el.closest('[role="grid"]') || el;
|
|
97
88
|
if (grid) {
|
|
98
89
|
grid.scrollLeft = 0;
|
|
99
90
|
grid.scrollTop = 0;
|
|
100
91
|
}
|
|
101
92
|
});
|
|
102
|
-
}
|
|
93
|
+
}
|
|
103
94
|
};
|
|
104
95
|
/** Default strategies for the RDG preset (used when you spread Plugins.RDG). */
|
|
105
96
|
const RDGDefaultStrategies = {
|