@cdmx/wappler_ag_grid 2.1.8 → 2.1.10
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/CHANGELOG.md +17 -0
- package/README.md +2 -0
- package/dmx-ag-grid.js +21 -4
- package/package.json +2 -1
- package/tests/13-flags-styles.html +71 -6
- package/tests/data/sample-data.js +12 -1
- package/tests/findings.json +43 -3
- package/tests/lib/test-helpers.js +3 -1
- package/tests/run-all-puppeteer.cjs +6 -2
- package/tests/run-all.cjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## Version 2.1.9 (Filter on JS-rendered columns)
|
|
4
|
+
|
|
5
|
+
### Summary
|
|
6
|
+
Column filters, the floating filter row, the quick filter, `applyFilters` and preset `cfilters` now match the
|
|
7
|
+
text a `js_data_changes` function renders (HTML removed) instead of the raw field value. Columns whose raw
|
|
8
|
+
value is a JSON array/object are matched against their JSON text. Fixes "contains" filters on result-list
|
|
9
|
+
columns (e.g. Monitoring Results / Screening Results) returning no rows.
|
|
10
|
+
|
|
11
|
+
### Changes Made
|
|
12
|
+
1. **`dmx-ag-grid.js`** - text-type `js_data_changes` columns get a `filterValueGetter` that runs the render
|
|
13
|
+
function and strips tags via `removeHtmlTags` (now a hoisted function declaration); number/date columns keep
|
|
14
|
+
the raw value for their comparison filters. The shared filter value getter stringifies object/array values.
|
|
15
|
+
2. **`tests/13-flags-styles.html`**, **`tests/data/sample-data.js`** - `jsFilterGrid` scenario with a
|
|
16
|
+
`transactions` fixture and page assertions for column + quick filters.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
3
20
|
## Version 2.1.4 (Row Color Formatting Fix)
|
|
4
21
|
|
|
5
22
|
### Summary
|
package/README.md
CHANGED
|
@@ -408,6 +408,8 @@ ex: jsChanges.
|
|
|
408
408
|
}
|
|
409
409
|
</script>
|
|
410
410
|
```
|
|
411
|
+
Column filters, the floating filter row and the quick filter match the text this function renders (HTML tags removed, `<br>` treated as a line break), so searching for a word that is visible in the cell finds the row. This applies to text columns; number and date columns keep filtering on their raw value. Columns whose raw value is a JSON array/object are matched against their JSON text.
|
|
412
|
+
|
|
411
413
|
---
|
|
412
414
|
# Custom Headers
|
|
413
415
|
|
package/dmx-ag-grid.js
CHANGED
|
@@ -1353,8 +1353,10 @@ dmx.Component('ag-grid', {
|
|
|
1353
1353
|
return parseFloat(value);
|
|
1354
1354
|
}
|
|
1355
1355
|
}
|
|
1356
|
-
// Return the original value if no matching changes were found
|
|
1357
|
-
|
|
1356
|
+
// Return the original value if no matching changes were found.
|
|
1357
|
+
// ponytail: arrays/objects (JSON result lists) stringify so text filters can match nested words;
|
|
1358
|
+
// keys match too - flatten string leaves if that ever bites.
|
|
1359
|
+
return (value !== null && typeof value === 'object') ? JSON.stringify(value) : value;
|
|
1358
1360
|
};
|
|
1359
1361
|
};
|
|
1360
1362
|
if (Array.isArray(this.props.column_defs) && this.props.column_defs.length > 0) {
|
|
@@ -1538,6 +1540,20 @@ dmx.Component('ag-grid', {
|
|
|
1538
1540
|
return cellValue;
|
|
1539
1541
|
}
|
|
1540
1542
|
}
|
|
1543
|
+
// ponytail: filter on what the user sees - renderer output with tags stripped.
|
|
1544
|
+
// Covers column/floating filters, quick filter, applyFilters and cfilters (all read filterValueGetter).
|
|
1545
|
+
// Text columns only: number/date filters compare the raw value, so they keep the base getter.
|
|
1546
|
+
if (dataType === 'text') {
|
|
1547
|
+
const baseFilterValueGetter = filterValueGetter;
|
|
1548
|
+
filterValueGetter = (params) => {
|
|
1549
|
+
const fn = window[matchingJsChange.function];
|
|
1550
|
+
const rendered = (typeof fn === 'function' && params.data) ? fn(params.data) : undefined;
|
|
1551
|
+
if (typeof rendered !== 'string') {
|
|
1552
|
+
return baseFilterValueGetter ? baseFilterValueGetter(params) : undefined;
|
|
1553
|
+
}
|
|
1554
|
+
return removeHtmlTags(rendered);
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1541
1557
|
}
|
|
1542
1558
|
else {
|
|
1543
1559
|
cellRenderer = undefined;
|
|
@@ -2288,7 +2304,8 @@ dmx.Component('ag-grid', {
|
|
|
2288
2304
|
|
|
2289
2305
|
//CSV Export Function
|
|
2290
2306
|
// Helper function to remove HTML tags from string
|
|
2291
|
-
|
|
2307
|
+
// Function declaration (hoisted): column-def filter getters built earlier in refreshGrid use it.
|
|
2308
|
+
function removeHtmlTags(htmlString) {
|
|
2292
2309
|
if (typeof htmlString !== 'string') return htmlString;
|
|
2293
2310
|
// Remove all HTML tags and decode common HTML entities
|
|
2294
2311
|
return htmlString
|
|
@@ -2300,7 +2317,7 @@ dmx.Component('ag-grid', {
|
|
|
2300
2317
|
.replace(/&/g, '&')
|
|
2301
2318
|
.replace(/"/g, '"')
|
|
2302
2319
|
.replace(/'/g, "'");
|
|
2303
|
-
}
|
|
2320
|
+
}
|
|
2304
2321
|
|
|
2305
2322
|
exportGridData = (currentGridInstance, currentGridConfig) => {
|
|
2306
2323
|
const exportConfig = currentGridConfig.context.exportConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdmx/wappler_ag_grid",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "App Connect module for AG Grid v35 - Advanced data grid with enhanced editing, filtering, and tree data capabilities.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,3 +29,4 @@
|
|
|
29
29
|
"publish-dry-run": "npm publish ./dist --access public --dry-run"
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
<main class="test-main">
|
|
38
38
|
<dmx-test-data id="rows" source="employees"></dmx-test-data>
|
|
39
|
+
<dmx-test-data id="txns" source="transactions"></dmx-test-data>
|
|
39
40
|
|
|
40
41
|
<section class="feature">
|
|
41
42
|
<h2>Suppression flags + ci_sort + hide_filters/sort</h2>
|
|
@@ -238,6 +239,28 @@
|
|
|
238
239
|
</div>
|
|
239
240
|
</section>
|
|
240
241
|
|
|
242
|
+
<section class="feature">
|
|
243
|
+
<h2>Filters on JS-rendered columns (js_data_changes)</h2>
|
|
244
|
+
<p class="desc">
|
|
245
|
+
<code>monitoring_results</code> / <code>screening_results</code> are JSON lists rendered by
|
|
246
|
+
<code>js_data_changes</code> functions (one entry per line via <code><br></code>). Since 2.1.9 the
|
|
247
|
+
column filters, <code>applyFilters</code> and the quick filter match the rendered text, not the raw array
|
|
248
|
+
(text columns only: <code>amount</code> also has a JS renderer and must keep its number filter).
|
|
249
|
+
Try typing <code>PAYOUT</code> in a floating filter or the quick filter box.
|
|
250
|
+
</p>
|
|
251
|
+
<input type="search" id="jsqf" placeholder="Quick filter…" oninput="document.getElementById('jsFilterGrid').dmxComponent.get('gridInstance').setGridOption('quickFilterText', this.value)">
|
|
252
|
+
<div class="grid-wrap">
|
|
253
|
+
<dmx-ag-grid
|
|
254
|
+
id="jsFilterGrid"
|
|
255
|
+
dmx-bind:data="txns.value"
|
|
256
|
+
grid_theme="ag-theme-alpine"
|
|
257
|
+
quick_filter_field="jsqf"
|
|
258
|
+
dmx-bind:js_data_changes="[{field:'monitoring_results', function:'renderMonitoring'},{field:'screening_results', function:'renderScreening'},{field:'amount', function:'renderAmount'}]"
|
|
259
|
+
dmx-bind:pagination="false">
|
|
260
|
+
</dmx-ag-grid>
|
|
261
|
+
</div>
|
|
262
|
+
</section>
|
|
263
|
+
|
|
241
264
|
<section class="feature">
|
|
242
265
|
<h2>Live rstyles assertions</h2>
|
|
243
266
|
<p class="desc">
|
|
@@ -250,6 +273,12 @@
|
|
|
250
273
|
</main>
|
|
251
274
|
|
|
252
275
|
<script>
|
|
276
|
+
// js_data_changes renderers for jsFilterGrid: JSON lists -> one entry per line.
|
|
277
|
+
window.renderMonitoring = data => (data.monitoring_results || []).map(r => '<b>' + r.rule + '</b> (' + r.score + ')').join('<br>');
|
|
278
|
+
window.renderScreening = data => (data.screening_results || []).map(r => r.list + ': ' + r.match).join('<br>');
|
|
279
|
+
// Number column with a JS renderer: its number filter must keep comparing the raw value.
|
|
280
|
+
window.renderAmount = data => '<span class="badge">' + Number(data.amount).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + '</span>';
|
|
281
|
+
|
|
253
282
|
// Helpers: count rows in a grid's DOM with a given background colour. Multiple matches
|
|
254
283
|
// are accepted (puppeteer normalises CSS to rgb()).
|
|
255
284
|
function bgCount(gridId, predicate) {
|
|
@@ -364,13 +393,49 @@
|
|
|
364
393
|
checks.cs_textHigh = { expect: highSalary, actual: csTextHigh };
|
|
365
394
|
checks.cs_cellLow = { expect: lowSalary, actual: csCellLow };
|
|
366
395
|
|
|
367
|
-
|
|
368
|
-
|
|
396
|
+
// js_data_changes filter checks (2.1.9): filters must match the rendered text, not the raw JSON array.
|
|
397
|
+
// The component's filter methods defer to dmx.nextTick, so this part is async and __rsCheck is
|
|
398
|
+
// published once it has run.
|
|
399
|
+
(async () => {
|
|
400
|
+
const txns = (window.SampleData && window.SampleData.transactions) || [];
|
|
401
|
+
const hasWord = (list, key, word) => (list || []).some(r => String(r[key]).includes(word));
|
|
402
|
+
const monPayout = txns.filter(r => hasWord(r.monitoring_results, 'rule', 'PAYOUT')).length;
|
|
403
|
+
const scrPayout = txns.filter(r => hasWord(r.screening_results, 'match', 'PAYOUT')).length;
|
|
404
|
+
const anyPayout = txns.filter(r => hasWord(r.monitoring_results, 'rule', 'PAYOUT') || hasWord(r.screening_results, 'match', 'PAYOUT')).length;
|
|
405
|
+
const el = document.getElementById('jsFilterGrid');
|
|
406
|
+
const comp = el && el.dmxComponent;
|
|
407
|
+
const gi = comp && comp.get('gridInstance');
|
|
408
|
+
const tick = () => new Promise(res => dmx.nextTick(res));
|
|
409
|
+
const call = async (name, arg) => { comp.methods[name].call(comp, arg); await tick(); await tick(); };
|
|
410
|
+
const visible = () => gi.getDisplayedRowCount();
|
|
411
|
+
if (gi) {
|
|
412
|
+
await call('applyFilters', { monitoring_results: { type: 'contains', filter: 'PAYOUT' } });
|
|
413
|
+
checks.jsFilter_monitoring = { expect: monPayout, actual: visible() };
|
|
414
|
+
await call('applyFilters', { screening_results: { type: 'contains', filter: 'PAYOUT' } });
|
|
415
|
+
checks.jsFilter_screening = { expect: scrPayout, actual: visible() };
|
|
416
|
+
await call('applyFilters', { monitoring_results: { type: 'contains', filter: 'NO-SUCH-RULE' } });
|
|
417
|
+
checks.jsFilter_noMatch = { expect: 0, actual: visible() };
|
|
418
|
+
await call('applyFilters', { id: { type: 'equals', filter: 9003 } });
|
|
419
|
+
checks.jsFilter_plainColumn = { expect: 1, actual: visible() };
|
|
420
|
+
await call('applyFilters', { amount: { type: 'greaterThan', filter: 1000 } });
|
|
421
|
+
checks.jsFilter_numberWithRenderer = { expect: txns.filter(r => r.amount > 1000).length, actual: visible() };
|
|
422
|
+
await call('clearFilters');
|
|
423
|
+
gi.setGridOption('quickFilterText', 'payout');
|
|
424
|
+
checks.jsFilter_quick = { expect: anyPayout, actual: visible() };
|
|
425
|
+
gi.setGridOption('quickFilterText', '');
|
|
426
|
+
checks.jsFilter_cleared = { expect: txns.length, actual: visible() };
|
|
427
|
+
} else {
|
|
428
|
+
checks.jsFilter_gridReady = { expect: true, actual: false };
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const failed = Object.entries(checks).filter(([_, v]) => v.actual !== v.expect);
|
|
432
|
+
window.__rsCheck = { checks, ok: failed.length === 0, failed: failed.map(([k]) => k) };
|
|
369
433
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
434
|
+
const box = document.getElementById('rs-check');
|
|
435
|
+
box.textContent = Object.entries(checks)
|
|
436
|
+
.map(([k, v]) => (v.actual === v.expect ? '✓' : '✗') + ' ' + k + ' — expected ' + v.expect + ', got ' + v.actual)
|
|
437
|
+
.join('\n');
|
|
438
|
+
})();
|
|
374
439
|
}
|
|
375
440
|
runChecks();
|
|
376
441
|
</script>
|
|
@@ -59,5 +59,16 @@
|
|
|
59
59
|
{ id: 5, name: 'Echo', department: 'Finance', status: true }
|
|
60
60
|
];
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
// Transactions whose result columns are JSON lists rendered via js_data_changes.
|
|
63
|
+
// Used by 13-flags-styles.html to prove column + quick filters match the rendered text.
|
|
64
|
+
const transactions = [
|
|
65
|
+
{ id: 9001, reference: 'TXN9001', amount: 1250.00, monitoring_results: [{ rule: 'PAYOUT', score: 80 }, { rule: 'VELOCITY', score: 40 }], screening_results: [{ list: 'PEP', match: 'PAYOUT AGENT' }] },
|
|
66
|
+
{ id: 9002, reference: 'TXN9002', amount: 300.00, monitoring_results: [{ rule: 'VELOCITY', score: 55 }], screening_results: [{ list: 'SANCTIONS', match: 'NONE' }] },
|
|
67
|
+
{ id: 9003, reference: 'TXN9003', amount: 9800.00, monitoring_results: [{ rule: 'PAYOUT', score: 95 }], screening_results: [] },
|
|
68
|
+
{ id: 9004, reference: 'TXN9004', amount: 75.00, monitoring_results: [], screening_results: [{ list: 'PEP', match: 'PAYOUT' }] },
|
|
69
|
+
{ id: 9005, reference: 'TXN9005', amount: 410.00, monitoring_results: null, screening_results: null },
|
|
70
|
+
{ id: 9006, reference: 'TXN9006', amount: 2200.00, monitoring_results: [{ rule: 'STRUCTURING', score: 70 }, { rule: 'PAYOUT', score: 60 }], screening_results: [{ list: 'ADVERSE MEDIA', match: 'NONE' }] }
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
window.SampleData = { employees, orders, departments, statusRows, transactions };
|
|
63
74
|
})();
|
package/tests/findings.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"startedAt": "2026-
|
|
2
|
+
"startedAt": "2026-10-01T12:45:25.153Z",
|
|
3
3
|
"base": "http://localhost:8765/tests",
|
|
4
4
|
"pages": [
|
|
5
5
|
{
|
|
@@ -594,7 +594,8 @@
|
|
|
594
594
|
"rsLegacy",
|
|
595
595
|
"csCell",
|
|
596
596
|
"transformGrid",
|
|
597
|
-
"tipGrid"
|
|
597
|
+
"tipGrid",
|
|
598
|
+
"jsFilterGrid"
|
|
598
599
|
],
|
|
599
600
|
"missing": [],
|
|
600
601
|
"result": {
|
|
@@ -711,6 +712,17 @@
|
|
|
711
712
|
"filterStateKeys": [],
|
|
712
713
|
"columnStateLen": 0,
|
|
713
714
|
"domRows": 25
|
|
715
|
+
},
|
|
716
|
+
"jsFilterGrid": {
|
|
717
|
+
"count": 6,
|
|
718
|
+
"state": {
|
|
719
|
+
"firstDataRendered": true
|
|
720
|
+
},
|
|
721
|
+
"dataLen": null,
|
|
722
|
+
"selectedRowsLen": 0,
|
|
723
|
+
"filterStateKeys": [],
|
|
724
|
+
"columnStateLen": 0,
|
|
725
|
+
"domRows": 6
|
|
714
726
|
}
|
|
715
727
|
}
|
|
716
728
|
},
|
|
@@ -760,6 +772,34 @@
|
|
|
760
772
|
"cs_cellLow": {
|
|
761
773
|
"expect": 5,
|
|
762
774
|
"actual": 5
|
|
775
|
+
},
|
|
776
|
+
"jsFilter_monitoring": {
|
|
777
|
+
"expect": 3,
|
|
778
|
+
"actual": 3
|
|
779
|
+
},
|
|
780
|
+
"jsFilter_screening": {
|
|
781
|
+
"expect": 2,
|
|
782
|
+
"actual": 2
|
|
783
|
+
},
|
|
784
|
+
"jsFilter_noMatch": {
|
|
785
|
+
"expect": 0,
|
|
786
|
+
"actual": 0
|
|
787
|
+
},
|
|
788
|
+
"jsFilter_plainColumn": {
|
|
789
|
+
"expect": 1,
|
|
790
|
+
"actual": 1
|
|
791
|
+
},
|
|
792
|
+
"jsFilter_numberWithRenderer": {
|
|
793
|
+
"expect": 3,
|
|
794
|
+
"actual": 3
|
|
795
|
+
},
|
|
796
|
+
"jsFilter_quick": {
|
|
797
|
+
"expect": 4,
|
|
798
|
+
"actual": 4
|
|
799
|
+
},
|
|
800
|
+
"jsFilter_cleared": {
|
|
801
|
+
"expect": 6,
|
|
802
|
+
"actual": 6
|
|
763
803
|
}
|
|
764
804
|
},
|
|
765
805
|
"ok": true,
|
|
@@ -888,5 +928,5 @@
|
|
|
888
928
|
}
|
|
889
929
|
}
|
|
890
930
|
],
|
|
891
|
-
"finishedAt": "2026-
|
|
931
|
+
"finishedAt": "2026-10-01T12:45:49.727Z"
|
|
892
932
|
}
|
|
@@ -131,7 +131,9 @@
|
|
|
131
131
|
console.error('[TestKit] no method', method, 'on', id);
|
|
132
132
|
return null;
|
|
133
133
|
}
|
|
134
|
-
|
|
134
|
+
// AppConnect's __method wrapper drops its first argument (Wappler passes the event there),
|
|
135
|
+
// so prepend a placeholder or methods like applyFilters(model) receive undefined.
|
|
136
|
+
return g[key].apply(g, [null].concat(args));
|
|
135
137
|
},
|
|
136
138
|
list: function () {
|
|
137
139
|
// Find dmx-ag-grid elements in the DOM and return their dmx ids — more reliable
|
|
@@ -22,7 +22,7 @@ const PAGES = [
|
|
|
22
22
|
{ file: '10-state-persistence.html',grids: ['stateGrid'] },
|
|
23
23
|
{ file: '11-layout.html', grids: ['autoH','fixedH','scrollH'] },
|
|
24
24
|
{ file: '12-methods.html', grids: ['m'] },
|
|
25
|
-
{ file: '13-flags-styles.html', grids: ['flagsGrid','rsFn','rsOp','rsCompound','rsShort','rsFirst','rsLegacy','csCell','transformGrid','tipGrid'], pageAssertion: '__rsCheck' },
|
|
25
|
+
{ file: '13-flags-styles.html', grids: ['flagsGrid','rsFn','rsOp','rsCompound','rsShort','rsFirst','rsLegacy','csCell','transformGrid','tipGrid','jsFilterGrid'], pageAssertion: '__rsCheck' },
|
|
26
26
|
{ file: '14-compact-view.html', grids: ['compactGrid','extButtons','cellEvtGrid'] },
|
|
27
27
|
{ file: '15-csp.html', grids: ['cspGrid','statusGrid'], pageAssertion: '__cspCheck' }
|
|
28
28
|
];
|
|
@@ -132,8 +132,11 @@ function findChrome() {
|
|
|
132
132
|
let assertion = null;
|
|
133
133
|
if (spec.pageAssertion) {
|
|
134
134
|
try {
|
|
135
|
+
// Page checks poll until their grids settle; wait for them to publish instead of
|
|
136
|
+
// reading too early (a missing assertion must fail, not pass silently).
|
|
137
|
+
await page.waitForFunction((key) => !!window[key], { timeout: 10000 }, spec.pageAssertion);
|
|
135
138
|
assertion = await page.evaluate((key) => window[key] || null, spec.pageAssertion);
|
|
136
|
-
} catch (e) { assertion = { error: e.message }; }
|
|
139
|
+
} catch (e) { assertion = { ok: false, error: e.message }; }
|
|
137
140
|
}
|
|
138
141
|
const assertionFailed = assertion && assertion.ok === false;
|
|
139
142
|
|
|
@@ -160,6 +163,7 @@ function findChrome() {
|
|
|
160
163
|
if (assertion) {
|
|
161
164
|
if (assertionFailed) {
|
|
162
165
|
console.log(' assertion FAILED — checks that did not match:');
|
|
166
|
+
if (assertion.error) console.log(' error: ' + assertion.error);
|
|
163
167
|
(assertion.failed || []).forEach(k => {
|
|
164
168
|
const c = assertion.checks && assertion.checks[k];
|
|
165
169
|
console.log(' ' + k + ' expected ' + (c && c.expect) + ' got ' + (c && c.actual));
|
package/tests/run-all.cjs
CHANGED
|
@@ -25,7 +25,7 @@ const PAGES = [
|
|
|
25
25
|
{ file: '10-state-persistence.html',grids: ['stateGrid'] },
|
|
26
26
|
{ file: '11-layout.html', grids: ['autoH','fixedH','scrollH'] },
|
|
27
27
|
{ file: '12-methods.html', grids: ['m'] },
|
|
28
|
-
{ file: '13-flags-styles.html', grids: ['flagsGrid','
|
|
28
|
+
{ file: '13-flags-styles.html', grids: ['flagsGrid','rsFn','rsOp','rsCompound','rsShort','rsFirst','rsLegacy','csCell','transformGrid','tipGrid','jsFilterGrid'] },
|
|
29
29
|
{ file: '14-compact-view.html', grids: ['compactGrid','extButtons','cellEvtGrid'] },
|
|
30
30
|
{ file: '15-csp.html', grids: ['cspGrid','statusGrid'] }
|
|
31
31
|
];
|