@cdmx/wappler_ag_grid 2.1.8 → 2.1.9
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 +17 -4
- package/package.json +1 -1
- package/tests/13-flags-styles.html +66 -6
- package/tests/data/sample-data.js +12 -1
- package/tests/findings.json +39 -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`** - `js_data_changes` columns get a `filterValueGetter` that runs the render function
|
|
13
|
+
and strips tags via `removeHtmlTags` (now a hoisted function declaration); the shared filter value getter
|
|
14
|
+
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. 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,16 @@ 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
|
+
const baseFilterValueGetter = filterValueGetter;
|
|
1546
|
+
filterValueGetter = (params) => {
|
|
1547
|
+
const fn = window[matchingJsChange.function];
|
|
1548
|
+
if (typeof fn !== 'function' || !params.data) {
|
|
1549
|
+
return baseFilterValueGetter ? baseFilterValueGetter(params) : undefined;
|
|
1550
|
+
}
|
|
1551
|
+
return removeHtmlTags(fn(params.data));
|
|
1552
|
+
};
|
|
1541
1553
|
}
|
|
1542
1554
|
else {
|
|
1543
1555
|
cellRenderer = undefined;
|
|
@@ -2288,7 +2300,8 @@ dmx.Component('ag-grid', {
|
|
|
2288
2300
|
|
|
2289
2301
|
//CSV Export Function
|
|
2290
2302
|
// Helper function to remove HTML tags from string
|
|
2291
|
-
|
|
2303
|
+
// Function declaration (hoisted): column-def filter getters built earlier in refreshGrid use it.
|
|
2304
|
+
function removeHtmlTags(htmlString) {
|
|
2292
2305
|
if (typeof htmlString !== 'string') return htmlString;
|
|
2293
2306
|
// Remove all HTML tags and decode common HTML entities
|
|
2294
2307
|
return htmlString
|
|
@@ -2300,7 +2313,7 @@ dmx.Component('ag-grid', {
|
|
|
2300
2313
|
.replace(/&/g, '&')
|
|
2301
2314
|
.replace(/"/g, '"')
|
|
2302
2315
|
.replace(/'/g, "'");
|
|
2303
|
-
}
|
|
2316
|
+
}
|
|
2304
2317
|
|
|
2305
2318
|
exportGridData = (currentGridInstance, currentGridConfig) => {
|
|
2306
2319
|
const exportConfig = currentGridConfig.context.exportConfig;
|
package/package.json
CHANGED
|
@@ -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,27 @@
|
|
|
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
|
+
Try typing <code>PAYOUT</code> in a floating filter or the quick filter box.
|
|
249
|
+
</p>
|
|
250
|
+
<input type="search" id="jsqf" placeholder="Quick filter…" oninput="document.getElementById('jsFilterGrid').dmxComponent.get('gridInstance').setGridOption('quickFilterText', this.value)">
|
|
251
|
+
<div class="grid-wrap">
|
|
252
|
+
<dmx-ag-grid
|
|
253
|
+
id="jsFilterGrid"
|
|
254
|
+
dmx-bind:data="txns.value"
|
|
255
|
+
grid_theme="ag-theme-alpine"
|
|
256
|
+
quick_filter_field="jsqf"
|
|
257
|
+
dmx-bind:js_data_changes="[{field:'monitoring_results', function:'renderMonitoring'},{field:'screening_results', function:'renderScreening'}]"
|
|
258
|
+
dmx-bind:pagination="false">
|
|
259
|
+
</dmx-ag-grid>
|
|
260
|
+
</div>
|
|
261
|
+
</section>
|
|
262
|
+
|
|
241
263
|
<section class="feature">
|
|
242
264
|
<h2>Live rstyles assertions</h2>
|
|
243
265
|
<p class="desc">
|
|
@@ -250,6 +272,10 @@
|
|
|
250
272
|
</main>
|
|
251
273
|
|
|
252
274
|
<script>
|
|
275
|
+
// js_data_changes renderers for jsFilterGrid: JSON lists -> one entry per line.
|
|
276
|
+
window.renderMonitoring = data => (data.monitoring_results || []).map(r => '<b>' + r.rule + '</b> (' + r.score + ')').join('<br>');
|
|
277
|
+
window.renderScreening = data => (data.screening_results || []).map(r => r.list + ': ' + r.match).join('<br>');
|
|
278
|
+
|
|
253
279
|
// Helpers: count rows in a grid's DOM with a given background colour. Multiple matches
|
|
254
280
|
// are accepted (puppeteer normalises CSS to rgb()).
|
|
255
281
|
function bgCount(gridId, predicate) {
|
|
@@ -364,13 +390,47 @@
|
|
|
364
390
|
checks.cs_textHigh = { expect: highSalary, actual: csTextHigh };
|
|
365
391
|
checks.cs_cellLow = { expect: lowSalary, actual: csCellLow };
|
|
366
392
|
|
|
367
|
-
|
|
368
|
-
|
|
393
|
+
// js_data_changes filter checks (2.1.9): filters must match the rendered text, not the raw JSON array.
|
|
394
|
+
// The component's filter methods defer to dmx.nextTick, so this part is async and __rsCheck is
|
|
395
|
+
// published once it has run.
|
|
396
|
+
(async () => {
|
|
397
|
+
const txns = (window.SampleData && window.SampleData.transactions) || [];
|
|
398
|
+
const hasWord = (list, key, word) => (list || []).some(r => String(r[key]).includes(word));
|
|
399
|
+
const monPayout = txns.filter(r => hasWord(r.monitoring_results, 'rule', 'PAYOUT')).length;
|
|
400
|
+
const scrPayout = txns.filter(r => hasWord(r.screening_results, 'match', 'PAYOUT')).length;
|
|
401
|
+
const anyPayout = txns.filter(r => hasWord(r.monitoring_results, 'rule', 'PAYOUT') || hasWord(r.screening_results, 'match', 'PAYOUT')).length;
|
|
402
|
+
const el = document.getElementById('jsFilterGrid');
|
|
403
|
+
const comp = el && el.dmxComponent;
|
|
404
|
+
const gi = comp && comp.get('gridInstance');
|
|
405
|
+
const tick = () => new Promise(res => dmx.nextTick(res));
|
|
406
|
+
const call = async (name, arg) => { comp.methods[name].call(comp, arg); await tick(); await tick(); };
|
|
407
|
+
const visible = () => gi.getDisplayedRowCount();
|
|
408
|
+
if (gi) {
|
|
409
|
+
await call('applyFilters', { monitoring_results: { type: 'contains', filter: 'PAYOUT' } });
|
|
410
|
+
checks.jsFilter_monitoring = { expect: monPayout, actual: visible() };
|
|
411
|
+
await call('applyFilters', { screening_results: { type: 'contains', filter: 'PAYOUT' } });
|
|
412
|
+
checks.jsFilter_screening = { expect: scrPayout, actual: visible() };
|
|
413
|
+
await call('applyFilters', { monitoring_results: { type: 'contains', filter: 'NO-SUCH-RULE' } });
|
|
414
|
+
checks.jsFilter_noMatch = { expect: 0, actual: visible() };
|
|
415
|
+
await call('applyFilters', { id: { type: 'equals', filter: 9003 } });
|
|
416
|
+
checks.jsFilter_plainColumn = { expect: 1, actual: visible() };
|
|
417
|
+
await call('clearFilters');
|
|
418
|
+
gi.setGridOption('quickFilterText', 'payout');
|
|
419
|
+
checks.jsFilter_quick = { expect: anyPayout, actual: visible() };
|
|
420
|
+
gi.setGridOption('quickFilterText', '');
|
|
421
|
+
checks.jsFilter_cleared = { expect: txns.length, actual: visible() };
|
|
422
|
+
} else {
|
|
423
|
+
checks.jsFilter_gridReady = { expect: true, actual: false };
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const failed = Object.entries(checks).filter(([_, v]) => v.actual !== v.expect);
|
|
427
|
+
window.__rsCheck = { checks, ok: failed.length === 0, failed: failed.map(([k]) => k) };
|
|
369
428
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
429
|
+
const box = document.getElementById('rs-check');
|
|
430
|
+
box.textContent = Object.entries(checks)
|
|
431
|
+
.map(([k, v]) => (v.actual === v.expect ? '✓' : '✗') + ' ' + k + ' — expected ' + v.expect + ', got ' + v.actual)
|
|
432
|
+
.join('\n');
|
|
433
|
+
})();
|
|
374
434
|
}
|
|
375
435
|
runChecks();
|
|
376
436
|
</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:37:40.392Z",
|
|
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,30 @@
|
|
|
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_quick": {
|
|
793
|
+
"expect": 4,
|
|
794
|
+
"actual": 4
|
|
795
|
+
},
|
|
796
|
+
"jsFilter_cleared": {
|
|
797
|
+
"expect": 6,
|
|
798
|
+
"actual": 6
|
|
763
799
|
}
|
|
764
800
|
},
|
|
765
801
|
"ok": true,
|
|
@@ -888,5 +924,5 @@
|
|
|
888
924
|
}
|
|
889
925
|
}
|
|
890
926
|
],
|
|
891
|
-
"finishedAt": "2026-
|
|
927
|
+
"finishedAt": "2026-10-01T12:38:05.318Z"
|
|
892
928
|
}
|
|
@@ -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','stylesGrid','transformGrid','tipGrid'] },
|
|
28
|
+
{ file: '13-flags-styles.html', grids: ['flagsGrid','stylesGrid','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
|
];
|