From dc9d200725c1a9573dd307d391fd54c5402ea930 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Fri, 30 Oct 2026 23:56:20 -0700 Subject: [PATCH] fix(ui): keep the voice highlight on layer rows Toggle the count-line modifier with a directive that leaves other classes alone, so a re-render no longer clears gev-voice-focus. --- scripts/package-boundaries.json | 3 ++ src/ui/layerPanel/LayerRows.vue | 9 +++--- src/ui/layerPanel/layerRows.test.mjs | 46 ++++++++++++++++++++++++++++ src/ui/rail/classToggle.js | 11 +++++++ 4 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 src/ui/layerPanel/layerRows.test.mjs create mode 100644 src/ui/rail/classToggle.js diff --git a/scripts/package-boundaries.json b/scripts/package-boundaries.json index 98e7a716b..84cec6de3 100644 --- a/scripts/package-boundaries.json +++ b/scripts/package-boundaries.json @@ -552,6 +552,7 @@ "src/ui/railCardBlocks.js", "src/ui/layerRowText.js", "src/ui/layerPanel/LayerRows.vue", + "src/ui/rail/classToggle.js", "src/ui/layerPanel/RowText.vue", "src/ui/railTimeline.js", "src/ui/clearLayersControl.js", @@ -857,6 +858,7 @@ "src/ui/railCardBlocks.js", "src/ui/layerRowText.js", "src/ui/layerPanel/LayerRows.vue", + "src/ui/rail/classToggle.js", "src/ui/layerPanel/RowText.vue", "src/ui/railTimeline.js", "src/ui/layers.js", @@ -2553,6 +2555,7 @@ "src/ui/railCardBlocks.js", "src/ui/layerRowText.js", "src/ui/layerPanel/LayerRows.vue", + "src/ui/rail/classToggle.js", "src/ui/layerPanel/RowText.vue", "src/ui/railTimeline.js", "src/ui/layers.js", diff --git a/src/ui/layerPanel/LayerRows.vue b/src/ui/layerPanel/LayerRows.vue index e3897ce30..f404938b6 100644 --- a/src/ui/layerPanel/LayerRows.vue +++ b/src/ui/layerPanel/LayerRows.vue @@ -2,6 +2,7 @@ import RailChips from '../rail/RailChips.vue'; import RailRowList from '../rail/RailRowList.vue'; import RowText from './RowText.vue'; +import { vClassToggle } from '../rail/classToggle.js'; // The data layer rows under their group headings. LayerPanel presents every // value; clicks reach it with the layer id, and it re-reads the live @@ -21,10 +22,10 @@ defineProps({ }}
({ + key: 'flights', + id: 'flights', + englishName: 'Flights', + icon: 'flight', + name: 'Flights', + meta: '', + toggle: { + classes: [], + text: 'OFF', + ariaLabel: 'Flights', + title: '', + ariaBusy: 'false', + ariaDisabled: 'false', + feedState: '', + }, + count: { countLine, text: '12', empty: false }, + controls: null, +}); + +test('the count-line class toggles without dropping the voice highlight', () => { + document.body.replaceChildren(); + const container = document.body.appendChild(document.createElement('div')); + render(h(LayerRows, { items: [row(false)] }), container); + const element = container.querySelector('[data-layer-id="flights"]'); + assert.equal(element.className, 'data-toggle-row'); + element.classList.add('gev-voice-focus'); + + render(h(LayerRows, { items: [row(true)] }), container); + assert.equal(element.classList.contains('data-toggle-row--count-line'), true); + assert.equal(element.classList.contains('gev-voice-focus'), true); + + render(h(LayerRows, { items: [row(false)] }), container); + assert.equal(element.classList.contains('data-toggle-row--count-line'), false); + assert.equal(element.classList.contains('gev-voice-focus'), true); + assert.equal(element.classList.contains('data-toggle-row'), true); + render(null, container); +}); diff --git a/src/ui/rail/classToggle.js b/src/ui/rail/classToggle.js new file mode 100644 index 000000000..53383691f --- /dev/null +++ b/src/ui/rail/classToggle.js @@ -0,0 +1,11 @@ +/** + * `v-class-toggle`: toggles each named class with `classList`, so classes other + * code adds to the element (a voice highlight) survive a re-render. A `:class` + * binding rewrites the whole `className` instead. + */ +const apply = (el, { value }) => { + for (const [name, on] of Object.entries(value || {})) + if (el.classList.contains(name) !== Boolean(on)) + el.classList.toggle(name, Boolean(on)); +}; +export const vClassToggle = { mounted: apply, updated: apply };