ui: bordered header badges and ICCID-labelled card actions (v2.6.2)

- ADM/SCP80/SCP81 header indicators become bordered chips (neutral gray
  border, rounded, px-1.5 py-0.5); SCP80/SCP81 get an even ml-1 gap, so no
  separator glyph is needed
- profiler/snapshot card actions carry the equipped card's ICCID:
  'Profile from card <ICCID>', 'Check card <ICCID>' (row button, arrow
  dropped), 'New snapshot: <ICCID>'; pysimUpdateCardActionLabels() fills
  the spans on every status poll and after a language switch
- these buttons switch to data-needs='card-iccid': disabled without a
  readable ICCID ('Card equipped but its ICCID is not readable' hint)
- profiler rows wrap so the longer button cannot break the layout;
  help EN/RU updated; SW cache simple-v202
This commit is contained in:
2026-09-20 01:40:39 +03:00
parent 97bc8f929d
commit 81b50199cc
8 changed files with 65 additions and 17 deletions
+9
View File
@@ -61,6 +61,15 @@ test('the ADM field sits between From card and Add, outside the card fieldsets',
'ADM input must sit between the From card and Add buttons');
});
test('card-dependent profiler buttons show the ICCID and need a readable one', () => {
assert.match(html, /id="profiler-from-card-btn" data-needs="card-iccid"/);
assert.match(html, /id="snapshot-new-btn" data-needs="card-iccid"/);
assert.match(html, /data-needs="card-iccid" onclick="profilerCheck/);
// both static buttons carry the dynamic ICCID span
assert.match(html, /id="profiler-from-card-btn"[\s\S]*?<span class="profiler-iccid-label"><\/span><\/button>/);
assert.match(html, /id="snapshot-new-btn"[\s\S]*?<span class="profiler-iccid-label" data-iccid-sep=": "><\/span><\/button>/);
});
test('labelled containers use fieldsets with embedded legends', () => {
const legendCls = '<legend class="px-1 text-xs font-medium text-gray-500 dark:text-slate-400"';
for (const label of ['STK menu', 'STATUS and Polling', 'TERMINAL PROFILE',
+28
View File
@@ -26,6 +26,7 @@ code += extractFunc(html, 'pysimAvailabilityState') + '\n';
code += extractFunc(html, 'pysimControlDisabled') + '\n';
code += extractFunc(html, 'pysimNeedsHint') + '\n';
code += extractFunc(html, 'pysimApplyAvailability') + '\n';
code += extractFunc(html, 'pysimUpdateCardActionLabels') + '\n';
code += extractFunc(html, 'pysimUpdateStateIndicator') + '\n';
code += extractFunc(html, 'pysimUpdateIccidIndicator') + '\n';
code += extractFunc(html, 'pysimUpdatePresetIndicator') + '\n';
@@ -231,3 +232,30 @@ test('indicator image stays within the 32px header row budget', () => {
const size = Number(m[1]);
assert.ok(size >= 24 && size <= 32, 'size ' + size + 'px would change the header height');
});
test('header badges are bordered chips with even spacing', () => {
for (const id of ['state-indicator-adm', 'state-indicator-scp80', 'state-indicator-scp81']) {
const m = new RegExp('id="' + id + '"[^>]*class="([^"]*)"').exec(html);
assert.ok(m, id + ' markup not found');
assert.match(m[1], /\bborder\b/);
assert.match(m[1], /\brounded\b/);
}
assert.match(html, /id="state-indicator-scp80"[^>]*class="[^"]*ml-1/);
assert.match(html, /id="state-indicator-scp81"[^>]*class="[^"]*ml-1/);
});
test('card action labels carry the equipped ICCID', () => {
setup();
const plain = fakeEl();
const colon = fakeEl();
colon.attrs['data-iccid-sep'] = ': ';
globalThis.document.querySelectorAll = sel => sel === '.profiler-iccid-label' ? [plain, colon] : [];
_pysimCardIccid = '89701450001700031958';
pysimUpdateCardActionLabels();
assert.strictEqual(plain.textContent, ' 89701450001700031958');
assert.strictEqual(colon.textContent, ': 89701450001700031958');
_pysimCardIccid = null;
pysimUpdateCardActionLabels();
assert.strictEqual(plain.textContent, '');
assert.strictEqual(colon.textContent, '');
});