@rickcedwhat/playwright-smart-table 6.7.4 → 6.7.6

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 (37) hide show
  1. package/README.md +7 -3
  2. package/dist/engine/rowFinder.d.ts +1 -1
  3. package/dist/engine/rowFinder.js +7 -6
  4. package/dist/engine/tableIteration.d.ts +25 -0
  5. package/dist/engine/tableIteration.js +210 -0
  6. package/dist/filterEngine.d.ts +5 -1
  7. package/dist/filterEngine.js +23 -13
  8. package/dist/index.d.ts +1 -1
  9. package/dist/{strategies → plugins}/glide/columns.js +2 -5
  10. package/dist/{strategies → plugins}/glide/headers.js +0 -3
  11. package/dist/plugins/glide/index.d.ts +31 -0
  12. package/dist/{strategies/glide.js → plugins/glide/index.js} +29 -48
  13. package/dist/plugins/index.d.ts +16 -0
  14. package/dist/plugins/index.js +16 -0
  15. package/dist/plugins/mui/index.d.ts +8 -0
  16. package/dist/plugins/mui/index.js +25 -0
  17. package/dist/plugins/rdg/index.d.ts +17 -0
  18. package/dist/{strategies/rdg.js → plugins/rdg/index.js} +25 -26
  19. package/dist/smartRow.d.ts +6 -1
  20. package/dist/smartRow.js +27 -9
  21. package/dist/strategies/filter.d.ts +13 -0
  22. package/dist/strategies/filter.js +38 -0
  23. package/dist/strategies/index.d.ts +21 -0
  24. package/dist/strategies/index.js +6 -0
  25. package/dist/typeContext.d.ts +1 -1
  26. package/dist/typeContext.js +19 -13
  27. package/dist/types.d.ts +19 -20
  28. package/dist/useTable.js +52 -186
  29. package/dist/utils/sentinel.d.ts +5 -0
  30. package/dist/utils/sentinel.js +8 -0
  31. package/package.json +4 -4
  32. package/dist/plugins.d.ts +0 -44
  33. package/dist/plugins.js +0 -13
  34. package/dist/strategies/glide.d.ts +0 -45
  35. package/dist/strategies/rdg.d.ts +0 -34
  36. /package/dist/{strategies → plugins}/glide/columns.d.ts +0 -0
  37. /package/dist/{strategies → plugins}/glide/headers.d.ts +0 -0
package/README.md CHANGED
@@ -70,7 +70,7 @@ const table = await useTable(page.locator('#my-table')).init();
70
70
  const row = await table.findRow({ Name: 'John Doe' });
71
71
 
72
72
  // Access cells by column name
73
- const email = await row.getCell('Email').textContent();
73
+ const email = await row.getCell('Email').innerText();
74
74
 
75
75
  // Search across paginated tables
76
76
  const allActive = await table.findRows({ Status: 'Active' });
@@ -146,9 +146,9 @@ const table = useTable(page.locator('#table'), {
146
146
  },
147
147
  // Override how data is written to the 'Tags' column (for .smartFill())
148
148
  Tags: {
149
- write: async (cell, value) => {
149
+ write: async ({ cell, targetValue }) => {
150
150
  await cell.click();
151
- await page.keyboard.type(value);
151
+ await page.keyboard.type(targetValue);
152
152
  await page.keyboard.press('Enter');
153
153
  }
154
154
  }
@@ -219,6 +219,10 @@ for (const row of active) {
219
219
 
220
220
  Contributions are welcome! Please feel free to submit a Pull Request.
221
221
 
222
+ ## Deprecations
223
+
224
+ - `generateConfigPrompt()` — deprecated. Use `generateConfig()` instead. `generateConfigPrompt()` will be removed in v7.0.0.
225
+
222
226
  ## License
223
227
 
224
228
  MIT © Cedrick Catalan
@@ -19,7 +19,7 @@ export declare class RowFinder<T = any> {
19
19
  exact?: boolean;
20
20
  maxPages?: number;
21
21
  }): Promise<SmartRow<T>>;
22
- findRows(filters?: Partial<T> | Record<string, FilterValue>, options?: {
22
+ findRows(filters?: Record<string, FilterValue>, options?: {
23
23
  exact?: boolean;
24
24
  maxPages?: number;
25
25
  }): Promise<SmartRowArray<T>>;
@@ -14,6 +14,7 @@ const debugUtils_1 = require("../utils/debugUtils");
14
14
  const smartRowArray_1 = require("../utils/smartRowArray");
15
15
  const validation_1 = require("../strategies/validation");
16
16
  const elementTracker_1 = require("../utils/elementTracker");
17
+ const sentinel_1 = require("../utils/sentinel");
17
18
  class RowFinder {
18
19
  constructor(rootLocator, config, resolve, filterEngine, tableMapper, makeSmartRow, tableState = { currentPageIndex: 0 }) {
19
20
  this.rootLocator = rootLocator;
@@ -42,14 +43,14 @@ class RowFinder {
42
43
  const sentinel = this.resolve(this.config.rowSelector, this.rootLocator)
43
44
  .filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
44
45
  const smartRow = this.makeSmartRow(sentinel, yield this.tableMapper.getMap(), 0);
45
- smartRow._isSentinel = true;
46
+ smartRow[sentinel_1.SENTINEL_ROW] = true;
46
47
  return smartRow;
47
48
  });
48
49
  }
49
- findRows(filters, options) {
50
- return __awaiter(this, void 0, void 0, function* () {
50
+ findRows() {
51
+ return __awaiter(this, arguments, void 0, function* (filters = {}, options) {
51
52
  var _a, _b, _c, _d;
52
- const filtersRecord = filters || {};
53
+ const filtersRecord = filters;
53
54
  const map = yield this.tableMapper.getMap();
54
55
  const allRows = [];
55
56
  const effectiveMaxPages = (_b = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : this.config.maxPages) !== null && _b !== void 0 ? _b : Infinity;
@@ -61,7 +62,7 @@ class RowFinder {
61
62
  let rowLocators = this.resolve(this.config.rowSelector, this.rootLocator);
62
63
  // Only apply filters if we have them
63
64
  if (Object.keys(filtersRecord).length > 0) {
64
- rowLocators = this.filterEngine.applyFilters(rowLocators, filtersRecord, map, (_a = options === null || options === void 0 ? void 0 : options.exact) !== null && _a !== void 0 ? _a : false, this.rootLocator.page());
65
+ rowLocators = this.filterEngine.applyFilters(rowLocators, filtersRecord, map, (_a = options === null || options === void 0 ? void 0 : options.exact) !== null && _a !== void 0 ? _a : false, this.rootLocator.page(), this.rootLocator);
65
66
  }
66
67
  // Get only newly seen matched rows
67
68
  const newIndices = yield tracker.getUnseenIndices(rowLocators);
@@ -132,7 +133,7 @@ class RowFinder {
132
133
  }
133
134
  }
134
135
  const allRows = this.resolve(this.config.rowSelector, this.rootLocator);
135
- const matchedRows = this.filterEngine.applyFilters(allRows, filters, map, options.exact || false, this.rootLocator.page());
136
+ const matchedRows = this.filterEngine.applyFilters(allRows, filters, map, options.exact || false, this.rootLocator.page(), this.rootLocator);
136
137
  const count = yield matchedRows.count();
137
138
  this.log(`Page ${this.tableState.currentPageIndex}: Found ${count} matches.`);
138
139
  if (count > 1) {
@@ -0,0 +1,25 @@
1
+ import type { Locator, Page } from '@playwright/test';
2
+ import type { SmartRow, RowIterationContext, RowIterationOptions } from '../types';
3
+ import type { FinalTableConfig } from '../types';
4
+ import type { SmartRowArray } from '../utils/smartRowArray';
5
+ export interface TableIterationEnv<T = any> {
6
+ getRowLocators: () => Locator;
7
+ getMap: () => Map<string, number>;
8
+ advancePage: (useBulk: boolean) => Promise<boolean>;
9
+ makeSmartRow: (rowLocator: Locator, map: Map<string, number>, rowIndex: number, tablePageIndex?: number) => SmartRow<T>;
10
+ createSmartRowArray: (rows: SmartRow<T>[]) => SmartRowArray<T>;
11
+ config: FinalTableConfig<T>;
12
+ getPage: () => Page;
13
+ }
14
+ /**
15
+ * Shared row-iteration loop used by forEach, map, and filter.
16
+ */
17
+ export declare function runForEach<T>(env: TableIterationEnv<T>, callback: (ctx: RowIterationContext<T>) => void | Promise<void>, options?: RowIterationOptions): Promise<void>;
18
+ /**
19
+ * Shared row-iteration loop for map.
20
+ */
21
+ export declare function runMap<T, R>(env: TableIterationEnv<T>, callback: (ctx: RowIterationContext<T>) => R | Promise<R>, options?: RowIterationOptions): Promise<R[]>;
22
+ /**
23
+ * Shared row-iteration loop for filter.
24
+ */
25
+ export declare function runFilter<T>(env: TableIterationEnv<T>, predicate: (ctx: RowIterationContext<T>) => boolean | Promise<boolean>, options?: RowIterationOptions): Promise<SmartRowArray<T>>;
@@ -0,0 +1,210 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.runForEach = runForEach;
13
+ exports.runMap = runMap;
14
+ exports.runFilter = runFilter;
15
+ const elementTracker_1 = require("../utils/elementTracker");
16
+ /**
17
+ * Shared row-iteration loop used by forEach, map, and filter.
18
+ */
19
+ function runForEach(env_1, callback_1) {
20
+ return __awaiter(this, arguments, void 0, function* (env, callback, options = {}) {
21
+ var _a, _b, _c, _d;
22
+ const map = env.getMap();
23
+ const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : env.config.maxPages;
24
+ const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : env.config.strategies.dedupe;
25
+ const dedupeKeys = dedupeStrategy ? new Set() : null;
26
+ const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
27
+ const useBulk = (_d = options.useBulkPagination) !== null && _d !== void 0 ? _d : false;
28
+ const tracker = new elementTracker_1.ElementTracker('forEach');
29
+ try {
30
+ let rowIndex = 0;
31
+ let stopped = false;
32
+ let pagesScanned = 1;
33
+ const stop = () => { stopped = true; };
34
+ while (!stopped) {
35
+ const rowLocators = env.getRowLocators();
36
+ const newIndices = yield tracker.getUnseenIndices(rowLocators);
37
+ const pageRows = yield rowLocators.all();
38
+ const smartRows = newIndices.map((idx, i) => env.makeSmartRow(pageRows[idx], map, rowIndex + i));
39
+ if (parallel) {
40
+ yield Promise.all(smartRows.map((row) => __awaiter(this, void 0, void 0, function* () {
41
+ if (stopped)
42
+ return;
43
+ if (dedupeKeys && dedupeStrategy) {
44
+ const key = yield dedupeStrategy(row);
45
+ if (dedupeKeys.has(key))
46
+ return;
47
+ dedupeKeys.add(key);
48
+ }
49
+ yield callback({ row, rowIndex: row.rowIndex, stop });
50
+ })));
51
+ }
52
+ else {
53
+ for (const row of smartRows) {
54
+ if (stopped)
55
+ break;
56
+ if (dedupeKeys && dedupeStrategy) {
57
+ const key = yield dedupeStrategy(row);
58
+ if (dedupeKeys.has(key))
59
+ continue;
60
+ dedupeKeys.add(key);
61
+ }
62
+ yield callback({ row, rowIndex: row.rowIndex, stop });
63
+ }
64
+ }
65
+ rowIndex += smartRows.length;
66
+ if (stopped || pagesScanned >= effectiveMaxPages)
67
+ break;
68
+ if (!(yield env.advancePage(useBulk)))
69
+ break;
70
+ pagesScanned++;
71
+ }
72
+ }
73
+ finally {
74
+ yield tracker.cleanup(env.getPage());
75
+ }
76
+ });
77
+ }
78
+ /**
79
+ * Shared row-iteration loop for map.
80
+ */
81
+ function runMap(env_1, callback_1) {
82
+ return __awaiter(this, arguments, void 0, function* (env, callback, options = {}) {
83
+ var _a, _b, _c, _d;
84
+ const map = env.getMap();
85
+ const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : env.config.maxPages;
86
+ const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : env.config.strategies.dedupe;
87
+ const dedupeKeys = dedupeStrategy ? new Set() : null;
88
+ const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : true;
89
+ const useBulk = (_d = options.useBulkPagination) !== null && _d !== void 0 ? _d : false;
90
+ const tracker = new elementTracker_1.ElementTracker('map');
91
+ const results = [];
92
+ const SKIP = Symbol('skip');
93
+ try {
94
+ let rowIndex = 0;
95
+ let stopped = false;
96
+ let pagesScanned = 1;
97
+ const stop = () => { stopped = true; };
98
+ while (!stopped) {
99
+ const rowLocators = env.getRowLocators();
100
+ const newIndices = yield tracker.getUnseenIndices(rowLocators);
101
+ const pageRows = yield rowLocators.all();
102
+ const smartRows = newIndices.map((idx, i) => env.makeSmartRow(pageRows[idx], map, rowIndex + i));
103
+ if (parallel) {
104
+ const pageResults = yield Promise.all(smartRows.map((row) => __awaiter(this, void 0, void 0, function* () {
105
+ if (dedupeKeys && dedupeStrategy) {
106
+ const key = yield dedupeStrategy(row);
107
+ if (dedupeKeys.has(key))
108
+ return SKIP;
109
+ dedupeKeys.add(key);
110
+ }
111
+ return callback({ row, rowIndex: row.rowIndex, stop });
112
+ })));
113
+ for (const r of pageResults) {
114
+ if (r !== SKIP)
115
+ results.push(r);
116
+ }
117
+ }
118
+ else {
119
+ for (const row of smartRows) {
120
+ if (stopped)
121
+ break;
122
+ if (dedupeKeys && dedupeStrategy) {
123
+ const key = yield dedupeStrategy(row);
124
+ if (dedupeKeys.has(key))
125
+ continue;
126
+ dedupeKeys.add(key);
127
+ }
128
+ results.push(yield callback({ row, rowIndex: row.rowIndex, stop }));
129
+ }
130
+ }
131
+ rowIndex += smartRows.length;
132
+ if (stopped || pagesScanned >= effectiveMaxPages)
133
+ break;
134
+ if (!(yield env.advancePage(useBulk)))
135
+ break;
136
+ pagesScanned++;
137
+ }
138
+ }
139
+ finally {
140
+ yield tracker.cleanup(env.getPage());
141
+ }
142
+ return results;
143
+ });
144
+ }
145
+ /**
146
+ * Shared row-iteration loop for filter.
147
+ */
148
+ function runFilter(env_1, predicate_1) {
149
+ return __awaiter(this, arguments, void 0, function* (env, predicate, options = {}) {
150
+ var _a, _b, _c, _d;
151
+ const map = env.getMap();
152
+ const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : env.config.maxPages;
153
+ const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : env.config.strategies.dedupe;
154
+ const dedupeKeys = dedupeStrategy ? new Set() : null;
155
+ const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
156
+ const useBulk = (_d = options.useBulkPagination) !== null && _d !== void 0 ? _d : false;
157
+ const tracker = new elementTracker_1.ElementTracker('filter');
158
+ const matched = [];
159
+ try {
160
+ let rowIndex = 0;
161
+ let stopped = false;
162
+ let pagesScanned = 1;
163
+ const stop = () => { stopped = true; };
164
+ while (!stopped) {
165
+ const rowLocators = env.getRowLocators();
166
+ const newIndices = yield tracker.getUnseenIndices(rowLocators);
167
+ const pageRows = yield rowLocators.all();
168
+ const smartRows = newIndices.map((idx, i) => env.makeSmartRow(pageRows[idx], map, rowIndex + i, pagesScanned - 1));
169
+ if (parallel) {
170
+ const flags = yield Promise.all(smartRows.map((row) => __awaiter(this, void 0, void 0, function* () {
171
+ if (dedupeKeys && dedupeStrategy) {
172
+ const key = yield dedupeStrategy(row);
173
+ if (dedupeKeys.has(key))
174
+ return false;
175
+ dedupeKeys.add(key);
176
+ }
177
+ return predicate({ row, rowIndex: row.rowIndex, stop });
178
+ })));
179
+ smartRows.forEach((row, i) => { if (flags[i])
180
+ matched.push(row); });
181
+ }
182
+ else {
183
+ for (const row of smartRows) {
184
+ if (stopped)
185
+ break;
186
+ if (dedupeKeys && dedupeStrategy) {
187
+ const key = yield dedupeStrategy(row);
188
+ if (dedupeKeys.has(key))
189
+ continue;
190
+ dedupeKeys.add(key);
191
+ }
192
+ if (yield predicate({ row, rowIndex: row.rowIndex, stop })) {
193
+ matched.push(row);
194
+ }
195
+ }
196
+ }
197
+ rowIndex += smartRows.length;
198
+ if (stopped || pagesScanned >= effectiveMaxPages)
199
+ break;
200
+ if (!(yield env.advancePage(useBulk)))
201
+ break;
202
+ pagesScanned++;
203
+ }
204
+ }
205
+ finally {
206
+ yield tracker.cleanup(env.getPage());
207
+ }
208
+ return env.createSmartRowArray(matched);
209
+ });
210
+ }
@@ -6,6 +6,10 @@ export declare class FilterEngine {
6
6
  constructor(config: FinalTableConfig, resolve: (selector: any, parent: Locator | Page) => Locator);
7
7
  /**
8
8
  * Applies filters to a set of rows.
9
+ *
10
+ * Note: `rootLocator` is optional for backward compatibility in call sites that already
11
+ * pass only the page. When strategies.filter is present we construct a TableContext
12
+ * using the provided `rootLocator`.
9
13
  */
10
- applyFilters(baseRows: Locator, filters: Record<string, FilterValue>, map: Map<string, number>, exact: boolean, page: Page): Locator;
14
+ applyFilters(baseRows: Locator, filters: Record<string, FilterValue>, map: Map<string, number>, exact: boolean, page: Page, rootLocator?: Locator): Locator;
11
15
  }
@@ -9,31 +9,41 @@ class FilterEngine {
9
9
  }
10
10
  /**
11
11
  * Applies filters to a set of rows.
12
+ *
13
+ * Note: `rootLocator` is optional for backward compatibility in call sites that already
14
+ * pass only the page. When strategies.filter is present we construct a TableContext
15
+ * using the provided `rootLocator`.
12
16
  */
13
- applyFilters(baseRows, filters, map, exact, page) {
17
+ applyFilters(baseRows, filters, map, exact, page, rootLocator) {
18
+ var _a;
14
19
  let filtered = baseRows;
15
20
  // Iterate through each filter criteria
16
21
  for (const [colName, value] of Object.entries(filters)) {
17
22
  // Find column index
18
23
  const colIndex = map.get(colName);
19
- // TODO: Use ColumnStrategy for better resolution error handling
20
24
  if (colIndex === undefined) {
21
25
  throw new Error((0, stringUtils_1.buildColumnNotFoundError)(colName, Array.from(map.keys())));
22
26
  }
23
27
  const filterVal = value;
24
- // Use strategy if provided (For future: configured filter strategies)
25
- // But for now, we implement the default logic or use custom if we add it to config later
28
+ // If a pluggable FilterStrategy is provided, prefer it.
29
+ if (((_a = this.config.strategies) === null || _a === void 0 ? void 0 : _a.filter) && typeof this.config.strategies.filter.apply === 'function') {
30
+ const tableContext = {
31
+ root: rootLocator,
32
+ config: this.config,
33
+ page,
34
+ resolve: this.resolve
35
+ };
36
+ filtered = this.config.strategies.filter.apply({
37
+ rows: filtered,
38
+ filter: { column: colName, value: filterVal },
39
+ colIndex,
40
+ tableContext
41
+ });
42
+ continue;
43
+ }
26
44
  // Default Filter Logic
27
45
  const cellTemplate = this.resolve(this.config.cellSelector, page);
28
- // ⚠️ CRITICAL WARNING: DO NOT "FIX" OR REFACTOR THIS LOGIC. ⚠️
29
- // At first glance, `cellTemplate.nth(colIndex)` looks like a global page selector
30
- // that will return the Nth cell on the entire page, rather than the Nth cell in the row.
31
- // THIS IS INTENTIONAL AND CORRECT.
32
- // Playwright deeply understands nested locator scoping. When this global-looking locator
33
- // is passed into `filtered.filter({ has: ... })` below, Playwright magically and
34
- // automatically re-bases the `nth()` selector to be strictly relative to the ROW being evaluated.
35
- // Attempting to manually force generic relative locators here will break complex function
36
- // selectors and introduce regressions. Leave it as is.
46
+ // Playwright scoping: `cellTemplate.nth(colIndex)` will be re-based when used in filtered.filter({ has: ... })
37
47
  const targetCell = cellTemplate.nth(colIndex);
38
48
  if (typeof filterVal === 'function') {
39
49
  // Locator-based filter: (cell) => cell.locator(...)
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { useTable } from './useTable';
2
- export type { TableConfig, TableResult, SmartRow, Selector, FilterValue, PaginationPrimitives, SortingStrategy, FillOptions, RowIterationContext, RowIterationOptions, TableContext, StrategyContext, BeforeCellReadFn, GetCellLocatorFn, GetActiveCellFn, } from './types';
2
+ export type { TableConfig, TableResult, SmartRow, Selector, FilterValue, PaginationPrimitives, SortingStrategy, FillOptions, RowIterationContext, RowIterationOptions, TableContext, StrategyContext, BeforeCellReadFn, GetCellLocatorFn, GetActiveCellFn, DebugConfig, } from './types';
3
3
  export { Strategies } from './strategies';
4
4
  export { Plugins } from './plugins';
@@ -34,10 +34,8 @@ exports.glideGoRight = glideGoRight;
34
34
  const glideGoHome = (context) => __awaiter(void 0, void 0, void 0, function* () {
35
35
  const { root, page } = context;
36
36
  // Glide renders to canvas - the accessibility table (root) is inside the canvas
37
- // We need to find and focus the canvas element that contains our root
38
37
  yield root.evaluate((el) => {
39
38
  var _a;
40
- // Find the closest canvas ancestor
41
39
  const canvas = el.closest('canvas') || ((_a = el.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector('canvas'));
42
40
  if (canvas instanceof HTMLCanvasElement) {
43
41
  canvas.tabIndex = 0;
@@ -45,10 +43,9 @@ const glideGoHome = (context) => __awaiter(void 0, void 0, void 0, function* ()
45
43
  }
46
44
  });
47
45
  yield page.waitForTimeout(100);
48
- // Reset to top-left - Cross-OS sequence (Mac/Windows)
49
46
  yield page.keyboard.press('Control+Home');
50
- yield page.keyboard.press('Meta+ArrowUp'); // Mac Go-To-Top
51
- yield page.keyboard.press('Home'); // Ensure start of row
47
+ yield page.keyboard.press('Meta+ArrowUp');
48
+ yield page.keyboard.press('Home');
52
49
  yield page.waitForTimeout(150);
53
50
  });
54
51
  exports.glideGoHome = glideGoHome;
@@ -24,10 +24,8 @@ const scrollRightHeader = (context, options) => __awaiter(void 0, void 0, void 0
24
24
  const texts = yield headerLoc.allInnerTexts();
25
25
  return texts.map(t => t.trim());
26
26
  });
27
- // Initial capture
28
27
  let currentHeaders = yield getVisible();
29
28
  currentHeaders.forEach(h => collectedHeaders.add(h));
30
- // Find scroller using JS for better iframe/shadow support
31
29
  const scrollerHandle = yield root.evaluateHandle((el, selector) => {
32
30
  if (selector && el.matches(selector))
33
31
  return el;
@@ -60,7 +58,6 @@ const scrollRightHeader = (context, options) => __awaiter(void 0, void 0, void 0
60
58
  else {
61
59
  console.warn("HeaderStrategies.scrollRight: Could not find scroller. Returning visible headers.");
62
60
  }
63
- // Scroll back to start
64
61
  yield scrollerHandle.evaluate(el => el.scrollLeft = 0);
65
62
  yield page.waitForTimeout(200);
66
63
  return Array.from(collectedHeaders);
@@ -0,0 +1,31 @@
1
+ import { FillStrategy, TableConfig } from '../../types';
2
+ /** Strategies only for Glide Data Grid. Includes fillSimple; use when you want to supply your own selectors or override fill. */
3
+ export declare const GlideStrategies: {
4
+ fillSimple: FillStrategy;
5
+ fill: FillStrategy;
6
+ pagination: import("../../types").PaginationPrimitives;
7
+ header: (context: import("../../types").StrategyContext, options?: {
8
+ limit?: number;
9
+ selector?: string;
10
+ scrollAmount?: number;
11
+ }) => Promise<string[]>;
12
+ navigation: {
13
+ goUp: (context: import("../../types").StrategyContext) => Promise<void>;
14
+ goDown: (context: import("../../types").StrategyContext) => Promise<void>;
15
+ goLeft: (context: import("../../types").StrategyContext) => Promise<void>;
16
+ goRight: (context: import("../../types").StrategyContext) => Promise<void>;
17
+ goHome: (context: import("../../types").StrategyContext) => Promise<void>;
18
+ };
19
+ loading: {
20
+ isHeaderLoading: () => Promise<boolean>;
21
+ };
22
+ getCellLocator: ({ row, columnIndex }: any) => any;
23
+ getActiveCell: ({ page }: any) => Promise<{
24
+ rowIndex: number;
25
+ columnIndex: number;
26
+ locator: any;
27
+ } | null>;
28
+ };
29
+ export declare const Glide: Partial<TableConfig> & {
30
+ Strategies: typeof GlideStrategies;
31
+ };
@@ -9,23 +9,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.GlideStrategies = exports.glideGetActiveCell = exports.glideGetCellLocator = exports.glidePaginationStrategy = exports.glideFillSimple = exports.glideFillStrategy = void 0;
13
- const columns_1 = require("./glide/columns");
14
- const headers_1 = require("./glide/headers");
15
- const pagination_1 = require("./pagination");
16
- const stabilization_1 = require("./stabilization");
17
- /**
18
- * Fill strategy for Glide Data Grid with textarea validation.
19
- * This is the default strategy that works with the standard Glide Data Grid editor.
20
- */
12
+ exports.Glide = exports.GlideStrategies = void 0;
13
+ const columns_1 = require("./columns");
14
+ const headers_1 = require("./headers");
15
+ const pagination_1 = require("../../strategies/pagination");
16
+ const stabilization_1 = require("../../strategies/stabilization");
21
17
  const glideFillStrategy = (_a) => __awaiter(void 0, [_a], void 0, function* ({ value, page }) {
22
- // Edit Cell
23
18
  yield page.keyboard.press('Enter');
24
- // Wait for editor to appear
25
19
  const textarea = page.locator('textarea.gdg-input');
26
20
  yield textarea.waitFor({ state: 'visible', timeout: 2000 });
27
21
  yield page.keyboard.type(String(value));
28
- // Wait for textarea value to match what we typed
29
22
  yield textarea.evaluate((el, expectedValue) => {
30
23
  return new Promise((resolve) => {
31
24
  const checkValue = () => {
@@ -39,74 +32,48 @@ const glideFillStrategy = (_a) => __awaiter(void 0, [_a], void 0, function* ({ v
39
32
  checkValue();
40
33
  });
41
34
  }, String(value));
42
- // Small delay to let the grid process the value
43
35
  yield page.waitForTimeout(50);
44
36
  yield page.keyboard.press('Enter');
45
- // Wait for editor to close (commit completed)
46
37
  yield textarea.waitFor({ state: 'detached', timeout: 2000 });
47
- // Wait for accessibility layer to sync with canvas state
48
38
  yield page.waitForTimeout(300);
49
39
  });
50
- exports.glideFillStrategy = glideFillStrategy;
51
- /**
52
- * Simple fill strategy for Glide Data Grid.
53
- * Use this if your Glide implementation doesn't use the standard textarea editor.
54
- * This is faster but may not work for all Glide configurations.
55
- */
56
40
  const glideFillSimple = (_a) => __awaiter(void 0, [_a], void 0, function* ({ value, page }) {
57
41
  yield page.keyboard.press('Enter');
58
42
  yield page.keyboard.type(String(value));
59
43
  yield page.keyboard.press('Enter');
60
44
  });
61
- exports.glideFillSimple = glideFillSimple;
62
- exports.glidePaginationStrategy = pagination_1.PaginationStrategies.infiniteScroll({
45
+ const glidePaginationStrategy = pagination_1.PaginationStrategies.infiniteScroll({
63
46
  scrollTarget: 'xpath=//ancestor::body//div[contains(@class, "dvn-scroller")]',
64
47
  scrollAmount: 500,
65
48
  action: 'js-scroll',
66
49
  stabilization: stabilization_1.StabilizationStrategies.contentChanged({ timeout: 5000 }),
67
- timeout: 5000 // Overall timeout
50
+ timeout: 5000
68
51
  });
69
52
  const glideGetCellLocator = ({ row, columnIndex }) => {
70
- // Use relative locator to support virtualization (where rowIndex resets or is offsets)
71
- // The accessibility DOM usually contains 'td' elements with the data.
72
53
  return row.locator('td').nth(columnIndex);
73
54
  };
74
- exports.glideGetCellLocator = glideGetCellLocator;
75
55
  const glideGetActiveCell = (_a) => __awaiter(void 0, [_a], void 0, function* ({ page }) {
76
- // Find the focused cell/element
77
- // Use broad selector for focused element
78
56
  const focused = page.locator('*:focus').first();
79
57
  if ((yield focused.count()) === 0)
80
58
  return null;
81
- // Debug log
82
- if (process.env.DEBUG)
83
- console.log('Found focused element:', yield focused.evaluate((e) => e.outerHTML));
84
- // Try to extract position from ID if possible
85
59
  const id = (yield focused.getAttribute('id')) || '';
86
- // Expected format: glide-cell-COL-ROW
87
60
  const parts = id.split('-');
88
61
  let rowIndex = -1;
89
62
  let columnIndex = -1;
90
63
  if (parts.length >= 4 && parts[0] === 'glide' && parts[1] === 'cell') {
91
- columnIndex = parseInt(parts[2]) - 1; // 1-based in ID to 0-based
64
+ columnIndex = parseInt(parts[2]) - 1;
92
65
  rowIndex = parseInt(parts[3]);
93
66
  }
94
- else {
95
- // Fallback: If we can't parse ID, we assume it's the correct cell
96
- // because we just navigated to it.
97
- // Returning -1 indices might be confusing but won't stop smartRow from using the locator.
98
- }
99
67
  return {
100
68
  rowIndex,
101
69
  columnIndex,
102
70
  locator: focused
103
71
  };
104
72
  });
105
- exports.glideGetActiveCell = glideGetActiveCell;
106
- exports.GlideStrategies = {
107
- fill: exports.glideFillStrategy,
108
- fillSimple: exports.glideFillSimple,
109
- pagination: exports.glidePaginationStrategy,
73
+ /** Default strategies for the Glide preset (fill only; no fillSimple). */
74
+ const GlideDefaultStrategies = {
75
+ fill: glideFillStrategy,
76
+ pagination: glidePaginationStrategy,
110
77
  header: headers_1.scrollRightHeader,
111
78
  navigation: {
112
79
  goUp: columns_1.glideGoUp,
@@ -116,8 +83,22 @@ exports.GlideStrategies = {
116
83
  goHome: columns_1.glideGoHome
117
84
  },
118
85
  loading: {
119
- isHeaderLoading: () => __awaiter(void 0, void 0, void 0, function* () { return false; }) // Glide renders headers on a canvas, there is no innerText delay
86
+ isHeaderLoading: () => __awaiter(void 0, void 0, void 0, function* () { return false; })
120
87
  },
121
- getCellLocator: exports.glideGetCellLocator,
122
- getActiveCell: exports.glideGetActiveCell
88
+ getCellLocator: glideGetCellLocator,
89
+ getActiveCell: glideGetActiveCell
90
+ };
91
+ /** Strategies only for Glide Data Grid. Includes fillSimple; use when you want to supply your own selectors or override fill. */
92
+ exports.GlideStrategies = Object.assign(Object.assign({}, GlideDefaultStrategies), { fillSimple: glideFillSimple });
93
+ /**
94
+ * Full preset for Glide Data Grid (selectors + default strategies only).
95
+ * Spread: useTable(loc, { ...Plugins.Glide, maxPages: 5 }).
96
+ * Strategies only (including fillSimple): useTable(loc, { rowSelector: '...', strategies: Plugins.Glide.Strategies }).
97
+ */
98
+ const GlidePreset = {
99
+ headerSelector: 'table[role="grid"] thead tr th',
100
+ rowSelector: 'table[role="grid"] tbody tr',
101
+ cellSelector: 'td',
102
+ strategies: GlideDefaultStrategies
123
103
  };
104
+ exports.Glide = Object.defineProperty(GlidePreset, 'Strategies', { get: () => exports.GlideStrategies, enumerable: false });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Presets for specific table/grid libraries. Each plugin exposes:
3
+ * - Plugins.X — full preset (selectors + headerTransformer if any + strategies). Spread: useTable(loc, { ...Plugins.MUI, maxPages: 5 }).
4
+ * - Plugins.X.Strategies — strategies only. Use with your own selectors: useTable(loc, { rowSelector: '...', strategies: Plugins.MUI.Strategies }).
5
+ */
6
+ export declare const Plugins: {
7
+ RDG: Partial<import("..").TableConfig<any>> & {
8
+ Strategies: typeof import("./rdg").RDGStrategies;
9
+ };
10
+ Glide: Partial<import("..").TableConfig<any>> & {
11
+ Strategies: typeof import("./glide").GlideStrategies;
12
+ };
13
+ MUI: Partial<import("..").TableConfig<any>> & {
14
+ Strategies: typeof import("./mui").MUIStrategies;
15
+ };
16
+ };
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Plugins = void 0;
4
+ const rdg_1 = require("./rdg");
5
+ const glide_1 = require("./glide");
6
+ const mui_1 = require("./mui");
7
+ /**
8
+ * Presets for specific table/grid libraries. Each plugin exposes:
9
+ * - Plugins.X — full preset (selectors + headerTransformer if any + strategies). Spread: useTable(loc, { ...Plugins.MUI, maxPages: 5 }).
10
+ * - Plugins.X.Strategies — strategies only. Use with your own selectors: useTable(loc, { rowSelector: '...', strategies: Plugins.MUI.Strategies }).
11
+ */
12
+ exports.Plugins = {
13
+ RDG: rdg_1.RDG,
14
+ Glide: glide_1.Glide,
15
+ MUI: mui_1.MUI,
16
+ };