diff --git a/src/client/components/contextView.tsx b/src/client/components/contextView.tsx index 917c275..b17e6cf 100644 --- a/src/client/components/contextView.tsx +++ b/src/client/components/contextView.tsx @@ -66,14 +66,13 @@ function TrendReserved(props: { children: ReactNode; catLabel: (cat: Category |
-
+
{CATS.map(c => ( -
+ - {props.catLabel(c.key)} - - -
+ {props.catLabel(c.key)} + + ))}
@@ -94,7 +93,7 @@ export function makeContextView( const CurrentComposition = makeCurrentComposition(kit, StackedBar, Legend) const TrendChart = makeTrendChart(kit) const TrendOverview = makeTrendOverview(kit) - const RequestDetail = makeRequestDetail(kit, StackedBar) + const RequestDetail = makeRequestDetail(kit, StackedBar, Legend) const EventList = makeEventList(kit) const FileCard = makeFileCard(kit, settings) // The stats board's subagent-cost cell reads the shared page-scope cold-head cache. @@ -572,6 +571,7 @@ export function makeContextView( stepsOf={stepsOf} onLocate={locateNode} hoverKey={trendHoverCat} + onHoverKey={setHoverCat} />
)} diff --git a/src/client/components/currentComposition.tsx b/src/client/components/currentComposition.tsx index d4cbe33..40dfec2 100644 --- a/src/client/components/currentComposition.tsx +++ b/src/client/components/currentComposition.tsx @@ -1,14 +1,9 @@ import { type ReactElement } from 'react' -import type { PartsPart } from '../categories' import type { Headline } from '../headline' import type { ViewKit } from '../viewkit' -import type { StackedBarProps } from './stackedBar' +import type { StackedBarProps, LegendProps } from './stackedBar' -type LegendFn = (props: { - parts: PartsPart[] - hoverKey?: string | null - onHoverKey?: (key: string | null) => void -}) => ReactElement +type LegendFn = (props: LegendProps) => ReactElement export interface CurrentCompositionProps { head: Headline diff --git a/src/client/components/requestDetail.tsx b/src/client/components/requestDetail.tsx index 33daef1..9d7ee53 100644 --- a/src/client/components/requestDetail.tsx +++ b/src/client/components/requestDetail.tsx @@ -5,7 +5,7 @@ import { cacheHitPercent } from '../format' import type { StepBrief } from '../brief' import { blockSummaryOf, callNamesOf, callSummaryOf } from '../callSummary' import type { ConversationNodeLike } from '../services' -import type { StackedBarProps } from './stackedBar' +import type { StackedBarProps, LegendProps } from './stackedBar' import type { ViewKit } from '../viewkit' export interface RequestDetailProps { @@ -24,16 +24,18 @@ export interface RequestDetailProps { stepsOf?: (turn: number | undefined) => number /** Reveal a brief row's node in the Context browser; absent = rows render inert. */ onLocate?: (node: SurfaceNode, isResponse: boolean) => void - /** Mirrored category hover (shared with the overview/browser): lights the composition bar's matching - * segment; the bar never reports hovers back. */ + /** Category hover shared with the overview/browser and the composition legend: lights the matching bar + * segment and chip, and the chips report hovers back — the bar never does. */ hoverKey?: string | null + onHoverKey?: (key: string | null) => void } export function makeRequestDetail( kit: ViewKit, StackedBar: (props: StackedBarProps) => ReactElement, + Legend: (props: LegendProps) => ReactElement, ): ComponentType { - const { t, fmt, fmtTime, catLabel, eventLabel, eventAt } = kit + const { t, fmt, fmtTime, eventLabel, eventAt } = kit /** One brief row: a fixed-width kind tag plus one glanceable line. The tag carries a styled, instant explanation bubble (the * shared `.lc-tip` chrome); the content span keeps the native title (preview + locate hint), so the two never stack. @@ -213,6 +215,8 @@ export function makeRequestDetail( const parts = delta ? CATS.map((c, i) => ({ key: c.key, color: c.color, value: Math.abs(deltas[i]) })) : partsOf(req) + // Delta's chips print the signed change; the bar's own parts keep only magnitudes. + const legendParts = delta ? parts.map((p, i) => ({ ...p, raw: deltas[i] })) : parts return (
@@ -248,25 +252,7 @@ export function makeRequestDetail( {/* Mirrors the shared category hover with the tip off — a cross-card hover must not float a second tooltip over a bar the pointer does not rest on (same rule as the browser's own bar). */} -
- {CATS.map((c, i) => { - const v = delta ? deltas[i] : req[c.key] || 0 - return ( -
- - {catLabel(c.key)} - {delta ? ( - 0 ? ' lc-detail-num-up' : v < 0 ? ' lc-detail-num-down' : '')}> - {(v > 0 ? '+' : '') + fmt(v)} - - ) : ( - {'≈' + fmt(v)} - )} - {!delta && req.total > 0 ? `${Math.round(v / req.total * 100)}%` : ''} -
- ) - })} -
+
) }) diff --git a/src/client/components/stackedBar.tsx b/src/client/components/stackedBar.tsx index 6a85b52..e0ac29a 100644 --- a/src/client/components/stackedBar.tsx +++ b/src/client/components/stackedBar.tsx @@ -289,17 +289,19 @@ export function makeStackedBar(kit: ViewKit): (props: StackedBarProps) => ReactE } } -export function makeLegend(kit: ViewKit): (props: { +/** The category chips' interface, shared by the composition card's legend and the trend panel. */ +export interface LegendProps { parts: PartsPart[] hoverKey?: string | null onHoverKey?: (key: string | null) => void -}) => ReactElement { + /** Signed figures (the trend panel's Delta mode): the count prints with its sign and the sign's color, + * and the share drops out — a signed change has no share of the used total. */ + delta?: boolean +} + +export function makeLegend(kit: ViewKit): (props: LegendProps) => ReactElement { const { t, fmt, catLabel } = kit - return function Legend(props: { - parts: PartsPart[] - hoverKey?: string | null - onHoverKey?: (key: string | null) => void - }): ReactElement { + return function Legend(props: LegendProps): ReactElement { let total = 0 for (const p of props.parts) total += p.raw ?? p.value return ( @@ -307,19 +309,20 @@ export function makeLegend(kit: ViewKit): (props: { {props.parts.map((p) => { const count = p.raw ?? p.value const on = props.hoverKey !== undefined && props.hoverKey === p.key + const sign = props.delta === true && count !== 0 ? ' lc-chip-nums-' + (count > 0 ? 'up' : 'down') : '' return ( { if (props.onHoverKey !== undefined) props.onHoverKey(p.key) }} onMouseLeave={() => { if (props.onHoverKey !== undefined) props.onHoverKey(null) }} > {catLabel(p.key)} - - {'≈' + fmt(count)} - {total > 0 ? {`${Math.round(count / total * 100)}%`} : null} + + {props.delta === true ? (count > 0 ? '+' : '') + fmt(count) : '≈' + fmt(count)} + {props.delta !== true && total > 0 ? {`${Math.round(count / total * 100)}%`} : null} ) diff --git a/src/client/styles/requestDetail.css b/src/client/styles/requestDetail.css index 6f186d9..f7b514b 100644 --- a/src/client/styles/requestDetail.css +++ b/src/client/styles/requestDetail.css @@ -13,8 +13,6 @@ /* Sign semantics mirror the harness diff blocks. */ .lc-detail-metric-up { color: var(--dsw-alias-state-success-primary); background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent); } .lc-detail-metric-down { color: var(--dsw-alias-state-error-primary); background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent); } -/* auto-fit instead of a fixed pair: a row's min-content is ~196px, below which two columns would spill. */ -.lc-detail-rows { margin-top: 10px; display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 4px 24px; } /* A fixed three-row lane (3 x the 27px row + 2 x the 2px gap): the container exists even when no row resolves, AND every row is a constant height whether or not it carries a chip — a chip is 2px taller than bare text, so without the floor the lane measured 79.5px on an input-less step and 81.5px on one with chips, moving the trend card (and @@ -34,15 +32,3 @@ button.lc-brief-row-link { cursor: pointer; } .lc-brief-chip-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .lc-brief-chip-link { cursor: pointer; } .lc-brief-more { flex: none; font-size: 11px; color: var(--dsw-alias-label-secondary); } -.lc-detail-row { display: flex; align-items: center; gap: 8px; border-radius: 4px; /* Padding + matching negative margin: the highlight paints past the text while the layout stays put. */ padding: 1px 4px; margin: -1px -4px; transition: background-color var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease-in-out); } -/* Category hover-link: the shared hover lights the matching detail row. */ -.lc-detail-row i { transition: box-shadow var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease-in-out); } -.lc-detail-row-on { background: var(--dsw-alias-interactive-bg-hover); } -.lc-detail-row-on i { box-shadow: 0 0 0 1px var(--dsw-alias-brand-primary); } -.lc-detail-row-on .lc-detail-label { font-weight: 600; } -.lc-detail-label { min-width: 70px; white-space: nowrap; color: var(--dsw-alias-label-secondary); } -/* The figures pin to the row's right edge, so they form one column across the labels' ragged widths. */ -.lc-detail-num { margin-left: auto; width: 52px; text-align: right; font-variant-numeric: tabular-nums; } -.lc-detail-num-up { color: var(--dsw-alias-state-success-primary); } -.lc-detail-num-down { color: var(--dsw-alias-state-error-primary); } -.lc-detail-pct { width: 34px; text-align: right; color: var(--dsw-alias-label-secondary); } diff --git a/src/client/styles/stackedBar.css b/src/client/styles/stackedBar.css index 6cb3a89..1e38620 100644 --- a/src/client/styles/stackedBar.css +++ b/src/client/styles/stackedBar.css @@ -38,8 +38,11 @@ .lc-chip { display: flex; align-items: center; gap: 5px; min-width: 0; color: var(--dsw-alias-label-primary); padding: 1px 6px; border-radius: 6px; cursor: pointer; transition: background-color var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease-in-out); } .lc-chip-label { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; } .lc-chip-nums { margin-left: auto; flex: none; display: inline-flex; align-items: baseline; gap: 6px; white-space: nowrap; } -.lc-chip i, .lc-detail-row i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; } +.lc-chip i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; } .lc-chip i { transition: box-shadow var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease-in-out); } .lc-chip em { font-style: normal; color: var(--dsw-alias-label-secondary); } +/* Signed figures (the trend panel's Delta mode): the sign semantics mirror the harness diff blocks. */ +.lc-chip-nums-up { color: var(--dsw-alias-state-success-primary); } +.lc-chip-nums-down { color: var(--dsw-alias-state-error-primary); } .lc-chip-on { font-weight: 600; background: var(--dsw-alias-interactive-bg-hover); } .lc-chip-on i { box-shadow: 0 0 0 1px var(--dsw-alias-brand-primary); } diff --git a/tests/client/components/contextView.interactions.spec.ts b/tests/client/components/contextView.interactions.spec.ts index 272d75f..d879fd8 100644 --- a/tests/client/components/contextView.interactions.spec.ts +++ b/tests/client/components/contextView.interactions.spec.ts @@ -142,7 +142,7 @@ describe('ContextView — interactions', () => { const detailBar = query(m.container, '.lc-detail .lc-stacked') assert.ok(detailBar.className.includes('lc-stacked-dim')) assert.ok(queryAll(detailBar, '.lc-stacked-seg-on').length === 1) - assert.ok(queryAll(m.container, '.lc-detail-row-on').length === 1, 'the matching detail row lights too') + assert.ok(queryAll(m.container, '.lc-detail .lc-chip-on').length === 1, 'the matching detail chip lights too') await unhover(toolsRow) assert.equal(query(m.container, '.lc-chart').hasAttribute('data-catdim'), false) @@ -156,6 +156,20 @@ describe('ContextView — interactions', () => { await m.unmount() }) + test('trend panel chip hover feeds the shared category state back to the composition legend', async () => { + const m = await mountRich('sv-link-chip') + // The panel's chips are hover SOURCES, exactly as the composition legend's are; "tools" is the second chip. + const chip = queryAll(m.container, '.lc-detail .lc-legend .lc-chip')[1] + await hover(chip) + assert.equal(query(m.container, '.lc-chart').getAttribute('data-catdim'), 'tools') + const comp = queryAll(m.container, '.lc-card').find(c => text(c).includes(DICT_EN['overview.title'])) + assert.ok(comp !== undefined) + assert.equal(queryAll(comp, '.lc-legend .lc-chip-on').length, 1, 'the composition legend chip lights with it') + await unhover(chip) + assert.equal(query(m.container, '.lc-chart').hasAttribute('data-catdim'), false) + await m.unmount() + }) + test('expanding a browser category focuses the trend bars on it; collapsing restores all', async () => { const m = await mountRich('sv-focus') assert.equal(text(query(m.container, '.lc-axis-top')), '420', 'unfocused: the axis tops at the largest whole-bar total') diff --git a/tests/client/components/contextView.splitDetail.spec.ts b/tests/client/components/contextView.splitDetail.spec.ts index fe38a16..2b8ed23 100644 --- a/tests/client/components/contextView.splitDetail.spec.ts +++ b/tests/client/components/contextView.splitDetail.spec.ts @@ -71,8 +71,11 @@ describe('ContextView — the split generation (slim head + detail channel)', () // Pending: the trend card stands in the same boxes the loaded panel uses, so its arrival changes no card's height. assert.equal(queryAll(m.container, '.lc-trend-lane').length, 1, 'the reserved body renders while the read is in flight') - assert.equal(queryAll(m.container, '.lc-trend-lane .lc-detail-rows .lc-detail-row').length, 7, - 'the lane reserves one row per category') + assert.equal(queryAll(m.container, '.lc-trend-lane .lc-legend .lc-chip').length, 7, + 'the lane reserves one chip per category') + const laneChip = query(m.container, '.lc-trend-lane .lc-legend .lc-chip') + assert.deepEqual(Array.from(laneChip.children).map(el => el.className.split(' ')[0]), ['', 'lc-chip-label', 'lc-chip-nums'], + 'the reserved chips wear the legend chip anatomy, so the panels measure alike') assert.equal(queryAll(m.container, '.lc-trend-lane .lc-detail-metrics').length, 0, 'no fabricated figures in the reserved panel') diff --git a/tests/client/components/requestDetail.spec.ts b/tests/client/components/requestDetail.spec.ts index c3ab918..bf97426 100644 --- a/tests/client/components/requestDetail.spec.ts +++ b/tests/client/components/requestDetail.spec.ts @@ -2,14 +2,14 @@ import { createElement as h } from 'react' import assert from 'node:assert/strict' import { describe, test } from 'vitest' import { makeRequestDetail } from '../../../src/client/components/requestDetail' -import { makeStackedBar } from '../../../src/client/components/stackedBar' +import { makeStackedBar, makeLegend } from '../../../src/client/components/stackedBar' import type { RequestRecord, ContextEventRecord, SurfaceNode } from '../../../src/shared/types' import type { ConversationNodeLike } from '../../../src/client/services' import type { StepBrief } from '../../../src/client/brief' import { makeKit, mount, click, queryAll, query, surfaceNode, text } from '../helpers/kit' const kit = makeKit() -const RequestDetail = makeRequestDetail(kit, makeStackedBar(kit)) +const RequestDetail = makeRequestDetail(kit, makeStackedBar(kit), makeLegend(kit)) function req(over: Partial): RequestRecord { return { @@ -23,12 +23,11 @@ function req(over: Partial): RequestRecord { /** Full-category fixture so every row renders a non-zero figure. */ const FULL = { system: 100, tools: 200, user: 300, inject: 50, assistant: 400, tool: 250, total: 1300 } -/** A detail row's anatomy — dot, label, figure, share — by each child's base class (a delta row's figure adds a - * sign modifier). Pinned structurally, so reintroducing a per-row mini bar fails here in either mode, instead of - * passing an absence check on a deleted class. */ -const ROW_ANATOMY = ['', 'lc-detail-label', 'lc-detail-num', 'lc-detail-pct'] +/** A category chip's anatomy — dot, label, figures — by each child's base class (a delta chip's figures add a + * sign modifier). Pinned structurally: a child added, removed or reordered fails here in either mode. */ +const CHIP_ANATOMY = ['', 'lc-chip-label', 'lc-chip-nums'] const anatomy = (container: HTMLElement): string[][] => - queryAll(container, '.lc-detail-row').map(r => Array.from(r.children).map(el => el.className.split(' ')[0])) + queryAll(container, '.lc-legend .lc-chip').map(c => Array.from(c.children).map(el => el.className.split(' ')[0])) describe('RequestDetail header', () => { test('null request renders nothing', async () => { @@ -37,7 +36,7 @@ describe('RequestDetail header', () => { await m.unmount() }) - test('step detail: title, time, usage chips, per-category rows', async () => { + test('step detail: title, time, usage chips, per-category chips', async () => { const m = await mount(h(RequestDetail, { request: req({ prompt: 1000, output: 120, cacheRead: 500 }), })) @@ -52,13 +51,14 @@ describe('RequestDetail header', () => { const ms = await mount(h(RequestDetail, { request: req({}), stepsOf: (turn) => (turn === 1 ? 7 : 1) })) assert.ok(text(query(ms.container, '.lc-detail-head')).includes('Turn 1 · Step 1 of 7')) await ms.unmount() - const rows = queryAll(m.container, '.lc-detail-row') - assert.equal(rows.length, 7) - assert.ok(text(rows[0]).includes('System Prompt')) - assert.ok(text(rows[0]).includes('≈100')) - const pcts = queryAll(m.container, '.lc-detail-pct').map(el => text(el)) + const chips = queryAll(m.container, '.lc-legend .lc-chip') + assert.equal(chips.length, 7) + assert.ok(text(chips[0]).includes('System Prompt')) + assert.ok(text(chips[0]).includes('≈100')) + const pcts = queryAll(m.container, '.lc-chip-nums em').map(el => text(el)) assert.ok(pcts.some(p => p.endsWith('%')), 'percentages rendered when total > 0') - assert.deepEqual(anatomy(m.container), Array.from({ length: 7 }, () => ROW_ANATOMY)) + assert.equal(pcts[0], '8%', 'the share rides the used total: 100 of 1300') + assert.deepEqual(anatomy(m.container), Array.from({ length: 7 }, () => CHIP_ANATOMY)) await m.unmount() }) @@ -144,7 +144,7 @@ describe('RequestDetail header', () => { }) describe('RequestDetail delta mode', () => { - test('signed deltas vs the previous record: net chip, signed rows, no usage chips', async () => { + test('signed deltas vs the previous record: net chip, signed chips, no usage chips', async () => { const request = req({ system: 150, tools: 200, user: 250, inject: 0, assistant: 300, tool: 350, total: 1250, prompt: 900 }) const prev = req({ ...FULL, inject: 0 }) const m = await mount(h(RequestDetail, { request, prev })) @@ -156,14 +156,14 @@ describe('RequestDetail delta mode', () => { assert.ok(text(metric).includes('Δ 0')) assert.ok(!metric.className.includes('lc-detail-metric-up')) assert.ok(!metric.className.includes('lc-detail-metric-down')) - const nums = queryAll(m.container, '.lc-detail-num') - assert.ok(text(nums[0]).includes('+50')) - assert.ok(nums[0].className.includes('lc-detail-num-up')) - assert.ok(text(nums[2]).includes('-50')) - assert.ok(nums[2].className.includes('lc-detail-num-down')) - assert.ok(!nums[1].className.includes('lc-detail-num-up'), 'zero delta is neutral') - assert.ok(queryAll(m.container, '.lc-detail-pct').every(el => text(el) === ''), 'no percentages in delta mode') - assert.deepEqual(anatomy(m.container), Array.from({ length: 7 }, () => ROW_ANATOMY), 'delta rows carry no diverging bar') + const figs = queryAll(m.container, '.lc-chip-nums') + assert.ok(text(figs[0]).includes('+50')) + assert.ok(figs[0].className.includes('lc-chip-nums-up')) + assert.ok(text(figs[2]).includes('-50')) + assert.ok(figs[2].className.includes('lc-chip-nums-down')) + assert.ok(!figs[1].className.includes('lc-chip-nums-up'), 'zero delta is neutral') + assert.equal(queryAll(m.container, '.lc-chip-nums em').length, 0, 'a signed change carries no share') + assert.deepEqual(anatomy(m.container), Array.from({ length: 7 }, () => CHIP_ANATOMY), 'delta chips carry no diverging bar') await m.unmount() }) @@ -172,8 +172,8 @@ describe('RequestDetail delta mode', () => { const metric = query(m.container, '.lc-detail-metric') assert.ok(metric.className.includes('lc-detail-metric-up')) assert.ok(text(metric).includes('Δ +1.3k')) - assert.equal(queryAll(m.container, '.lc-detail-num-down').length, 0) - assert.equal(queryAll(m.container, '.lc-detail-num-up').length, 6, 'every non-zero category reads up') + assert.equal(queryAll(m.container, '.lc-chip-nums-down').length, 0) + assert.equal(queryAll(m.container, '.lc-chip-nums-up').length, 6, 'every non-zero category reads up') await m.unmount() const m2 = await mount(h(RequestDetail, { @@ -183,25 +183,25 @@ describe('RequestDetail delta mode', () => { const metric2 = query(m2.container, '.lc-detail-metric') assert.ok(metric2.className.includes('lc-detail-metric-down')) assert.ok(text(metric2).includes('Δ -1.3k')) - assert.equal(queryAll(m2.container, '.lc-detail-num-up').length, 0) - assert.equal(queryAll(m2.container, '.lc-detail-num-down').length, 6) + assert.equal(queryAll(m2.container, '.lc-chip-nums-up').length, 0) + assert.equal(queryAll(m2.container, '.lc-chip-nums-down').length, 6) await m2.unmount() }) - test('cumulative rows with a zero total render zero figures and no percentages', async () => { + test('cumulative chips with a zero total render zero figures and no shares', async () => { const m = await mount(h(RequestDetail, { request: req({ system: 0, tools: 0, user: 0, inject: 0, assistant: 0, tool: 0, total: 0 }), })) - const figs = queryAll(m.container, '.lc-detail-num').map(el => text(el)) + const figs = queryAll(m.container, '.lc-chip-nums').map(el => text(el)) assert.equal(figs.length, 7) assert.ok(figs.every(f => f === '≈0')) - assert.ok(queryAll(m.container, '.lc-detail-pct').every(el => text(el) === '')) + assert.equal(queryAll(m.container, '.lc-chip-nums em').length, 0) await m.unmount() }) }) describe('RequestDetail composition bar hover-link', () => { - test('the mirrored hoverKey dims the bar, lights the matching segment and detail row; the tip stays off', async () => { + test('the mirrored hoverKey dims the bar, lights the matching segment and chip; the tip stays off', async () => { const m = await mount(h(RequestDetail, { request: req({}), hoverKey: 'tools' })) const bar = query(m.container, '.lc-stacked') assert.ok(bar.className.includes('lc-stacked-dim'), 'a mirrored hover dims the bar') @@ -209,24 +209,24 @@ describe('RequestDetail composition bar hover-link', () => { assert.equal(segs.length, 6, 'all six categories render') assert.ok(segs[1].className.includes('lc-stacked-seg-on'), 'exactly the tools segment (CATS order) lights') assert.ok(segs.filter(s => s.className.includes('lc-stacked-seg-on')).length === 1) - const rows = queryAll(m.container, '.lc-detail-row') - assert.ok(rows[1].className.includes('lc-detail-row-on'), 'the tools row lights too') - assert.ok(rows.filter(r => r.className.includes('lc-detail-row-on')).length === 1) + const chips = queryAll(m.container, '.lc-legend .lc-chip') + assert.ok(chips[1].className.includes('lc-chip-on'), 'the tools chip lights too') + assert.ok(chips.filter(c => c.className.includes('lc-chip-on')).length === 1) assert.equal(queryAll(m.container, '.lc-bar-tip').length, 0, 'a mirrored hover floats no tooltip') const m2 = await mount(h(RequestDetail, { request: req({}) })) assert.equal(query(m2.container, '.lc-stacked').className, 'lc-stacked') - assert.equal(queryAll(m2.container, '.lc-detail-row-on').length, 0) + assert.equal(queryAll(m2.container, '.lc-chip-on').length, 0) assert.equal(queryAll(m2.container, '.lc-bar-tip').length, 0) await m2.unmount() await m.unmount() }) - test('delta-mode detail rows light the matching category too', async () => { + test('delta-mode detail chips light the matching category too', async () => { const m = await mount(h(RequestDetail, { request: req({}), prev: req({ ...FULL }), hoverKey: 'user' })) - const rows = queryAll(m.container, '.lc-detail-row') - assert.ok(rows[2].className.includes('lc-detail-row-on'), 'the user row lights in delta mode') - assert.ok(rows.filter(r => r.className.includes('lc-detail-row-on')).length === 1) + const chips = queryAll(m.container, '.lc-legend .lc-chip') + assert.ok(chips[2].className.includes('lc-chip-on'), 'the user chip lights in delta mode') + assert.ok(chips.filter(c => c.className.includes('lc-chip-on')).length === 1) await m.unmount() }) })