@cdmx/wappler_ag_grid 2.1.4 → 2.1.5
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/package.json +4 -2
- package/tests/01-basic-render.html +106 -0
- package/tests/02-themes-locale.html +98 -0
- package/tests/03-editing.html +119 -0
- package/tests/04-selection.html +103 -0
- package/tests/05-action-buttons.html +97 -0
- package/tests/06-export.html +80 -0
- package/tests/07-import.html +80 -0
- package/tests/08-formatting.html +91 -0
- package/tests/09-grouping.html +74 -0
- package/tests/10-state-persistence.html +84 -0
- package/tests/11-layout.html +72 -0
- package/tests/12-methods.html +93 -0
- package/tests/13-flags-styles.html +374 -0
- package/tests/14-compact-view.html +112 -0
- package/tests/README.md +146 -0
- package/tests/data/sample-data.js +52 -0
- package/tests/dmxAppConnect/dmxAppConnect.js +10 -0
- package/tests/dmxAppConnect/dmxAppConnect.js.map +1 -0
- package/tests/index.html +107 -0
- package/tests/lib/common-head.html +30 -0
- package/tests/lib/page-template.js +54 -0
- package/tests/lib/run-collector.js +32 -0
- package/tests/lib/test-helpers.js +145 -0
- package/tests/lib/test-suite.css +178 -0
- package/tests/run-all-puppeteer.cjs +180 -0
- package/tests/run-all.cjs +144 -0
- package/tests/serve.cjs +66 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>13 · Flags & Styles · AG Grid Test Suite</title>
|
|
6
|
+
<link rel="stylesheet" href="./lib/test-suite.css">
|
|
7
|
+
<link rel="stylesheet" href="../node_modules/ag-grid-community/styles/ag-theme-alpine.css">
|
|
8
|
+
<link rel="stylesheet" href="../ag-theme-custom.css">
|
|
9
|
+
<link rel="stylesheet" href="../switch-toggle-slider.css">
|
|
10
|
+
<script src="./dmxAppConnect/dmxAppConnect.js" defer></script>
|
|
11
|
+
<script src="./lib/version-banner.js" defer></script>
|
|
12
|
+
<script src="./lib/test-helpers.js" defer></script>
|
|
13
|
+
<script src="./lib/page-template.js" defer></script>
|
|
14
|
+
<script src="../node_modules/ag-grid-community/dist/ag-grid-community.min.js" defer></script>
|
|
15
|
+
<script src="../dmx-ag-grid.js" defer></script>
|
|
16
|
+
<script src="./data/sample-data.js" defer></script>
|
|
17
|
+
|
|
18
|
+
<script>
|
|
19
|
+
// Window-scoped function condition target for rstyles[*].condition = 'isRowVip()'.
|
|
20
|
+
// Must take row data and return boolean.
|
|
21
|
+
window.isRowVip = function (row) {
|
|
22
|
+
return !!row && row.rating >= 4.7 && row.active === true;
|
|
23
|
+
};
|
|
24
|
+
</script>
|
|
25
|
+
</head>
|
|
26
|
+
<body is="dmx-app" id="page13">
|
|
27
|
+
|
|
28
|
+
<script>
|
|
29
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
30
|
+
TestPage.header({
|
|
31
|
+
title: '13 · Flags & Styles (rstyles v2.1.4)',
|
|
32
|
+
summary: 'Suppression flags + the full rstyles condition matrix added in 2.1.4: function, value/operator, compound, field+shorthand, first-match-wins, legacy object form.'
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<main class="test-main">
|
|
38
|
+
<dmx-test-data id="rows" source="employees"></dmx-test-data>
|
|
39
|
+
|
|
40
|
+
<section class="feature">
|
|
41
|
+
<h2>Suppression flags + ci_sort + hide_filters/sort</h2>
|
|
42
|
+
<p class="desc">
|
|
43
|
+
Most behavior-suppression flags toggled on at once. <code>hide_filters="email,rating"</code>
|
|
44
|
+
and <code>hide_sort="department"</code> selectively disable filtering / sorting per column.
|
|
45
|
+
</p>
|
|
46
|
+
<div class="grid-wrap">
|
|
47
|
+
<dmx-ag-grid
|
|
48
|
+
id="flagsGrid"
|
|
49
|
+
dmx-bind:data="rows.value"
|
|
50
|
+
grid_theme="ag-theme-alpine"
|
|
51
|
+
dmx-bind:ci_sort="true"
|
|
52
|
+
dmx-bind:suppress_row_deselection="true"
|
|
53
|
+
dmx-bind:suppress_row_click_selection="true"
|
|
54
|
+
dmx-bind:suppress_menu_hide="true"
|
|
55
|
+
dmx-bind:suppress_movable_columns="true"
|
|
56
|
+
dmx-bind:suppress_clipboard_paste="true"
|
|
57
|
+
dmx-bind:suppress_scroll_on_new_data="true"
|
|
58
|
+
dmx-bind:always_show_vertical_scroll="true"
|
|
59
|
+
hide_filters="email,rating"
|
|
60
|
+
hide_sort="department"
|
|
61
|
+
dmx-bind:tooltip_show_delay="500"
|
|
62
|
+
dmx-bind:pagination="false">
|
|
63
|
+
</dmx-ag-grid>
|
|
64
|
+
</div>
|
|
65
|
+
</section>
|
|
66
|
+
|
|
67
|
+
<!--
|
|
68
|
+
rstyles entry shape (2.1.4):
|
|
69
|
+
{ field?: string, condition: string, customColor: css-color }
|
|
70
|
+
condition forms (first matching entry wins, color is applied as row background):
|
|
71
|
+
1. Function: condition = 'myFn()' — window.myFn(rowData) returns boolean
|
|
72
|
+
2. Value/operator: condition = 'salary > 90000', '==', '!=', '>=', '<=', '<'
|
|
73
|
+
3. Compound: condition = 'active == true && department == Engineering'
|
|
74
|
+
4. Field+shorthand: field = 'department', condition = 'Engineering' (exact match)
|
|
75
|
+
Bare values (no operator, no field) are skipped. customColor is applied as
|
|
76
|
+
`background` on the row.
|
|
77
|
+
-->
|
|
78
|
+
|
|
79
|
+
<section class="feature">
|
|
80
|
+
<h2>rstyles · function condition</h2>
|
|
81
|
+
<p class="desc">
|
|
82
|
+
Single entry: <code>{ condition: 'isRowVip()', customColor: '#fef3c7' }</code>.
|
|
83
|
+
<code>window.isRowVip(row)</code> returns true when rating ≥ 4.7 and active.
|
|
84
|
+
Expect ~4 highlighted rows (David, Kate, Olivia, Sam, Xavier).
|
|
85
|
+
</p>
|
|
86
|
+
<div class="grid-wrap">
|
|
87
|
+
<dmx-ag-grid
|
|
88
|
+
id="rsFn"
|
|
89
|
+
dmx-bind:data="rows.value"
|
|
90
|
+
grid_theme="ag-theme-alpine"
|
|
91
|
+
dmx-bind:rstyles='[{"condition":"isRowVip()","customColor":"#fef3c7"}]'
|
|
92
|
+
dmx-bind:pagination="false">
|
|
93
|
+
</dmx-ag-grid>
|
|
94
|
+
</div>
|
|
95
|
+
</section>
|
|
96
|
+
|
|
97
|
+
<section class="feature">
|
|
98
|
+
<h2>rstyles · value/operator condition</h2>
|
|
99
|
+
<p class="desc">
|
|
100
|
+
<code>condition: 'salary > 90000'</code> → light green for high earners,
|
|
101
|
+
<code>condition: 'salary <= 65000'</code> → light red for low (exercises the 2.1.4
|
|
102
|
+
<code><=</code>/<code>>=</code> parser fix).
|
|
103
|
+
</p>
|
|
104
|
+
<div class="grid-wrap">
|
|
105
|
+
<dmx-ag-grid
|
|
106
|
+
id="rsOp"
|
|
107
|
+
dmx-bind:data="rows.value"
|
|
108
|
+
grid_theme="ag-theme-alpine"
|
|
109
|
+
dmx-bind:rstyles='[{"condition":"salary > 90000","customColor":"#d1fae5"},{"condition":"salary <= 65000","customColor":"#fee2e2"}]'
|
|
110
|
+
dmx-bind:pagination="false">
|
|
111
|
+
</dmx-ag-grid>
|
|
112
|
+
</div>
|
|
113
|
+
</section>
|
|
114
|
+
|
|
115
|
+
<section class="feature">
|
|
116
|
+
<h2>rstyles · compound && / ||</h2>
|
|
117
|
+
<p class="desc">
|
|
118
|
+
<code>active == true && department == Engineering</code> →
|
|
119
|
+
light blue.
|
|
120
|
+
</p>
|
|
121
|
+
<div class="grid-wrap">
|
|
122
|
+
<dmx-ag-grid
|
|
123
|
+
id="rsCompound"
|
|
124
|
+
dmx-bind:data="rows.value"
|
|
125
|
+
grid_theme="ag-theme-alpine"
|
|
126
|
+
dmx-bind:rstyles='[{"condition":"active == true && department == Engineering","customColor":"#dbeafe"}]'
|
|
127
|
+
dmx-bind:pagination="false">
|
|
128
|
+
</dmx-ag-grid>
|
|
129
|
+
</div>
|
|
130
|
+
</section>
|
|
131
|
+
|
|
132
|
+
<section class="feature">
|
|
133
|
+
<h2>rstyles · Field + plain-value shorthand</h2>
|
|
134
|
+
<p class="desc">
|
|
135
|
+
<code>{ field: 'department', condition: 'Marketing', customColor: '#fce7f3' }</code> —
|
|
136
|
+
exact string match on <code>row.department</code>.
|
|
137
|
+
</p>
|
|
138
|
+
<div class="grid-wrap">
|
|
139
|
+
<dmx-ag-grid
|
|
140
|
+
id="rsShort"
|
|
141
|
+
dmx-bind:data="rows.value"
|
|
142
|
+
grid_theme="ag-theme-alpine"
|
|
143
|
+
dmx-bind:rstyles='[{"field":"department","condition":"Marketing","customColor":"#fce7f3"}]'
|
|
144
|
+
dmx-bind:pagination="false">
|
|
145
|
+
</dmx-ag-grid>
|
|
146
|
+
</div>
|
|
147
|
+
</section>
|
|
148
|
+
|
|
149
|
+
<section class="feature">
|
|
150
|
+
<h2>rstyles · first-match wins</h2>
|
|
151
|
+
<p class="desc">
|
|
152
|
+
Two overlapping conditions on the same field. The first entry
|
|
153
|
+
(<code>salary > 95000 → orange</code>) should win for David (110k) and
|
|
154
|
+
Kate (105k); the second entry (<code>salary > 90000 → yellow</code>) catches
|
|
155
|
+
the next band only.
|
|
156
|
+
</p>
|
|
157
|
+
<div class="grid-wrap">
|
|
158
|
+
<dmx-ag-grid
|
|
159
|
+
id="rsFirst"
|
|
160
|
+
dmx-bind:data="rows.value"
|
|
161
|
+
grid_theme="ag-theme-alpine"
|
|
162
|
+
dmx-bind:rstyles='[{"condition":"salary > 95000","customColor":"#fed7aa"},{"condition":"salary > 90000","customColor":"#fef9c3"}]'
|
|
163
|
+
dmx-bind:pagination="false">
|
|
164
|
+
</dmx-ag-grid>
|
|
165
|
+
</div>
|
|
166
|
+
</section>
|
|
167
|
+
|
|
168
|
+
<section class="feature">
|
|
169
|
+
<h2>rstyles · legacy object form (backwards compat)</h2>
|
|
170
|
+
<p class="desc">
|
|
171
|
+
Pre-2.1.4 callers passed an object keyed by field name. The runtime still accepts both
|
|
172
|
+
shapes (<code>Object.values()</code> normalises to the array path), but
|
|
173
|
+
<code>rstyles</code>'s schema is <code>type: Array</code>, so AppConnect coerces an
|
|
174
|
+
object-shaped <code>dmx-bind:</code> attribute to <code>[]</code> before the runtime sees
|
|
175
|
+
it. The legacy code path is only reachable programmatically — this scenario sets
|
|
176
|
+
<code>rstyles</code> via <code>dmx.app.get('rsLegacy').set(...)</code> and reloads.
|
|
177
|
+
</p>
|
|
178
|
+
<div class="grid-wrap">
|
|
179
|
+
<dmx-ag-grid
|
|
180
|
+
id="rsLegacy"
|
|
181
|
+
dmx-bind:data="rows.value"
|
|
182
|
+
grid_theme="ag-theme-alpine"
|
|
183
|
+
dmx-bind:pagination="false">
|
|
184
|
+
</dmx-ag-grid>
|
|
185
|
+
</div>
|
|
186
|
+
</section>
|
|
187
|
+
|
|
188
|
+
<section class="feature">
|
|
189
|
+
<h2>cstyles · text + cell area, with >= / <= operators</h2>
|
|
190
|
+
<p class="desc">
|
|
191
|
+
Two cstyles entries for <code>salary</code>:
|
|
192
|
+
<code>area:'text'</code> recolors the text (green if ≥ 90k),
|
|
193
|
+
<code>area:'cell'</code> sets the cell background (red if <= 65k).
|
|
194
|
+
Exercises the same 2.1.4 operator parsing fix.
|
|
195
|
+
</p>
|
|
196
|
+
<div class="grid-wrap">
|
|
197
|
+
<dmx-ag-grid
|
|
198
|
+
id="csCell"
|
|
199
|
+
dmx-bind:data="rows.value"
|
|
200
|
+
grid_theme="ag-theme-alpine"
|
|
201
|
+
dmx-bind:cstyles='[{"field":"salary","condition":"salary >= 90000","customColor":"#047857","font":"bold","area":"text"},{"field":"salary","condition":"salary <= 65000","customColor":"#fee2e2","area":"cell"}]'
|
|
202
|
+
dmx-bind:pagination="false">
|
|
203
|
+
</dmx-ag-grid>
|
|
204
|
+
</div>
|
|
205
|
+
</section>
|
|
206
|
+
|
|
207
|
+
<section class="feature">
|
|
208
|
+
<h2>data_changes & display_data_changes</h2>
|
|
209
|
+
<p class="desc">
|
|
210
|
+
<code>data_changes</code> rewrites field values before display.
|
|
211
|
+
<code>display_data_changes</code> overlays HTML badges on values.
|
|
212
|
+
</p>
|
|
213
|
+
<div class="grid-wrap">
|
|
214
|
+
<dmx-ag-grid
|
|
215
|
+
id="transformGrid"
|
|
216
|
+
dmx-bind:data="rows.value"
|
|
217
|
+
grid_theme="ag-theme-alpine"
|
|
218
|
+
dmx-bind:data_changes="[{field:'department', changes:[{from:'Engineering', to:'Eng.'},{from:'Marketing', to:'Mktg.'}]}]"
|
|
219
|
+
dmx-bind:display_data_changes="[{field:'active', changes:[{from:true, to:'<span style=color:green>ACTIVE</span>'},{from:false, to:'<span style=color:red>INACTIVE</span>'}]}]"
|
|
220
|
+
dmx-bind:pagination="false">
|
|
221
|
+
</dmx-ag-grid>
|
|
222
|
+
</div>
|
|
223
|
+
</section>
|
|
224
|
+
|
|
225
|
+
<section class="feature">
|
|
226
|
+
<h2>Custom tooltip</h2>
|
|
227
|
+
<p class="desc"><code>custom_tooltip</code> sets an HTML tooltip template; <code>tooltip_show_delay</code> shortens the hover delay.</p>
|
|
228
|
+
<div class="grid-wrap">
|
|
229
|
+
<dmx-ag-grid
|
|
230
|
+
id="tipGrid"
|
|
231
|
+
dmx-bind:data="rows.value"
|
|
232
|
+
grid_theme="ag-theme-alpine"
|
|
233
|
+
custom_tooltip="<b>{{field}}</b>: {{value}}"
|
|
234
|
+
dmx-bind:tooltip_config="[{field:'email'},{field:'salary'}]"
|
|
235
|
+
dmx-bind:tooltip_show_delay="300"
|
|
236
|
+
dmx-bind:pagination="false">
|
|
237
|
+
</dmx-ag-grid>
|
|
238
|
+
</div>
|
|
239
|
+
</section>
|
|
240
|
+
|
|
241
|
+
<section class="feature">
|
|
242
|
+
<h2>Live rstyles assertions</h2>
|
|
243
|
+
<p class="desc">
|
|
244
|
+
Runs in the page after the grids render and counts coloured rows per scenario.
|
|
245
|
+
The puppeteer runner reads <code>window.__rsCheck</code> and fails the page if any
|
|
246
|
+
condition didn't apply at least one matching row.
|
|
247
|
+
</p>
|
|
248
|
+
<pre class="log" id="rs-check"></pre>
|
|
249
|
+
</section>
|
|
250
|
+
</main>
|
|
251
|
+
|
|
252
|
+
<script>
|
|
253
|
+
// Helpers: count rows in a grid's DOM with a given background colour. Multiple matches
|
|
254
|
+
// are accepted (puppeteer normalises CSS to rgb()).
|
|
255
|
+
function bgCount(gridId, predicate) {
|
|
256
|
+
const root = document.querySelector('#' + gridId + '-grid');
|
|
257
|
+
if (!root) return -1;
|
|
258
|
+
let n = 0;
|
|
259
|
+
root.querySelectorAll('.ag-row').forEach(row => {
|
|
260
|
+
const bg = (row.getAttribute('style') || '').toLowerCase();
|
|
261
|
+
if (predicate(bg)) n++;
|
|
262
|
+
});
|
|
263
|
+
return n;
|
|
264
|
+
}
|
|
265
|
+
// Match a hex color by translating to the rgb() AG Grid leaves on style="".
|
|
266
|
+
function hexToRgb(hex) {
|
|
267
|
+
hex = hex.replace('#', '');
|
|
268
|
+
if (hex.length === 3) hex = hex.split('').map(c => c + c).join('');
|
|
269
|
+
const num = parseInt(hex, 16);
|
|
270
|
+
return 'rgb(' + ((num >> 16) & 255) + ', ' + ((num >> 8) & 255) + ', ' + (num & 255) + ')';
|
|
271
|
+
}
|
|
272
|
+
function hasBg(color) {
|
|
273
|
+
const rgb = hexToRgb(color);
|
|
274
|
+
return bg => bg.includes(rgb) || bg.includes(color.toLowerCase());
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Inject the legacy object-form rstyles directly onto the component, then trigger a
|
|
278
|
+
// re-render — this is the only way to exercise the backwards-compat branch in 2.1.4
|
|
279
|
+
// because `dmx-bind:rstyles="{…}"` is coerced to [] by AppConnect's Array type schema.
|
|
280
|
+
function primeLegacy() {
|
|
281
|
+
if (!window.dmx || !dmx.app || !dmx.app.get) return setTimeout(primeLegacy, 100);
|
|
282
|
+
const c = dmx.app.get('rsLegacy');
|
|
283
|
+
if (!c) return setTimeout(primeLegacy, 100);
|
|
284
|
+
if (!c.__primed) {
|
|
285
|
+
const legacy = {
|
|
286
|
+
sales: { field: 'department', condition: 'Sales', customColor: '#e9d5ff' },
|
|
287
|
+
hr: { field: 'department', condition: 'HR', customColor: '#fef9c3' }
|
|
288
|
+
};
|
|
289
|
+
// Reach the underlying component instance so we can mutate props.rstyles and call
|
|
290
|
+
// refreshGrid — `c` is the reactive proxy, not the component.
|
|
291
|
+
const inst = document.getElementById('rsLegacy');
|
|
292
|
+
const comp = inst && inst.dmxComponent;
|
|
293
|
+
if (comp) {
|
|
294
|
+
comp.props.rstyles = legacy;
|
|
295
|
+
if (typeof comp.refreshGrid === 'function') {
|
|
296
|
+
const gi = comp.refreshGrid();
|
|
297
|
+
comp.set('gridInstance', gi);
|
|
298
|
+
}
|
|
299
|
+
c.__primed = true;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
primeLegacy();
|
|
304
|
+
|
|
305
|
+
// Wait for ready then evaluate every rstyles scenario and store outcomes for the runner.
|
|
306
|
+
function runChecks() {
|
|
307
|
+
if (document.body.dataset.gridsReady !== 'true') return setTimeout(runChecks, 200);
|
|
308
|
+
// Read fixtures here — they're populated by the deferred sample-data.js, so capturing
|
|
309
|
+
// them at module scope would freeze them at [] before that script ran.
|
|
310
|
+
const employees = (window.SampleData && window.SampleData.employees) || [];
|
|
311
|
+
if (!employees.length) return setTimeout(runChecks, 200);
|
|
312
|
+
// Wait an extra cycle so the legacy-form re-render lands before we count rows.
|
|
313
|
+
if (!document.querySelector('#rsLegacy-grid .ag-row [col-id]')) return setTimeout(runChecks, 200);
|
|
314
|
+
|
|
315
|
+
// Expected hand-counts derived from sample-data.js
|
|
316
|
+
const vipRows = employees.filter(r => r.rating >= 4.7 && r.active === true).length;
|
|
317
|
+
const highSalary = employees.filter(r => r.salary > 90000).length;
|
|
318
|
+
const lowSalary = employees.filter(r => r.salary <= 65000).length;
|
|
319
|
+
const activeEng = employees.filter(r => r.active === true && r.department === 'Engineering').length;
|
|
320
|
+
const marketingRows = employees.filter(r => r.department === 'Marketing').length;
|
|
321
|
+
const over95 = employees.filter(r => r.salary > 95000).length;
|
|
322
|
+
const between90_95 = employees.filter(r => r.salary > 90000 && r.salary <= 95000).length;
|
|
323
|
+
const salesRows = employees.filter(r => r.department === 'Sales').length;
|
|
324
|
+
const hrRows = employees.filter(r => r.department === 'HR').length;
|
|
325
|
+
|
|
326
|
+
const checks = {
|
|
327
|
+
rsFn: { expect: vipRows, actual: bgCount('rsFn', hasBg('#fef3c7')) },
|
|
328
|
+
rsOp_high: { expect: highSalary, actual: bgCount('rsOp', hasBg('#d1fae5')) },
|
|
329
|
+
rsOp_low: { expect: lowSalary, actual: bgCount('rsOp', hasBg('#fee2e2')) },
|
|
330
|
+
rsCompound: { expect: activeEng, actual: bgCount('rsCompound', hasBg('#dbeafe')) },
|
|
331
|
+
rsShort: { expect: marketingRows, actual: bgCount('rsShort', hasBg('#fce7f3')) },
|
|
332
|
+
rsFirst_top: { expect: over95, actual: bgCount('rsFirst', hasBg('#fed7aa')) },
|
|
333
|
+
rsFirst_mid: { expect: between90_95, actual: bgCount('rsFirst', hasBg('#fef9c3')) },
|
|
334
|
+
rsLegacy_sl: { expect: salesRows, actual: bgCount('rsLegacy', hasBg('#e9d5ff')) },
|
|
335
|
+
rsLegacy_hr: { expect: hrRows, actual: bgCount('rsLegacy', hasBg('#fef9c3')) }
|
|
336
|
+
};
|
|
337
|
+
// cstyles: text-area uses inline color: rgb(...) on the cell, cell-area uses background-color
|
|
338
|
+
const csTextHigh = (() => {
|
|
339
|
+
const root = document.querySelector('#csCell-grid');
|
|
340
|
+
if (!root) return -1;
|
|
341
|
+
let n = 0;
|
|
342
|
+
const targetRgb = hexToRgb('#047857');
|
|
343
|
+
root.querySelectorAll('[col-id="salary"]').forEach(cell => {
|
|
344
|
+
const s = (cell.getAttribute('style') || '').toLowerCase();
|
|
345
|
+
if (s.includes(targetRgb)) n++;
|
|
346
|
+
});
|
|
347
|
+
return n;
|
|
348
|
+
})();
|
|
349
|
+
const csCellLow = (() => {
|
|
350
|
+
const root = document.querySelector('#csCell-grid');
|
|
351
|
+
if (!root) return -1;
|
|
352
|
+
let n = 0;
|
|
353
|
+
const targetRgb = hexToRgb('#fee2e2');
|
|
354
|
+
root.querySelectorAll('[col-id="salary"]').forEach(cell => {
|
|
355
|
+
const s = (cell.getAttribute('style') || '').toLowerCase();
|
|
356
|
+
if (s.includes(targetRgb)) n++;
|
|
357
|
+
});
|
|
358
|
+
return n;
|
|
359
|
+
})();
|
|
360
|
+
checks.cs_textHigh = { expect: highSalary, actual: csTextHigh };
|
|
361
|
+
checks.cs_cellLow = { expect: lowSalary, actual: csCellLow };
|
|
362
|
+
|
|
363
|
+
const failed = Object.entries(checks).filter(([_, v]) => v.actual !== v.expect);
|
|
364
|
+
window.__rsCheck = { checks, ok: failed.length === 0, failed: failed.map(([k]) => k) };
|
|
365
|
+
|
|
366
|
+
const box = document.getElementById('rs-check');
|
|
367
|
+
box.textContent = Object.entries(checks)
|
|
368
|
+
.map(([k, v]) => (v.actual === v.expect ? '✓' : '✗') + ' ' + k + ' — expected ' + v.expect + ', got ' + v.actual)
|
|
369
|
+
.join('\n');
|
|
370
|
+
}
|
|
371
|
+
runChecks();
|
|
372
|
+
</script>
|
|
373
|
+
</body>
|
|
374
|
+
</html>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>14 · Compact View & Extended Buttons</title>
|
|
6
|
+
<link rel="stylesheet" href="./lib/test-suite.css">
|
|
7
|
+
<link rel="stylesheet" href="../node_modules/ag-grid-community/styles/ag-theme-alpine.css">
|
|
8
|
+
<link rel="stylesheet" href="../ag-theme-custom.css">
|
|
9
|
+
<link rel="stylesheet" href="../switch-toggle-slider.css">
|
|
10
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
11
|
+
<script src="./dmxAppConnect/dmxAppConnect.js" defer></script>
|
|
12
|
+
<script src="./lib/version-banner.js" defer></script>
|
|
13
|
+
<script src="./lib/test-helpers.js" defer></script>
|
|
14
|
+
<script src="./lib/page-template.js" defer></script>
|
|
15
|
+
<script src="../node_modules/ag-grid-community/dist/ag-grid-community.min.js" defer></script>
|
|
16
|
+
<script src="../dmx-ag-grid.js" defer></script>
|
|
17
|
+
<script src="./data/sample-data.js" defer></script>
|
|
18
|
+
</head>
|
|
19
|
+
<body is="dmx-app" id="page14">
|
|
20
|
+
|
|
21
|
+
<script>
|
|
22
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
23
|
+
TestPage.header({
|
|
24
|
+
title: '14 · Compact View & Extended Buttons',
|
|
25
|
+
summary: 'compact_view layout; buttons 6–15 wired up to verify the extended button slots and class toggles.'
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<main class="test-main">
|
|
31
|
+
<dmx-test-data id="rows" source="employees"></dmx-test-data>
|
|
32
|
+
<dmx-event-log id="evLog"></dmx-event-log>
|
|
33
|
+
|
|
34
|
+
<section class="feature">
|
|
35
|
+
<h2>Compact view mode</h2>
|
|
36
|
+
<p class="desc"><code>compact_view=true</code> with a 3-column grid, row height 20px.</p>
|
|
37
|
+
<div class="grid-wrap">
|
|
38
|
+
<dmx-ag-grid
|
|
39
|
+
id="compactGrid"
|
|
40
|
+
dmx-bind:data="rows.value"
|
|
41
|
+
grid_theme="ag-theme-alpine"
|
|
42
|
+
dmx-bind:compact_view="true"
|
|
43
|
+
dmx-bind:compact_view_grid_size="3"
|
|
44
|
+
dmx-bind:compact_view_item_height="20"
|
|
45
|
+
dmx-bind:pagination="false">
|
|
46
|
+
</dmx-ag-grid>
|
|
47
|
+
</div>
|
|
48
|
+
</section>
|
|
49
|
+
|
|
50
|
+
<section class="feature">
|
|
51
|
+
<h2>Buttons 6 – 15 + class toggles</h2>
|
|
52
|
+
<p class="desc">
|
|
53
|
+
Exercises the upper range of custom action buttons + the
|
|
54
|
+
<code>action_button_class_toggles</code> / <code>action_button_icon_class_toggles</code> arrays.
|
|
55
|
+
</p>
|
|
56
|
+
<div class="grid-wrap">
|
|
57
|
+
<dmx-ag-grid
|
|
58
|
+
id="extButtons"
|
|
59
|
+
dmx-bind:data="rows.value"
|
|
60
|
+
grid_theme="ag-theme-alpine"
|
|
61
|
+
dmx-bind:enable_actions="true"
|
|
62
|
+
actions_column_position="right"
|
|
63
|
+
dmx-bind:pin_actions="true"
|
|
64
|
+
dmx-bind:enable_custom_action_btns="true"
|
|
65
|
+
dmx-bind:button6_action_btn="true" button6_action_tooltip="Link" button6_action_icon_class="fas fa-link"
|
|
66
|
+
dmx-bind:button7_action_btn="true" button7_action_tooltip="DL" button7_action_icon_class="fas fa-download"
|
|
67
|
+
dmx-bind:button8_action_btn="true" button8_action_tooltip="PDF" button8_action_icon_class="fas fa-file-pdf"
|
|
68
|
+
dmx-bind:button9_action_btn="true" button9_action_tooltip="Star" button9_action_icon_class="fas fa-star"
|
|
69
|
+
dmx-bind:button10_action_btn="true" button10_action_tooltip="Trash" button10_action_icon_class="fas fa-trash-alt"
|
|
70
|
+
dmx-bind:button11_action_btn="true" button11_action_tooltip="Cog" button11_action_icon_class="fas fa-cog"
|
|
71
|
+
dmx-bind:button12_action_btn="true" button12_action_tooltip="Search" button12_action_icon_class="fas fa-search"
|
|
72
|
+
dmx-bind:button13_action_btn="true" button13_action_tooltip="Like" button13_action_icon_class="fas fa-thumbs-up"
|
|
73
|
+
dmx-bind:button14_action_btn="true" button14_action_tooltip="Bell" button14_action_icon_class="fas fa-bell"
|
|
74
|
+
dmx-bind:button15_action_btn="true" button15_action_tooltip="Ban" button15_action_icon_class="fas fa-ban"
|
|
75
|
+
dmx-bind:action_button_class_toggles="[{btn_id:'button6', condition:'active', class:'btn-success'}]"
|
|
76
|
+
dmx-bind:action_button_icon_class_toggles="[{btn_id:'button9', condition:'rating >= 4.5', class:'fa-spin'}]"
|
|
77
|
+
dmx-bind:pagination="false"
|
|
78
|
+
dmx-on:row_action_button6="evLog.add('row_action_button6')"
|
|
79
|
+
dmx-on:row_action_button10="evLog.add('row_action_button10')"
|
|
80
|
+
dmx-on:row_action_button15="evLog.add('row_action_button15')">
|
|
81
|
+
</dmx-ag-grid>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="kv">
|
|
84
|
+
<div class="k">last action</div><div class="v" dmx-text="evLog.last.name">—</div>
|
|
85
|
+
<div class="k">total actions</div><div class="v" dmx-text="evLog.count">0</div>
|
|
86
|
+
</div>
|
|
87
|
+
</section>
|
|
88
|
+
|
|
89
|
+
<section class="feature">
|
|
90
|
+
<h2>Cell + row events explicitly enabled</h2>
|
|
91
|
+
<p class="desc">
|
|
92
|
+
<code>cell_click_event</code> and <code>row_double_click_event</code> with actions enabled — cell clicks
|
|
93
|
+
report as cell events instead of row events.
|
|
94
|
+
</p>
|
|
95
|
+
<div class="grid-wrap">
|
|
96
|
+
<dmx-ag-grid
|
|
97
|
+
id="cellEvtGrid"
|
|
98
|
+
dmx-bind:data="rows.value"
|
|
99
|
+
grid_theme="ag-theme-alpine"
|
|
100
|
+
dmx-bind:enable_actions="true"
|
|
101
|
+
dmx-bind:edit_action_btn="true"
|
|
102
|
+
dmx-bind:cell_click_event="true"
|
|
103
|
+
dmx-bind:row_double_click_event="true"
|
|
104
|
+
dmx-bind:pagination="false"
|
|
105
|
+
dmx-on:cell_clicked="evLog.add('cell_clicked')"
|
|
106
|
+
dmx-on:row_double_clicked="evLog.add('row_double_clicked')">
|
|
107
|
+
</dmx-ag-grid>
|
|
108
|
+
</div>
|
|
109
|
+
</section>
|
|
110
|
+
</main>
|
|
111
|
+
</body>
|
|
112
|
+
</html>
|
package/tests/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# AG Grid · Wappler Module · Browser Test Suite
|
|
2
|
+
|
|
3
|
+
End-to-end UI tests for `<dmx-ag-grid>` driven by the
|
|
4
|
+
[agent-browser](https://www.npmjs.com/package/agent-browser) CLI. Works on Windows
|
|
5
|
+
and Linux/macOS.
|
|
6
|
+
|
|
7
|
+
## Quick run
|
|
8
|
+
|
|
9
|
+
From the repo root:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# 1. start the static server (project root must be served; tests/* uses relative
|
|
13
|
+
# paths to ../node_modules and ../dmx-ag-grid.js)
|
|
14
|
+
node tests/serve.cjs # http://localhost:8765/tests/index.html
|
|
15
|
+
|
|
16
|
+
# 2. drive every scenario, capture findings.json
|
|
17
|
+
# recommended — headless Chrome via puppeteer-core (faster, more reliable):
|
|
18
|
+
node tests/run-all-puppeteer.cjs
|
|
19
|
+
# alternative — drives the agent-browser CLI (Windows + Linux):
|
|
20
|
+
node tests/run-all.cjs
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Both produce the same `tests/findings.json` and per-page status table.
|
|
24
|
+
See `FINDINGS.md` for the last run's results and the bugs that were patched
|
|
25
|
+
along the way.
|
|
26
|
+
|
|
27
|
+
A human can also open `http://localhost:8765/tests/index.html` to click through
|
|
28
|
+
scenarios manually.
|
|
29
|
+
|
|
30
|
+
## Layout
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
tests/
|
|
34
|
+
├── README.md ← this file
|
|
35
|
+
├── VERSIONS.json ← pinned dependency versions
|
|
36
|
+
├── FINDINGS.md ← last run's results + bugs/fixes
|
|
37
|
+
├── findings.json ← machine-readable last run output
|
|
38
|
+
├── index.html ← scenario directory
|
|
39
|
+
├── 01-basic-render.html ← default columns, sort, filter, pagination
|
|
40
|
+
├── 02-themes-locale.html ← all 5 themes + dark + RTL + locale
|
|
41
|
+
├── 03-editing.html ← cell + row + static-select editors
|
|
42
|
+
├── 04-selection.html ← single/multi selection + checkbox + status
|
|
43
|
+
├── 05-action-buttons.html ← edit/view/delete + button1..5 + conditions
|
|
44
|
+
├── 06-export.html ← CSV / XLSX / PDF
|
|
45
|
+
├── 07-import.html ← importFileData (CSV + XLSX)
|
|
46
|
+
├── 08-formatting.html ← amount, date, ctypes, cnames, cwidths
|
|
47
|
+
├── 09-grouping.html ← group_config + columns_to_sum/count
|
|
48
|
+
├── 10-state-persistence.html ← saveColumnState + applyFilters
|
|
49
|
+
├── 11-layout.html ← autoHeight, fixed_header, scroll
|
|
50
|
+
├── 12-methods.html ← every imperative method as a button
|
|
51
|
+
├── 13-flags-styles.html ← suppress_*, rstyles, cstyles, data_changes
|
|
52
|
+
├── 14-compact-view.html ← compact_view + buttons 6–15
|
|
53
|
+
├── serve.cjs ← cross-platform static server
|
|
54
|
+
├── run-all.cjs ← agent-browser CLI driver (cross-platform)
|
|
55
|
+
├── run-all-puppeteer.cjs ← headless Chrome driver (recommended)
|
|
56
|
+
├── data/
|
|
57
|
+
│ └── sample-data.js ← employees + orders fixtures
|
|
58
|
+
├── dmxAppConnect/
|
|
59
|
+
│ ├── dmxAppConnect.js ← AppConnect runtime (pinned: 2.2.4)
|
|
60
|
+
│ └── dmxAppConnect.js.map
|
|
61
|
+
└── lib/
|
|
62
|
+
├── test-suite.css ← page chrome styling
|
|
63
|
+
├── version-banner.js ← detects + warns on AppConnect drift
|
|
64
|
+
├── test-helpers.js ← <dmx-test-data>, <dmx-event-log>, TestKit
|
|
65
|
+
├── page-template.js ← header + body.dataset.gridsReady
|
|
66
|
+
├── run-collector.js ← injected via --init-script for headless runs
|
|
67
|
+
└── common-head.html ← reference snippet of the vendor load order
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Readiness signal
|
|
71
|
+
|
|
72
|
+
`page-template.js` sets `document.body.dataset.gridsReady = 'true'` once every
|
|
73
|
+
`<dmx-ag-grid>` on the page has fired `onGridReady` or `onFirstDataRendered`.
|
|
74
|
+
Agents (or the `run-all.cjs` driver) wait on that flag instead of guessing
|
|
75
|
+
timings.
|
|
76
|
+
|
|
77
|
+
## Console helpers
|
|
78
|
+
|
|
79
|
+
Loaded by every scenario:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
TestKit.list() // ['basicGrid', ...]
|
|
83
|
+
TestKit.grid('basicGrid') // reactive data proxy
|
|
84
|
+
TestKit.snapshot('basicGrid') // { count, state, selectedRows, … }
|
|
85
|
+
TestKit.callMethod('basicGrid', 'reloadGrid', true) // -> grid.__reloadGrid(true)
|
|
86
|
+
TestKit.waitForGrid('basicGrid') // promise resolving on gridReady
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Note: `dmx.app.get(id)` in AppConnect 2.2.4 returns a reactive data **proxy**
|
|
90
|
+
(direct field access, methods exposed as `__method`), not the component instance.
|
|
91
|
+
`TestKit.callMethod` translates the friendly name automatically.
|
|
92
|
+
|
|
93
|
+
## What's covered
|
|
94
|
+
|
|
95
|
+
| # | Feature group | Notable attrs / methods |
|
|
96
|
+
|----|------------------------|-----------------------------------------------------------------------------------------------|
|
|
97
|
+
| 01 | Basic render | inferred columnDefs, sort, filter, floating_filter, pagination, count binding, quick filter |
|
|
98
|
+
| 02 | Themes & locale | grid_theme × 5, dark_mode, enable_rtl, locale_text (EN/HE/RU/ES/PT) |
|
|
99
|
+
| 03 | Editing | cell_editable, row_editable, editable_fields, cstatic_select_editors |
|
|
100
|
+
| 04 | Selection | row_selection (single/multi), row_checkbox_event, row_status_event, row_clicked |
|
|
101
|
+
| 05 | Action buttons | enable_actions, edit/view/delete + button1..5 + btn_condition |
|
|
102
|
+
| 06 | Export | export_to_csv/xls/pdf + trim + exclude_hidden + remove_html + custom filename |
|
|
103
|
+
| 07 | Import | importFileData(fieldId) for .csv and .xlsx |
|
|
104
|
+
| 08 | Formatting | amount_fields + precision, date_format, ctypes, cnames, cwidths, wrap_header_text |
|
|
105
|
+
| 09 | Grouping & aggregation | group_config, columns_to_sum, columns_to_count, footer_sum_precision |
|
|
106
|
+
| 10 | State persistence | column_state_storage_key, save/reset, applyFilters/getAppliedFilters |
|
|
107
|
+
| 11 | Layout | dom_layout, fixed_header, fixed_footer, fixed_horizontal_scroll, hide_id_field |
|
|
108
|
+
| 12 | Methods | loadGrid, reloadGrid, destroyGrid, pinColumns, hideColumns, getSelectedRows, getFilteredData |
|
|
109
|
+
| 13 | Flags & styles | suppress_*, hide_filters, hide_sort, rstyles, cstyles, data_changes, custom_tooltip, ci_sort |
|
|
110
|
+
| 14 | Compact + ext buttons | compact_view, action_button_class_toggles, buttons 6–15, cell_click_event |
|
|
111
|
+
|
|
112
|
+
## What's intentionally NOT covered
|
|
113
|
+
|
|
114
|
+
- Server-side data sources (`serverConnect`) — substituted by `<dmx-test-data>`
|
|
115
|
+
for static fixtures
|
|
116
|
+
- The 30 Wappler IDE input panels in `app_connect/components.hjson`
|
|
117
|
+
- `dmxS3Upload`, `dmxFormatter`, etc. — those modules were removed from
|
|
118
|
+
`dmxAppConnect/`
|
|
119
|
+
|
|
120
|
+
## Version manifest
|
|
121
|
+
|
|
122
|
+
`VERSIONS.json` records the AppConnect build (currently **2.2.4**, built
|
|
123
|
+
2026-01-19) plus every vendor library. `lib/version-banner.js` logs a warning
|
|
124
|
+
to the console if a different AppConnect drops in.
|
|
125
|
+
|
|
126
|
+
Upgrade flow:
|
|
127
|
+
|
|
128
|
+
1. Replace `tests/dmxAppConnect/dmxAppConnect.js`
|
|
129
|
+
2. Run `node tests/run-all.cjs`
|
|
130
|
+
3. If `tests/findings.json` still shows all PASS, bump `appConnect.expected` in
|
|
131
|
+
`VERSIONS.json`. Otherwise file the regressions in `FINDINGS.md`.
|
|
132
|
+
|
|
133
|
+
## Cross-platform notes
|
|
134
|
+
|
|
135
|
+
- `run-all.cjs` invokes `agent-browser` via `shell:true`. On Windows that picks
|
|
136
|
+
up `agent-browser.cmd`; on Linux/macOS it finds `agent-browser` from `$PATH`.
|
|
137
|
+
Override the binary via `AGENT_BROWSER_BIN=…`
|
|
138
|
+
- `serve.cjs` is plain Node http — no Windows-only paths
|
|
139
|
+
- Test pages use `/` paths everywhere
|
|
140
|
+
|
|
141
|
+
## Known limitations of the run-all driver
|
|
142
|
+
|
|
143
|
+
- agent-browser maintains a single Chrome session; navigating between 14 pages
|
|
144
|
+
can occasionally trip a CDP timeout. The driver retries each scenario once
|
|
145
|
+
after a daemon close. If a scenario still fails, run it in isolation by
|
|
146
|
+
opening the URL directly.
|