feat(ui): render the map source chips and status badge from the store

createMapSourceControls() (./ui/maps) no longer builds the chip row or
writes the status badge: it presents both and hands them to its publish
option, so the entry stays Vue-free. The shell publishes them to the
gev-display store and connects the controls, and the dock renders the
chips and calls select() for an available one. renderMapStackChips and
syncMapStackChips are deprecated. The map source QA harnesses wait for
the dock to render and no longer read the removed element refs.
This commit is contained in:
Sameh Khamis committed 2026-10-31 12:11:35 -07:00
1 parent decac29fbd
commit 4966877859
13 files changed
+233 -155

No files matched your search

+74 -1
View File
@@ -31,7 +31,7 @@ function fixture(t) {
[...document.querySelectorAll('.style-btn.active')].map(
(button) => button.dataset.style,
);
return { $, settings, active };
return { $, settings, active, pinia };
}
test('before VisualSettings publishes, the dock shows NORMAL', (t) => {
@@ -79,3 +79,76 @@ test('the dock and the corner label the style in the UI language', async (t) =>
assert.equal($('#style-mini-value').textContent, 'НУАР');
assert.equal($('#active-style-name').textContent, 'НУАР');
});
const chipView = (activeId, { warn = false, text = 'OSM' } = {}) => ({
chips: [
{
id: 'bing-aerial',
label: 'Bing',
available: false,
active: false,
requirement: 'ION',
title: 'Needs a key',
ariaLabel: 'Bing unavailable: Needs a key',
},
...['esri-imagery', 'osm'].map((id) => ({
id,
label: id.toUpperCase(),
available: true,
active: id === activeId,
requirement: '',
title: id.toUpperCase(),
ariaLabel: null,
})),
],
status: { text, warn },
});
test('the map source row renders the published chips and selects through the controls', async (t) => {
const { $, pinia } = fixture(t);
assert.equal($('#map-stack-status').textContent, '3D');
assert.equal(document.querySelectorAll('.map-stack-chip').length, 0);
const store = useDisplayStore(pinia);
const selected = [];
store.connectMapSource({
select: async (id) => selected.push(id),
});
store.publishMapSource(chipView('osm'));
await nextTick();
const chips = [
...document.querySelectorAll('#map-stack-chips .map-stack-chip'),
];
assert.deepEqual(
chips.map((chip) => chip.dataset.stackId),
['bing-aerial', 'esri-imagery', 'osm'],
);
const [bing, esri, osm] = chips;
assert.equal(bing.classList.contains('unavailable'), true);
assert.equal(bing.getAttribute('aria-disabled'), 'true');
assert.equal(
bing.getAttribute('aria-label'),
'Bing unavailable: Needs a key',
);
assert.equal(bing.title, 'Needs a key');
assert.equal(bing.querySelector('.map-stack-chip-req').textContent, 'ION');
assert.equal(osm.classList.contains('active'), true);
assert.equal(osm.getAttribute('aria-pressed'), 'true');
assert.equal(osm.hasAttribute('aria-label'), false);
assert.equal(esri.querySelector('.map-stack-chip-req'), null);
bing.click();
esri.click();
assert.deepEqual(selected, ['esri-imagery']);
esri.classList.add('voice-highlight');
store.publishMapSource(chipView('esri-imagery', { text: '...', warn: true }));
await nextTick();
assert.equal(esri.classList.contains('active'), true);
assert.equal(esri.classList.contains('voice-highlight'), true);
assert.equal(osm.classList.contains('active'), false);
assert.equal($('#map-stack-status').textContent, '...');
assert.equal($('#map-stack-status').classList.contains('warn'), true);
store.publishMapSource(null);
await nextTick();
assert.equal($('#map-stack-status').textContent, '3D');
assert.equal($('#map-stack-status').classList.contains('warn'), false);
assert.equal(document.querySelectorAll('.map-stack-chip').length, 0);
});