fix(client): guard the DNA bar against a stale hover key
A snapshot push can drop the band under a resting pointer (compaction, tail slide out of the served window) without a mouseleave, and the dead key exact-matched nothing: the whole strip sat dimmed with nothing lit — the very state the open-category pin's breakdown gate rules out. Honor a band hover only while its key names a rendered band, so the mirror/pin fallbacks resume underneath. Also correct the hover-link comment: DNA hovers stay on the bar and never report back to the overview (the link is one-way in DNA mode, as the existing test already pins).
This commit is contained in:
1 parent
5af019ef02
commit
40a054b99b
2 files changed
+36
-5
No files matched your search
@@ -746,6 +746,10 @@ export function makeContextBrowser(
|
||||
label: dnaLabel(it),
|
||||
group: it.cat,
|
||||
})) ?? null
|
||||
// A band hover is honored only while its key names a RENDERED band: a push can drop the band under a resting
|
||||
// pointer (compaction, tail slide) without a mouseleave, and a dead key exact-matches nothing — the bar would
|
||||
// sit all-dimmed with nothing lit (the same invariant the pin's `breakdown` gate keeps).
|
||||
const liveDnaKey = dnaKey !== null && dnaByKey.has(dnaKey) ? dnaKey : null
|
||||
// A band click opens its category + element row below (the same reveal the step brief uses) and scrolls it into view.
|
||||
const pickDna = (key: string): void => {
|
||||
const it = dnaByKey.get(key)
|
||||
@@ -1051,11 +1055,12 @@ export function makeContextBrowser(
|
||||
parts={dnaParts ?? parts}
|
||||
height={10}
|
||||
// Highlight precedence: pointer hover (local in DNA mode, the shared link otherwise) over the open-category pin.
|
||||
// The mirrored hover link (see `linked` above) works while the browser shows the live surface — in DNA mode an incoming
|
||||
// category key lights that category's BANDS (the parts' `group`) — and reports hovers back to the overview. Tip stays off
|
||||
// in category mode: a cross-card hover must not float a second tooltip over a bar the pointer does not rest on — and the
|
||||
// group-keyed pin never exact-matches a DNA band, so the pinned highlight floats no tooltip either.
|
||||
hoverKey={dna ? dnaKey ?? linkKey ?? pinKey : linkKey ?? pinKey}
|
||||
// The mirrored link (see `linked` above) works while the browser shows the live surface; it is ONE-WAY in DNA
|
||||
// mode — an incoming category key lights that category's BANDS (the parts' `group`), while band hovers stay
|
||||
// on the bar and never report upward (the overview keeps showing the live category composition). Tip stays
|
||||
// off in category mode: a cross-card hover must not float a second tooltip over a bar the pointer does not
|
||||
// rest on — and the group-keyed pin never exact-matches a DNA band, so the pinned highlight floats no tip.
|
||||
hoverKey={dna ? liveDnaKey ?? linkKey ?? pinKey : linkKey ?? pinKey}
|
||||
onHoverKey={dna ? setDnaKey : linked ? props.onHoverKey : undefined}
|
||||
tip={dna}
|
||||
onPickKey={dna ? pickDna : undefined}
|
||||
|
||||
@@ -1754,6 +1754,32 @@ describe('ContextBrowser DNA mode and the open-category bar pin', () => {
|
||||
await m.unmount()
|
||||
})
|
||||
|
||||
test('a push dropping the hovered band does not dim the bar with nothing lit; the pin resumes underneath', async () => {
|
||||
const m = await mount(h(Browser, props({ data: dnaData, headers: dnaHeaders })))
|
||||
await click(dnaButton(m))
|
||||
await hover(bands(m)[3])
|
||||
assert.ok(bands(m)[3].className.includes('lc-stacked-seg-on'))
|
||||
assert.ok(query(m.container, '.lc-stacked').className.includes('lc-stacked-dim'))
|
||||
// The hovered node is compacted out of the live surface while the pointer rests on its band — a removed element
|
||||
// fires no mouseleave, so the stale key must be dropped instead of dimming the whole bar with nothing lit.
|
||||
const pushed = tl({
|
||||
current: { ...dnaData.current, user: 0, total: 255 },
|
||||
requests: dnaData.requests,
|
||||
nodes: dnaData.nodes.filter(n => n.seq !== 2),
|
||||
})
|
||||
await m.update(h(Browser, props({ data: pushed, headers: dnaHeaders })))
|
||||
assert.ok(!query(m.container, '.lc-stacked').className.includes('lc-stacked-dim'))
|
||||
assert.ok(bands(m).every(seg => !seg.className.includes('lc-stacked-seg-on')))
|
||||
assert.ok(!query(m.container, '.lc-bar-tip').className.includes('lc-bar-tip-on'), 'the dead key floats no tooltip either')
|
||||
// Band order is now sys, bash, write, n3, n4, n5: the open-category pin still lights its group under the stale hover.
|
||||
await click(catRow(m, 'assistant'))
|
||||
assert.deepEqual(bands(m).map(seg => seg.className.includes('lc-stacked-seg-on')), [false, false, false, false, true, false])
|
||||
// And a fresh hover on a rendered band still wins over the pin.
|
||||
await hover(bands(m)[3])
|
||||
assert.deepEqual(bands(m).map(seg => seg.className.includes('lc-stacked-seg-on')), [false, false, false, true, false, false])
|
||||
await m.unmount()
|
||||
})
|
||||
|
||||
test('the pin drops while the previewed step holds nothing for the open category', async () => {
|
||||
const wired = (previewSeq: number | null) => h(Browser, props({ data: dnaData, headers: dnaHeaders, previewSeq, onHoverKey: () => {} }))
|
||||
const m = await mount(wired(null))
|
||||
|
||||
Reference in new issue
Block a user