@rickcedwhat/playwright-smart-table 6.9.0 → 6.10.1

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.
Files changed (42) hide show
  1. package/README.md +9 -6
  2. package/dist/engine/rowFinder.js +153 -168
  3. package/dist/engine/tableIteration.js +105 -120
  4. package/dist/engine/tableMapper.js +88 -103
  5. package/dist/minimalConfigContext.d.ts +1 -1
  6. package/dist/minimalConfigContext.js +1 -1
  7. package/dist/packageVersion.d.ts +1 -1
  8. package/dist/packageVersion.js +1 -1
  9. package/dist/presets/glide/columns.js +23 -32
  10. package/dist/presets/glide/headers.js +18 -27
  11. package/dist/presets/glide/index.d.ts +9 -9
  12. package/dist/presets/glide/index.js +24 -33
  13. package/dist/presets/mui.js +197 -124
  14. package/dist/presets/rdg.d.ts +1 -1
  15. package/dist/presets/rdg.js +32 -41
  16. package/dist/smartRow.js +209 -101
  17. package/dist/strategies/columns.js +2 -11
  18. package/dist/strategies/dedupe.js +3 -12
  19. package/dist/strategies/fill.js +20 -29
  20. package/dist/strategies/headers.js +22 -31
  21. package/dist/strategies/index.d.ts +7 -0
  22. package/dist/strategies/index.js +3 -0
  23. package/dist/strategies/loading.js +24 -33
  24. package/dist/strategies/pagination.d.ts +10 -0
  25. package/dist/strategies/pagination.js +49 -30
  26. package/dist/strategies/sorting.js +18 -31
  27. package/dist/strategies/stabilization.js +28 -37
  28. package/dist/strategies/viewport.d.ts +52 -0
  29. package/dist/strategies/viewport.js +123 -0
  30. package/dist/typeContext.d.ts +1 -1
  31. package/dist/typeContext.js +160 -5
  32. package/dist/types.d.ts +153 -5
  33. package/dist/useTable.js +142 -76
  34. package/dist/utils/debugUtils.js +5 -16
  35. package/dist/utils/elementTracker.js +27 -40
  36. package/dist/utils/mutex.js +5 -16
  37. package/dist/utils/navigationBarrier.js +25 -36
  38. package/dist/utils/paginationPath.d.ts +4 -1
  39. package/dist/utils/paginationPath.js +164 -141
  40. package/dist/utils/smartRowArray.js +2 -11
  41. package/dist/utils.js +4 -13
  42. package/package.json +13 -8
@@ -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
- return __awaiter(this, void 0, void 0, function* () {
19
- var _a;
20
- let retries = 50; // 5 second timeout for client-side render
21
- while (oldText && retries-- > 0 && (yield displayedRows.innerText().catch(() => '')) === oldText) {
22
- yield context.page.waitForTimeout(100);
23
- }
24
- // Standard MUI loading strategy check
25
- if ((_a = context.config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading) {
26
- let loadingRetries = 30;
27
- while (loadingRetries-- > 0 && (yield context.config.strategies.loading.isTableLoading(context))) {
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
- // DataGrid specific fallback (if not using loading strategy)
32
- const overlay = context.root.locator('.MuiDataGrid-overlay');
33
- if ((yield overlay.count()) > 0) {
34
- let loadingRetries = 30;
35
- while (loadingRetries-- > 0 && (yield overlay.isVisible().catch(() => false))) {
36
- yield context.page.waitForTimeout(100);
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
- return __awaiter(this, void 0, void 0, function* () {
44
- // 1. If the user initialized useTable on the layout wrapper itself
45
- const inside = context.root.locator('.MuiTablePagination-root').first();
46
- if ((yield inside.count()) > 0)
47
- return inside;
48
- // 2. If the user initialized on the Table, look up to the closest Paper wrapper
49
- const paper = context.root.locator('xpath=ancestor::*[contains(@class, "MuiPaper-root")][1]').first();
50
- if ((yield paper.count()) > 0) {
51
- const inPaper = paper.locator('.MuiTablePagination-root').first();
52
- if ((yield inPaper.count()) > 0)
53
- return inPaper;
54
- }
55
- // 3. Fallback: find the first pagination footer that follows this context in the DOM
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(/\s*sorted\s+(a|de)scending/i, '').trim(),
59
+ headerTransformer: ({ text }) => text.replace(/\bsorted\s+(?:ascending|descending)\b/i, '').trim(),
73
60
  strategies: {
74
61
  pagination: {
75
- goNext: (context) => __awaiter(void 0, void 0, void 0, function* () {
76
- const root = yield getPaginationRoot(context);
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 ((yield nextBtn.count()) === 0 || (yield nextBtn.isDisabled()))
65
+ if (await nextBtn.count() === 0 || await nextBtn.isDisabled())
79
66
  return false;
80
67
  const displayedRows = root.locator('.MuiTablePagination-displayedRows');
81
- const oldText = yield displayedRows.innerText().catch(() => '');
68
+ const oldText = await displayedRows.innerText().catch(() => '');
82
69
  // Force click ignores cookie banners obscuring the footer
83
- yield nextBtn.click({ force: true });
84
- yield waitForMuiPaginationStabilization(context, displayedRows, oldText);
70
+ await nextBtn.click({ force: true });
71
+ await waitForMuiPaginationStabilization(context, displayedRows, oldText);
85
72
  return true;
86
- }),
87
- goPrevious: (context) => __awaiter(void 0, void 0, void 0, function* () {
88
- const root = yield getPaginationRoot(context);
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 ((yield prevBtn.count()) === 0 || (yield prevBtn.isDisabled()))
77
+ if (await prevBtn.count() === 0 || await prevBtn.isDisabled())
91
78
  return false;
92
79
  const displayedRows = root.locator('.MuiTablePagination-displayedRows');
93
- const oldText = yield displayedRows.innerText().catch(() => '');
94
- yield prevBtn.click({ force: true });
95
- yield waitForMuiPaginationStabilization(context, displayedRows, oldText);
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) => __awaiter(void 0, void 0, void 0, function* () {
99
- const root = yield getPaginationRoot(context);
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 && (yield prevBtn.count()) > 0 && !(yield prevBtn.isDisabled())) {
105
- const oldText = yield displayedRows.innerText().catch(() => '');
106
- yield prevBtn.click();
107
- yield waitForMuiPaginationStabilization(context, displayedRows, oldText);
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: (_a) => __awaiter(void 0, [_a], void 0, function* ({ columnName, context }) {
101
+ getSortState: async ({ columnName, context }) => {
115
102
  if (!context.getHeaderCell)
116
103
  return 'none';
117
- const header = yield context.getHeaderCell(columnName);
104
+ const header = await context.getHeaderCell(columnName);
118
105
  const sortLabel = header.locator('.MuiTableSortLabel-root').first();
119
- if (!(yield sortLabel.isVisible().catch(() => false)))
106
+ if (!(await sortLabel.isVisible().catch(() => false)))
120
107
  return 'none';
121
- const className = (yield sortLabel.getAttribute('class')) || '';
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: (_a) => __awaiter(void 0, [_a], void 0, function* ({ columnName, direction, context }) {
132
- var _b, _c;
117
+ },
118
+ doSort: async ({ columnName, direction, context }) => {
119
+ var _a, _b;
133
120
  if (!context.getHeaderCell)
134
121
  return;
135
- const header = yield context.getHeaderCell(columnName);
122
+ const header = await context.getHeaderCell(columnName);
136
123
  const sortLabel = header.locator('.MuiTableSortLabel-root').first();
137
- const target = (yield sortLabel.isVisible().catch(() => false)) ? sortLabel : header;
138
- let current = (yield ((_b = context.config.strategies.sorting) === null || _b === void 0 ? void 0 : _b.getSortState({ columnName, context }))) || 'none';
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
- yield target.click();
143
- yield context.page.waitForTimeout(100);
144
- current = (yield ((_c = context.config.strategies.sorting) === null || _c === void 0 ? void 0 : _c.getSortState({ columnName, context }))) || 'none';
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) => __awaiter(void 0, void 0, void 0, function* () {
136
+ dedupe: async (row) => {
150
137
  // MUI virtualized tables attach data-index to rows
151
- const dataIndex = yield row.getAttribute('data-index');
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 = yield row.locator('td, th').first().innerText().catch(() => '');
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) => __awaiter(void 0, void 0, void 0, function* () {
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
- yield nextBtn.waitFor({ state: 'visible', timeout: 2000 });
167
+ await nextBtn.waitFor({ state: 'visible', timeout: 2000 });
181
168
  }
182
169
  catch (e) {
183
170
  return false;
184
171
  }
185
- if (yield nextBtn.isDisabled())
172
+ if (await nextBtn.isDisabled())
186
173
  return false;
187
174
  const displayedRows = footer.locator('.MuiTablePagination-displayedRows');
188
- const oldText = yield displayedRows.innerText().catch(() => '');
189
- yield nextBtn.scrollIntoViewIfNeeded().catch(() => { });
190
- yield nextBtn.click({ force: true });
191
- yield waitForMuiPaginationStabilization(context, displayedRows, oldText);
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) => __awaiter(void 0, void 0, void 0, function* () {
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
- yield prevBtn.waitFor({ state: 'visible', timeout: 2000 });
185
+ await prevBtn.waitFor({ state: 'visible', timeout: 2000 });
199
186
  }
200
187
  catch (e) {
201
188
  return false;
202
189
  }
203
- if (yield prevBtn.isDisabled())
190
+ if (await prevBtn.isDisabled())
204
191
  return false;
205
192
  const displayedRows = footer.locator('.MuiTablePagination-displayedRows');
206
- const oldText = yield displayedRows.innerText().catch(() => '');
207
- yield prevBtn.scrollIntoViewIfNeeded().catch(() => { });
208
- yield prevBtn.click({ force: true });
209
- yield waitForMuiPaginationStabilization(context, displayedRows, oldText);
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: (_a) => __awaiter(void 0, [_a], void 0, function* ({ columnName, context }) {
201
+ getSortState: async ({ columnName, context }) => {
215
202
  if (!context.getHeaderCell)
216
203
  return 'none';
217
- const header = yield context.getHeaderCell(columnName);
218
- const sortAttr = yield header.getAttribute('aria-sort');
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: (_a) => __awaiter(void 0, [_a], void 0, function* ({ columnName, direction, context }) {
226
- var _b, _c;
211
+ },
212
+ doSort: async ({ columnName, direction, context }) => {
213
+ var _a, _b;
227
214
  if (!context.getHeaderCell)
228
215
  return;
229
- const header = yield context.getHeaderCell(columnName);
230
- let current = (yield ((_b = context.config.strategies.sorting) === null || _b === void 0 ? void 0 : _b.getSortState({ columnName, context }))) || 'none';
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 (yield clickTarget.isVisible()) {
235
- yield clickTarget.click({ force: true });
221
+ if (await clickTarget.isVisible()) {
222
+ await clickTarget.click({ force: true });
236
223
  }
237
224
  else {
238
- yield header.click({ force: true });
225
+ await header.click({ force: true });
239
226
  }
240
227
  // Sorting can trigger large re-renders/virtualization shifts
241
- yield context.page.waitForTimeout(500);
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 = yield displayedRows.innerText().catch(() => '');
245
- yield waitForMuiPaginationStabilization(context, displayedRows, text);
246
- current = (yield ((_c = context.config.strategies.sorting) === null || _c === void 0 ? void 0 : _c.getSortState({ columnName, context }))) || 'none';
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) => __awaiter(void 0, void 0, void 0, function* () {
238
+ dedupe: async (row) => {
252
239
  var _a;
253
240
  // DataGrid uses data-rowindex for its own virtualization management
254
- return (_a = (yield row.getAttribute('data-rowindex').catch(() => null))) !== null && _a !== void 0 ? _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) => __awaiter(void 0, void 0, void 0, function* () {
262
- return yield context.root.locator('.MuiDataGrid-overlay').isVisible().catch(() => false);
263
- })
334
+ isTableLoading: async (context) => {
335
+ return await context.root.locator('.MuiDataGrid-overlay').isVisible().catch(() => false);
336
+ }
264
337
  }
265
338
  }
266
339
  };
@@ -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("../types").PaginationPrimitives;
13
+ pagination: import("..").PaginationPrimitives;
14
14
  };
15
15
  export declare const RDG: Partial<TableConfig> & {
16
16
  Strategies: typeof RDGStrategies;
@@ -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) => __awaiter(void 0, void 0, void 0, function* () {
10
+ const scrollRightHeaderRDG = async (context) => {
20
11
  const { resolve, config, root, page } = context;
21
12
  const collectedHeaders = new Set();
22
- const gridHandle = yield root.evaluateHandle((el) => {
13
+ const gridHandle = await root.evaluateHandle((el) => {
23
14
  return el.querySelector('[role="grid"]') || el.closest('[role="grid"]');
24
15
  });
25
- const expectedColumns = yield gridHandle.evaluate(el => el ? parseInt(el.getAttribute('aria-colcount') || '0', 10) : 0);
26
- const getVisible = () => __awaiter(void 0, void 0, void 0, function* () {
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 = yield headerLoc.allInnerTexts();
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 = yield getVisible();
24
+ };
25
+ let currentHeaders = await getVisible();
35
26
  currentHeaders.forEach(h => collectedHeaders.add(h));
36
- const hasScroll = yield gridHandle.evaluate(el => el ? el.scrollWidth > el.clientWidth : false);
27
+ const hasScroll = await gridHandle.evaluate(el => el ? el.scrollWidth > el.clientWidth : false);
37
28
  if (hasScroll) {
38
- yield gridHandle.evaluate(el => el.scrollLeft = 0);
39
- yield page.waitForTimeout(200);
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
- yield gridHandle.evaluate(el => el.scrollLeft += 500);
43
- yield page.waitForTimeout(300);
44
- const newHeaders = yield getVisible();
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 = yield gridHandle.evaluate(el => el.scrollLeft >= el.scrollWidth - el.clientWidth - 10);
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
- yield gridHandle.evaluate(el => el.scrollLeft = 0);
52
- yield page.waitForTimeout(200);
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: (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, page }) {
67
- yield root.evaluate((el) => {
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: (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, page }) {
74
- yield root.evaluate((el) => {
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: (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, page }) {
81
- yield root.evaluate((el) => {
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: (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, page }) {
88
- yield root.evaluate((el) => {
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: (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, page }) {
95
- yield root.evaluate((el) => {
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 = {