Skip to content

feat(dashboard): add InfoTip so numbers can explain themselves (slice 67C) - #268

Merged
neomatrix369 merged 9 commits into
mainfrom
slice/67c-infotip
Nov 5, 2026
Merged

neomatrix369 merged 9 commits into
mainfrom
slice/67c-infotip

Conversation

@neomatrix369

@neomatrix369 neomatrix369 commented Nov 5, 2026 •

Copy link
Copy Markdown
Owner

What and why

Slice 67C (third of four in SLICE-67). Numbers on the dashboard need a consistent way to explain themselves. This adds one dependency-free tooltip primitive, InfoTip, and an explain prop on the three card components. Nothing renders explain yet; the screens adopt it in 67D.

  • InfoTip: a native <button type="button"> with aria-describedby pointing at a role="tooltip" span kept in the DOM and toggled with hidden (announced once, no aria-live, no aria-expanded). Tap toggles; Escape closes and keeps focus on the trigger; an outside press, a resize or an orientation change also closes it. 44 px target. The tip stays 8 px inside the viewport.
  • infoTipUtils.ts (pure, in the determinism guard's module list): clampShift(rect, viewportWidth) plus the aboutLabel / explainLabel sentence templates used by the consumers and their tests.
  • StatTile.hint renders through InfoTip; StatTile, StatCard and CollapsibleCard gained an explain prop. On CollapsibleCard the tip is a sibling of the toggle (inside headerExtra it would nest buttons).
  • Determinism (owner rule): same props give the same markup (tests normalise useId); the clamp is a pure function with literal-number tests.

Decision for the owner

StatTile.hint used to be always-visible small text; it is now behind the info button (tap to reveal). That is what the slice plan asks for ("behind an (i) icon") and it keeps compact cards less crowded at 390 px, but the captions on the database stats panel (data / index sizes, cluster quota) are no longer visible at a glance and find-in-page will not see them. Please confirm this before merging; the alternative is to keep the hint visible and use explain only for new explanations.

Verification

  • ./scripts/ci/quality-gates.sh: all gates passed (500 frontend tests, coverage 97.4 / 93.01 / 98.44 / 98.59 against the 95/90/95/95 floors; ruff, mypy, bandit, xenon, backend 986 tests, audits).
  • Real browser (own dev server against the running stack): 390 px, with the tip forced against the left and right screen edges it stays inside the screen (8..264 and 126..382) and the page does not scroll sideways; 1280 px, no overflow, the tip adds no card height, CollapsibleCard header toggle 882 x 44 beside a 44 x 44 button. Keyboard-only pass: Tab to the trigger, Enter opens, Space toggles, Escape closes, focus stays on the trigger. The browser pass used temporary explain props that were reverted, since no screen uses them yet.
  • Two review rounds (code quality and acceptance coverage); every requested change was made. The acceptance reviewer asked for the 1280 px keyboard pass and a stronger measurement test, both done.
  • Mutation checks: tabIndex={-1} on the trigger fails the keyboard test; not resetting the shift on close fails the measurement test.

Notes for the reviewer

  • Spec amended: the stub promised executable Tab and Enter tests, which jsdom cannot give honestly (user-event is not a dependency and jsdom does not turn Enter or Space into a click). The clause now says the jsdom tests own focusability, toggle and Escape, and Enter/Space is observed once in a real browser. Decision #311.
  • The compact StatCard label stays a direct text child so the merged 67A test that finds the Total value through the label keeps working.
  • Deferred: when both hint and explain are set a tile shows two 44 px buttons (owner decision in 67D); a duplicate-id guard for caller-supplied ids.
  • This PR and feat(api): add pure sweep_plan as the single source of sweep counts (slice 67B) #267 (67B) both add lines to CHANGELOG, GROWTH, DECISIONS and PROGRESS at the same spots; whichever merges second will need a trivial rebase.

Complexity

Automatically refreshed by CI for product-code changes.

Scope Tool Result
Python Radon highest CC 40 (rank E), 897 blocks
JS/TS ESLint 18 function(s) above CC 10
JS/TS ESLint sonarjs 7 function(s) above cognitive 15
Functions above the JavaScript threshold
  • ExperimentDetailScreen.tsx:50 — CC 118
  • SearchExplorerScreen.tsx:35 — CC 38
  • labels.ts:35 — CC 29
  • ExplorePanels.tsx:414 — CC 29
  • LoadingFeedbackPanel.tsx:41 — CC 28
  • ExperimentsScreen.tsx:64 — CC 21
  • VectorDbStatsPanel.tsx:83 — CC 18
  • ExperimentControlButtons.tsx:54 — CC 14
  • loadWalkthruResults.ts:37 — CC 14
  • ExperimentsScreen.tsx:467 — CC 13
Functions above the cognitive-complexity threshold
  • ExperimentDetailScreen.tsx:50 — cognitive 63
  • LoadingFeedbackPanel.tsx:41 — cognitive 27
  • labels.ts:35 — cognitive 25
  • ExplorePanels.tsx:414 — cognitive 23
  • markdown.ts:59 — cognitive 20
  • SearchExplorerScreen.tsx:35 — cognitive 17
  • steps.ts:63 — cognitive 16

…rs, make measuring idempotent, close on resize, test keyboard reachability
…o the Detail screen total lookup still works
…ta, decision 311

The stub promised a Tab and Enter test that jsdom cannot honestly give and
named CollapsibleCard.headerExtra, which would nest buttons; both are
corrected. Records the tap-to-reveal hint change for the owner to confirm.
Resolves the four documentation conflicts (CHANGELOG, DECISIONS, GROWTH,
PROGRESS) by keeping both slices' entries; 67B first, then 67C.
@neomatrix369
neomatrix369 merged commit 7f95143 into main Nov 5, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant