@cdmx/wappler_ag_grid 2.1.3 → 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/.claude/settings.json +7 -0
- package/CHANGELOG.md +23 -1
- package/app_connect/components.hjson +6 -5
- package/dmx-ag-grid.js +37 -12
- package/package.json +5 -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,93 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>12 · Component Methods</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
|
+
</head>
|
|
18
|
+
<body is="dmx-app" id="page12">
|
|
19
|
+
|
|
20
|
+
<script>
|
|
21
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
22
|
+
TestPage.header({
|
|
23
|
+
title: '12 · Component Methods',
|
|
24
|
+
summary: 'Every imperative method exposed in components.hjson, wired to a button so agents can fuzz them.'
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<main class="test-main">
|
|
30
|
+
<dmx-test-data id="rows" source="employees"></dmx-test-data>
|
|
31
|
+
|
|
32
|
+
<section class="feature">
|
|
33
|
+
<h2>Method playground</h2>
|
|
34
|
+
<p class="desc">Each button calls a documented method. Watch the binding row + console for effects.</p>
|
|
35
|
+
|
|
36
|
+
<div class="grid-wrap">
|
|
37
|
+
<dmx-ag-grid
|
|
38
|
+
id="m"
|
|
39
|
+
dmx-bind:data="rows.value"
|
|
40
|
+
grid_theme="ag-theme-alpine"
|
|
41
|
+
quick_filter_field="qf_input"
|
|
42
|
+
dmx-bind:pagination="true"
|
|
43
|
+
dmx-bind:pagination_page_size="10">
|
|
44
|
+
</dmx-ag-grid>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="controls" style="margin-top: 12px;">
|
|
48
|
+
<button class="primary" onclick="TestKit.callMethod('m','loadGrid')">loadGrid()</button>
|
|
49
|
+
<button onclick="TestKit.callMethod('m','reloadGrid', true)">reloadGrid(true)</button>
|
|
50
|
+
<button onclick="TestKit.callMethod('m','destroyGrid')">destroyGrid()</button>
|
|
51
|
+
<button onclick="TestKit.callMethod('m','pinColumns','first_name')">pinColumns('first_name')</button>
|
|
52
|
+
<button onclick="TestKit.callMethod('m','hideColumns','email')">hideColumns('email')</button>
|
|
53
|
+
<button onclick="TestKit.callMethod('m','getSelectedRows')">getSelectedRows()</button>
|
|
54
|
+
<button onclick="TestKit.callMethod('m','getAppliedFilters')">getAppliedFilters()</button>
|
|
55
|
+
<button onclick="TestKit.callMethod('m','getFilteredData')">getFilteredData()</button>
|
|
56
|
+
<button onclick="programmaticFilter()">applyFilters({department:'Sales'})</button>
|
|
57
|
+
<button onclick="TestKit.callMethod('m','clearFilters')">clearFilters()</button>
|
|
58
|
+
<button onclick="TestKit.callMethod('m','saveColumnState')">saveColumnState()</button>
|
|
59
|
+
<button onclick="TestKit.callMethod('m','resetColumnState')">resetColumnState()</button>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div class="controls">
|
|
63
|
+
<input type="text" id="qf_input" placeholder="quick filter text" oninput="TestKit.callMethod('m','quickFilter')">
|
|
64
|
+
<button onclick="dump()">log component state</button>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="kv">
|
|
68
|
+
<div class="k">count</div><div class="v" dmx-text="m.count">0</div>
|
|
69
|
+
<div class="k">selectedRows.length</div><div class="v" dmx-text="m.selectedRows.length">0</div>
|
|
70
|
+
<div class="k">filterState</div><div class="v" dmx-text="m.filterState">{}</div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="log" id="cli-log"></div>
|
|
74
|
+
</section>
|
|
75
|
+
</main>
|
|
76
|
+
|
|
77
|
+
<script>
|
|
78
|
+
function programmaticFilter() {
|
|
79
|
+
TestKit.callMethod('m', 'applyFilters', {
|
|
80
|
+
department: { type: 'equals', filter: 'Sales' }
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function dump() {
|
|
84
|
+
const snap = TestKit.snapshot('m');
|
|
85
|
+
const box = document.getElementById('cli-log');
|
|
86
|
+
const e = document.createElement('div');
|
|
87
|
+
e.className = 'log-entry';
|
|
88
|
+
e.textContent = new Date().toISOString().slice(11,19) + ' → ' + JSON.stringify(snap);
|
|
89
|
+
box.insertBefore(e, box.firstChild);
|
|
90
|
+
}
|
|
91
|
+
</script>
|
|
92
|
+
</body>
|
|
93
|
+
</html>
|
|
@@ -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>
|