From 84fe1d703849cf952069bc232a8dc782848a1b5e Mon Sep 17 00:00:00 2001 From: bowenliang123 Date: Mon, 5 Oct 2026 07:14:16 +0800 Subject: [PATCH] fix(trend): merge the title's three modifier toggles into one segmented group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Duration toggle (added in 01b1532) never joined the title-adjacent zero-margin rule, so its bare .lc-gran kept margin-left: auto and split the free space with the control cluster — on wide cards it drifted to the middle of the title row (242px each side at ~930px), and on wrapping widths it landed wherever the auto margin happened to put it (line 1's right end in English, line 2's lead at the narrowest). DNA / Adaptive / Duration now ride the title text's right as ONE multi-toggle segmented group (.lc-trend-mods), the tips moving from the group wrappers onto the buttons; the row keeps a single auto-margin push (the control cluster), so wrapping drops the two semantic groups whole instead of scattering the modifiers. Also drop the stale "Total/Delta disables under DNA" note on the disabled-chip rule — no gran button has been disabled since DNA+Delta shipped. --- src/client/components/contextView.tsx | 21 +++++++-------- src/client/styles/base.css | 10 +++---- tests/client/components/contextView.spec.ts | 30 ++++++++++----------- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/client/components/contextView.tsx b/src/client/components/contextView.tsx index 91990cb..bc6118b 100644 --- a/src/client/components/contextView.tsx +++ b/src/client/components/contextView.tsx @@ -458,31 +458,28 @@ export function makeContextView(
{t('trend.title')} - {/* The DNA toggle rides the title text's right, left of the adaptive switch (the browser - card's DNA toggle idiom): each bar becomes that request's per-item context fingerprint; - combined with Delta, each band shows that item's change against the previous bar. */} - + {/* The three display modifiers ride the title text's right as ONE multi-toggle segmented group + (the browser card's DNA toggle idiom), each segment lighting independently: DNA draws every + bar as that request's per-item context fingerprint (with Delta, per-item changes against the + previous bar); Adaptive rescales the axis to the visible bars, following the scroll; Duration + overlays each bar's step active time as a curve read off the right-hand quartile axis. */} + - - {/* The adaptive switch rides the title's right (the browser card's DNA toggle idiom): bars rescale - to the bars currently on screen instead of the whole retained log. */} - - - {/* The duration overlay switch rides with the other title-adjacent toggles: each bar's step - active time as a curve over the bars, read off the right-hand quartile axis. */} - diff --git a/src/client/styles/base.css b/src/client/styles/base.css index 0fd08b6..96e73a8 100644 --- a/src/client/styles/base.css +++ b/src/client/styles/base.css @@ -74,14 +74,14 @@ .lc-trend-ctl .lc-gran, .lc-heat-ctl .lc-gran { margin-left: 0; } /* The heat title's hint text loses its own auto margin inside the wrapper. */ .lc-heat-ctl .lc-card-sub { margin-left: 0; } -/* The trend title's DNA toggle and adaptive switch ride the title text's right as an adjacent pair — - each single-button group's auto margin is neutralized so they stay together (the browser card's DNA +/* The trend title's modifier group (DNA / adaptive / duration as one multi-toggle segmented group) + rides the title text's right — its auto margin is neutralized so the group hugs the title and the + control cluster's own auto margin remains the row's single right push (the browser card's DNA toggle idiom). */ -.lc-trend-dna, .lc-trend-adaptive { margin-left: 0; flex: none; } +.lc-trend-mods { margin-left: 0; flex: none; } .lc-gran-btn { border: 0; background: transparent; color: var(--dsw-alias-label-secondary); font-size: 11px; line-height: 1; padding: 3px 8px; border-radius: 5px; cursor: pointer; font-family: inherit; transition: color var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease-in-out); } .lc-gran-btn:hover { color: var(--dsw-alias-label-primary); } -/* Disabled chips (the Total/Delta pair while the trend card's DNA mode is on): inert and visually - receded, keeping the group's layout so the row does not reflow when DNA toggles. */ +/* Disabled chips: inert and visually receded, keeping the group's layout so the row does not reflow. */ .lc-gran-btn:disabled { opacity: 0.4; cursor: not-allowed; } .lc-gran-btn:disabled:hover { color: var(--dsw-alias-label-secondary); } .lc-gran-on, .lc-gran-on:hover { background: var(--dsw-alias-button-primary-fill); color: var(--dsw-alias-label-primary-foreground); } diff --git a/tests/client/components/contextView.spec.ts b/tests/client/components/contextView.spec.ts index 82ac3fb..9715d84 100644 --- a/tests/client/components/contextView.spec.ts +++ b/tests/client/components/contextView.spec.ts @@ -470,14 +470,15 @@ describe('ContextView — interactions', () => { const titleText = query(card, '.lc-card-title-text') assert.equal(text(titleText), DICT_EN['trend.title']) - // The switch is the DNA toggle's NEXT sibling — the pair rides the title text's right, left of the + // The three display modifiers ride the title text's right as ONE segmented group, left of the // card's right-hand control cluster. const toggleOf = () => buttonByText(m.container, DICT_EN['trend.adaptive']) - const dnaGroupOf = () => buttonByText(m.container, DICT_EN['trend.dna']).parentElement as HTMLElement - assert.ok(dnaGroupOf().className.includes('lc-trend-dna')) - assert.equal(dnaGroupOf().previousElementSibling, titleText) - assert.equal(toggleOf().parentElement?.previousElementSibling, dnaGroupOf()) - assert.equal(toggleOf().parentElement?.getAttribute('title'), DICT_EN['trend.adaptiveHint']) + const modsGroupOf = () => buttonByText(m.container, DICT_EN['trend.dna']).parentElement as HTMLElement + assert.ok(modsGroupOf().className.includes('lc-trend-mods')) + assert.equal(modsGroupOf().previousElementSibling, titleText) + assert.equal(toggleOf().parentElement, modsGroupOf()) + assert.equal(toggleOf().previousElementSibling, buttonByText(m.container, DICT_EN['trend.dna']), 'Adaptive rides right of DNA inside the group') + assert.equal(toggleOf().getAttribute('title'), DICT_EN['trend.adaptiveHint']) assert.ok(!toggleOf().className.includes('lc-gran-on'), 'off at mount') // Toggling is mount-local: the chart keeps rendering (jsdom has no layout, so the zero-width viewport falls @@ -503,13 +504,12 @@ describe('ContextView — interactions', () => { })), })) - // The switch rides the title's right, next after the adaptive one. + // The switch rides inside the title-adjacent modifier group, after the adaptive one. const msBtn = () => buttonByText(m.container, DICT_EN['trend.duration']) const group = msBtn().parentElement as HTMLElement - assert.ok(group.className.includes('lc-trend-duration')) - assert.equal(group.getAttribute('title'), DICT_EN['trend.durationTip']) - const adaptiveGroup = buttonByText(m.container, DICT_EN['trend.adaptive']).parentElement as HTMLElement - assert.equal(group.previousElementSibling, adaptiveGroup, 'Duration rides right of the adaptive switch') + assert.ok(group.className.includes('lc-trend-mods')) + assert.equal(msBtn().getAttribute('title'), DICT_EN['trend.durationTip']) + assert.equal(msBtn().previousElementSibling, buttonByText(m.container, DICT_EN['trend.adaptive']), 'Duration rides right of the adaptive switch') // The settings default (show) mounts the overlay ON: the curve spans the two stamped bars and the // right axis reads the 2s peak (jsdom's zero-width viewport keeps the whole-log scale, adaptive off anyway). assert.ok(msBtn().className.includes('lc-gran-on'), 'on at mount (the settings default)') @@ -534,9 +534,9 @@ describe('ContextView — interactions', () => { const dnaBtn = () => buttonByText(m.container, DICT_EN['trend.dna']) assert.ok(!dnaBtn().className.includes('lc-gran-on'), 'off at mount') const dnaGroup = dnaBtn().parentElement as HTMLElement - assert.ok(dnaGroup.className.includes('lc-trend-dna')) - assert.equal(dnaGroup.getAttribute('title'), DICT_EN['trend.dnaTip']) - assert.ok((dnaGroup.nextElementSibling as HTMLElement | null)?.className.includes('lc-trend-adaptive'), 'DNA rides left of the adaptive switch') + assert.ok(dnaGroup.className.includes('lc-trend-mods')) + assert.equal(dnaBtn().getAttribute('title'), DICT_EN['trend.dnaTip']) + assert.equal(dnaBtn().nextElementSibling, buttonByText(m.container, DICT_EN['trend.adaptive']), 'DNA rides left of the adaptive switch') // DNA on: the bars become per-item fingerprints and the axis reads totals. await click(dnaBtn()) @@ -568,7 +568,7 @@ describe('ContextView — interactions', () => { test('the trend and browser DNA toggles move as one', async () => { const m = await mountRich('sv-dna-link') - const trendDna = () => query(m.container, '.lc-trend-dna .lc-gran-btn') + const trendDna = () => buttonByText(m.container, DICT_EN['trend.dna']) const browserDna = () => query(m.container, '.lc-br-dna-ctl .lc-gran-btn') assert.ok(!trendDna().className.includes('lc-gran-on') && !browserDna().className.includes('lc-gran-on'), 'both off at mount')