@rickcedwhat/playwright-smart-table 6.15.0 → 6.17.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.
@@ -3,6 +3,7 @@ import { FinalTableConfig, Selector, SmartRow, FilterValue } from '../types';
3
3
  import { FilterEngine } from '../filterEngine';
4
4
  import { TableMapper } from './tableMapper';
5
5
  import { SmartRowArray } from '../utils/smartRowArray';
6
+ import { NavigationBarrier } from '../utils/navigationBarrier';
6
7
  export declare class RowFinder<T = any> {
7
8
  private rootLocator;
8
9
  private config;
@@ -10,10 +11,11 @@ export declare class RowFinder<T = any> {
10
11
  private tableMapper;
11
12
  private makeSmartRow;
12
13
  private tableState;
14
+ private advancePage;
13
15
  private resolve;
14
- constructor(rootLocator: Locator, config: FinalTableConfig, resolve: (item: Selector, parent: Locator | Page) => Locator, filterEngine: FilterEngine, tableMapper: TableMapper, makeSmartRow: (loc: Locator, map: Map<string, number>, index: number | undefined, tablePageIndex?: number) => SmartRow<T>, tableState?: {
16
+ constructor(rootLocator: Locator, config: FinalTableConfig, resolve: (item: Selector, parent: Locator | Page) => Locator, filterEngine: FilterEngine, tableMapper: TableMapper, makeSmartRow: (loc: Locator, map: Map<string, number>, index: number | undefined, tablePageIndex?: number, barrier?: NavigationBarrier) => SmartRow<T>, tableState?: {
15
17
  currentPageIndex: number;
16
- });
18
+ }, advancePage?: (useBulk: boolean) => Promise<boolean>);
17
19
  findRow(filters: Record<string, FilterValue>, options?: {
18
20
  exact?: boolean;
19
21
  maxPages?: number;
@@ -3,17 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RowFinder = void 0;
4
4
  const debugUtils_1 = require("../utils/debugUtils");
5
5
  const smartRowArray_1 = require("../utils/smartRowArray");
6
- const validation_1 = require("../strategies/validation");
7
6
  const elementTracker_1 = require("../utils/elementTracker");
8
7
  const sentinel_1 = require("../utils/sentinel");
8
+ const navigationBarrier_1 = require("../utils/navigationBarrier");
9
9
  class RowFinder {
10
- constructor(rootLocator, config, resolve, filterEngine, tableMapper, makeSmartRow, tableState = { currentPageIndex: 0 }) {
10
+ constructor(rootLocator, config, resolve, filterEngine, tableMapper, makeSmartRow, tableState = { currentPageIndex: 0 }, advancePage = async () => false) {
11
11
  this.rootLocator = rootLocator;
12
12
  this.config = config;
13
13
  this.filterEngine = filterEngine;
14
14
  this.tableMapper = tableMapper;
15
15
  this.makeSmartRow = makeSmartRow;
16
16
  this.tableState = tableState;
17
+ this.advancePage = advancePage;
17
18
  this.resolve = resolve;
18
19
  }
19
20
  async findRow(filters, options = {}) {
@@ -31,12 +32,12 @@ class RowFinder {
31
32
  await (0, debugUtils_1.debugDelay)(this.config, 'findRow');
32
33
  const sentinel = this.resolve(this.config.rowSelector, this.rootLocator)
33
34
  .filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
34
- const smartRow = this.makeSmartRow(sentinel, await this.tableMapper.getMap(), 0);
35
+ const smartRow = this.makeSmartRow(sentinel, await this.tableMapper.getMap(), undefined);
35
36
  smartRow[sentinel_1.SENTINEL_ROW] = true;
36
37
  return smartRow;
37
38
  }
38
39
  async findRows(filters = {}, options) {
39
- var _a, _b, _c, _d;
40
+ var _a, _b, _c;
40
41
  const filtersRecord = filters;
41
42
  const map = await this.tableMapper.getMap();
42
43
  const allRows = [];
@@ -64,12 +65,16 @@ class RowFinder {
64
65
  : undefined;
65
66
  const onRowLoadingTimeout = (_e = (_d = this.config.strategies.loading) === null || _d === void 0 ? void 0 : _d.onRowLoadingTimeout) !== null && _e !== void 0 ? _e : 'read-as-is';
66
67
  let added = 0;
68
+ // One barrier per batch — synchronizes cell navigation across all rows in this page's results
69
+ const useBarrier = this.config.concurrency === 'synchronized' && newIndices.length > 1;
70
+ const barrier = useBarrier ? new navigationBarrier_1.NavigationBarrier(newIndices.length) : undefined;
67
71
  for (const idx of newIndices) {
68
- const smartRow = this.makeSmartRow(currentRows[idx], map, allRows.length, this.tableState.currentPageIndex);
72
+ const smartRow = this.makeSmartRow(currentRows[idx], map, allRows.length, this.tableState.currentPageIndex, barrier);
69
73
  if (isRowLoading && await isRowLoading(smartRow)) {
70
74
  if (rowLoadingTimeout === undefined) {
71
75
  // No timeout configured (or invalid value) — legacy skip behavior
72
76
  (0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: page ${this.tableState.currentPageIndex} — row skipped (isRowLoading=true, no timeout)`);
77
+ barrier === null || barrier === void 0 ? void 0 : barrier.markFinished();
73
78
  continue;
74
79
  }
75
80
  // Wait up to rowLoadingTimeout ms (0 = one immediate re-check, no polling)
@@ -82,9 +87,12 @@ class RowFinder {
82
87
  }
83
88
  if (!resolved) {
84
89
  (0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: row ${allRows.length} — still loading after ${rowLoadingTimeout}ms, action: ${onRowLoadingTimeout}`);
85
- if (onRowLoadingTimeout === 'skip')
90
+ if (onRowLoadingTimeout === 'skip') {
91
+ barrier === null || barrier === void 0 ? void 0 : barrier.markFinished();
86
92
  continue;
93
+ }
87
94
  if (onRowLoadingTimeout === 'throw') {
95
+ barrier === null || barrier === void 0 ? void 0 : barrier.markFinished();
88
96
  throw new Error(`[SmartTable] Row ${allRows.length} did not finish loading within ${rowLoadingTimeout}ms`);
89
97
  }
90
98
  // 'read-as-is': fall through and add the row
@@ -102,31 +110,14 @@ class RowFinder {
102
110
  await collectMatches();
103
111
  // Pagination Loop
104
112
  while (pagesScanned < effectiveMaxPages && this.config.strategies.pagination) {
105
- const context = {
106
- root: this.rootLocator,
107
- config: this.config,
108
- resolve: this.resolve,
109
- page: this.rootLocator.page()
110
- };
111
- let paginationResult;
112
113
  const useBulk = (options === null || options === void 0 ? void 0 : options.useBulkPagination) !== false && !!((_c = this.config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.goNextBulk);
113
- if (useBulk) {
114
- paginationResult = await this.config.strategies.pagination.goNextBulk(context);
115
- }
116
- else if ((_d = this.config.strategies.pagination) === null || _d === void 0 ? void 0 : _d.goNext) {
117
- paginationResult = await this.config.strategies.pagination.goNext(context);
118
- }
119
- else {
120
- (0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: no pagination primitive — stopping`);
121
- break;
122
- }
123
- const didPaginate = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
114
+ const prevPage = this.tableState.currentPageIndex;
115
+ const didPaginate = await this.advancePage(useBulk);
124
116
  if (!didPaginate) {
125
117
  (0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: pagination returned false — end of data`);
126
118
  break;
127
119
  }
128
- const pagesJumped = typeof paginationResult === 'number' ? paginationResult : 1;
129
- this.tableState.currentPageIndex += pagesJumped;
120
+ const pagesJumped = this.tableState.currentPageIndex - prevPage;
130
121
  pagesScanned += pagesJumped;
131
122
  (0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: advanced ${pagesJumped} page(s), now at page ${this.tableState.currentPageIndex}`);
132
123
  await (0, debugUtils_1.debugDelay)(this.config, 'pagination');
@@ -140,7 +131,7 @@ class RowFinder {
140
131
  return (0, smartRowArray_1.createSmartRowArray)(allRows);
141
132
  }
142
133
  async findRowLocator(filters, options = {}) {
143
- var _a, _b;
134
+ var _a, _b, _c;
144
135
  const map = await this.tableMapper.getMap();
145
136
  const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : this.config.maxPages;
146
137
  let pagesScanned = 1;
@@ -183,29 +174,11 @@ class RowFinder {
183
174
  return matchedRows.first();
184
175
  if (pagesScanned < effectiveMaxPages) {
185
176
  (0, debugUtils_1.logDebug)(this.config, 'verbose', `Page ${this.tableState.currentPageIndex}: Not found. Attempting pagination...`);
186
- const context = {
187
- root: this.rootLocator,
188
- config: this.config,
189
- resolve: this.resolve,
190
- page: this.rootLocator.page()
191
- };
192
- let paginationResult;
193
- const pagination = this.config.strategies.pagination;
194
- const useBulk = options.useBulkPagination !== false && !!(pagination === null || pagination === void 0 ? void 0 : pagination.goNextBulk);
195
- if (useBulk && (pagination === null || pagination === void 0 ? void 0 : pagination.goNextBulk)) {
196
- paginationResult = await pagination.goNextBulk(context);
197
- }
198
- else if (pagination === null || pagination === void 0 ? void 0 : pagination.goNext) {
199
- paginationResult = await pagination.goNext(context);
200
- }
201
- else {
202
- (0, debugUtils_1.logDebug)(this.config, 'verbose', `Page ${this.tableState.currentPageIndex}: Pagination failed (no goNext or goNextBulk primitive).`);
203
- return null;
204
- }
205
- const didLoadMore = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
177
+ const useBulk = options.useBulkPagination !== false && !!((_c = this.config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.goNextBulk);
178
+ const prevPage = this.tableState.currentPageIndex;
179
+ const didLoadMore = await this.advancePage(useBulk);
206
180
  if (didLoadMore) {
207
- const pagesJumped = typeof paginationResult === 'number' ? paginationResult : 1;
208
- this.tableState.currentPageIndex += pagesJumped;
181
+ const pagesJumped = this.tableState.currentPageIndex - prevPage;
209
182
  pagesScanned += pagesJumped;
210
183
  (0, debugUtils_1.logDebug)(this.config, 'verbose', `findRowLocator: advanced ${pagesJumped} page(s), now at page ${this.tableState.currentPageIndex}`);
211
184
  await (0, debugUtils_1.debugDelay)(this.config, 'pagination');
package/dist/index.d.ts CHANGED
@@ -5,3 +5,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. */
7
7
  export { Plugins } from './plugins';
8
+ export { mergeTableConfig } from './utils/mergeTableConfig';
package/dist/index.js CHANGED
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.Plugins = exports.presets = exports.Strategies = exports.useTable = exports.PLAYWRIGHT_SMART_TABLE_VERSION = void 0;
36
+ exports.mergeTableConfig = exports.Plugins = exports.presets = exports.Strategies = exports.useTable = exports.PLAYWRIGHT_SMART_TABLE_VERSION = void 0;
37
37
  var packageVersion_1 = require("./packageVersion");
38
38
  Object.defineProperty(exports, "PLAYWRIGHT_SMART_TABLE_VERSION", { enumerable: true, get: function () { return packageVersion_1.PLAYWRIGHT_SMART_TABLE_VERSION; } });
39
39
  var useTable_1 = require("./useTable");
@@ -45,3 +45,5 @@ exports.presets = __importStar(require("./presets"));
45
45
  /** @deprecated Use `presets` instead. Plugins will be removed in v7.0.0. */
46
46
  var plugins_1 = require("./plugins");
47
47
  Object.defineProperty(exports, "Plugins", { enumerable: true, get: function () { return plugins_1.Plugins; } });
48
+ var mergeTableConfig_1 = require("./utils/mergeTableConfig");
49
+ Object.defineProperty(exports, "mergeTableConfig", { enumerable: true, get: function () { return mergeTableConfig_1.mergeTableConfig; } });
@@ -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.15.0";
6
+ export declare const PLAYWRIGHT_SMART_TABLE_VERSION: "6.17.0";
@@ -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.15.0";
9
+ exports.PLAYWRIGHT_SMART_TABLE_VERSION = "6.17.0";
@@ -36,3 +36,4 @@ export declare function createGlide(options?: GlideOptions): Partial<TableConfig
36
36
  export declare const Glide: Partial<TableConfig> & {
37
37
  Strategies: typeof GlideStrategies;
38
38
  };
39
+ export { Glide as glide };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Glide = exports.GlideStrategies = exports.createGlideViewport = void 0;
3
+ exports.glide = exports.Glide = exports.GlideStrategies = exports.createGlideViewport = void 0;
4
4
  exports.createGlide = createGlide;
5
5
  const headers_1 = require("./headers");
6
6
  const pagination_1 = require("../../strategies/pagination");
@@ -75,9 +75,15 @@ const glideGetActiveCell = async ({ page }) => {
75
75
  let rowIndex = -1;
76
76
  let columnIndex = -1;
77
77
  if (parts.length >= 4 && parts[0] === 'glide' && parts[1] === 'cell') {
78
- columnIndex = parseInt(parts[2]) - 1;
78
+ columnIndex = parseInt(parts[2]); // 0-based in Glide's id format: glide-cell-{col}-{row}
79
79
  rowIndex = parseInt(parts[3]);
80
80
  }
81
+ else if (id !== '') {
82
+ throw new Error(`Glide preset: focused element has id "${id}" which does not match the expected format "glide-cell-{colIndex}-{rowIndex}". ` +
83
+ `Glide may have changed its internal id format. Please open an issue at https://github.com/rickcedwhat/playwright-smart-table/issues.`);
84
+ }
85
+ if (rowIndex === -1 || columnIndex === -1)
86
+ return null;
81
87
  return {
82
88
  rowIndex,
83
89
  columnIndex,
@@ -129,3 +135,4 @@ const GlidePreset = {
129
135
  strategies: GlideDefaultStrategies
130
136
  };
131
137
  exports.Glide = Object.defineProperty(GlidePreset, 'Strategies', { get: () => exports.GlideStrategies, enumerable: false });
138
+ exports.glide = exports.Glide;
@@ -1,4 +1,5 @@
1
- export { muiTable, muiDataGrid } from './mui';
2
- export { RDG as rdg } from './rdg';
1
+ export { muiTable, muiDataGrid, createMuiTable, createMuiDataGrid } from './mui';
2
+ export type { MuiButtonLabels } from './mui';
3
+ export { RDG as rdg, RDG2D as rdg2D } from './rdg';
3
4
  export { Glide as glide, createGlide, createGlideViewport } from './glide';
4
5
  export type { GlideOptions, GlideViewportOptions } from './glide';
@@ -1,11 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createGlideViewport = exports.createGlide = exports.glide = exports.rdg = exports.muiDataGrid = exports.muiTable = void 0;
3
+ exports.createGlideViewport = exports.createGlide = exports.glide = exports.rdg2D = exports.rdg = exports.createMuiDataGrid = exports.createMuiTable = exports.muiDataGrid = exports.muiTable = void 0;
4
4
  var mui_1 = require("./mui");
5
5
  Object.defineProperty(exports, "muiTable", { enumerable: true, get: function () { return mui_1.muiTable; } });
6
6
  Object.defineProperty(exports, "muiDataGrid", { enumerable: true, get: function () { return mui_1.muiDataGrid; } });
7
+ Object.defineProperty(exports, "createMuiTable", { enumerable: true, get: function () { return mui_1.createMuiTable; } });
8
+ Object.defineProperty(exports, "createMuiDataGrid", { enumerable: true, get: function () { return mui_1.createMuiDataGrid; } });
7
9
  var rdg_1 = require("./rdg");
8
10
  Object.defineProperty(exports, "rdg", { enumerable: true, get: function () { return rdg_1.RDG; } });
11
+ Object.defineProperty(exports, "rdg2D", { enumerable: true, get: function () { return rdg_1.RDG2D; } });
9
12
  var glide_1 = require("./glide");
10
13
  Object.defineProperty(exports, "glide", { enumerable: true, get: function () { return glide_1.Glide; } });
11
14
  Object.defineProperty(exports, "createGlide", { enumerable: true, get: function () { return glide_1.createGlide; } });
@@ -1,16 +1,28 @@
1
1
  import { TableConfig } from '../types';
2
+ /** Aria-labels for MUI pagination buttons. Override for non-English locales. */
3
+ export interface MuiButtonLabels {
4
+ nextPage?: string;
5
+ previousPage?: string;
6
+ firstPage?: string;
7
+ }
2
8
  /**
3
- * Preset configuration for the standard Material UI `<Table />` component.
9
+ * Creates a preset configuration for the standard Material UI `<Table />` component.
4
10
  *
5
- * Handles:
6
- * - Selectors for `.MuiTableRow-root`, `.MuiTableCell-head`, etc.
7
- * - Pagination via the standard `.MuiTablePagination-actions` footer.
8
- * - Sorting via the `.MuiTableSortLabel-root` header classes.
9
- * - Virtualized DOM deduplication (falls back to cell text if unvirtualized).
11
+ * @param opts.buttonLabels - Override aria-labels for pagination buttons (use for non-English locales).
12
+ *
13
+ * @example
14
+ * // French locale
15
+ * const table = useTable(loc, createMuiTable({ buttonLabels: { nextPage: 'Page suivante', previousPage: 'Page précédente' } }));
10
16
  */
17
+ export declare function createMuiTable(opts?: {
18
+ buttonLabels?: MuiButtonLabels;
19
+ }): Partial<TableConfig>;
20
+ /** Default preset for MUI `<Table />`. For non-English locales use `createMuiTable({ buttonLabels })`. */
11
21
  export declare const muiTable: Partial<TableConfig>;
12
22
  /**
13
- * Preset configuration for the MUI DataGrid component (@mui/x-data-grid).
23
+ * Creates a preset configuration for the MUI DataGrid component (@mui/x-data-grid).
24
+ *
25
+ * @param opts.buttonLabels - Override aria-labels for pagination buttons (use for non-English locales).
14
26
  *
15
27
  * Handles:
16
28
  * - Selectors for `.MuiDataGrid-row`, `.MuiDataGrid-cell`, and `.MuiDataGrid-columnHeader`.
@@ -19,4 +31,8 @@ export declare const muiTable: Partial<TableConfig>;
19
31
  * - Vertical Virtualization deduplication using `data-rowindex`.
20
32
  * - Horizontal Virtualization support via `aria-colindex`.
21
33
  */
34
+ export declare function createMuiDataGrid(opts?: {
35
+ buttonLabels?: MuiButtonLabels;
36
+ }): Partial<TableConfig>;
37
+ /** Default preset for MUI DataGrid. For non-English locales use `createMuiDataGrid({ buttonLabels })`. */
22
38
  export declare const muiDataGrid: Partial<TableConfig>;