From 3095628479757740f15b6fe0537dbb2f88cd2d7a Mon Sep 17 00:00:00 2001 From: Experiments DB Dev Date: Mon, 1 Jun 2026 08:45:46 -0400 Subject: [PATCH] docs: design for cross-subject plot interactions Co-Authored-By: Claude Opus 4.8 --- ...-cross-subject-plot-interactions-design.md | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-01-cross-subject-plot-interactions-design.md diff --git a/docs/superpowers/specs/2026-06-01-cross-subject-plot-interactions-design.md b/docs/superpowers/specs/2026-06-01-cross-subject-plot-interactions-design.md new file mode 100644 index 0000000..e51e837 --- /dev/null +++ b/docs/superpowers/specs/2026-06-01-cross-subject-plot-interactions-design.md @@ -0,0 +1,113 @@ +# Cross-subject metrics plot — interaction enhancements + +**Date:** 2026-06-01 +**Component:** `frontend/src/components/AnalysisCharts.jsx` → `ExperimentAnalysisCharts` +**Rendered on:** `frontend/src/pages/ExperimentDetail.jsx` + +## Summary + +Add four client-side interaction features to the "Cross-subject metrics" plot (two +Recharts `LineChart`s — count-per-session and success-rate — where each line is one +subject, colored by group): + +1. A toggleable sidebar listing subjects grouped by their group, with per-subject and + per-group checkboxes to show/hide each subject's line. +2. Clicking a subject's legend entry toggles that subject's line visibility. +3. The hover tooltip orders its rows to match the vertical stacking of the points + (highest value at top). +4. Hovering a subject's legend entry (or sidebar row) highlights that subject's line. + +## Constraints + +- **Frontend only.** No backend, API, or database changes. All state is client-side + React state, not persisted. +- **No regressions.** The existing global controls (X axis, Color by, tick step), the + per-chart customize bars (size, Y range, X zoom, per-chart X-field override), the + Y-metric selector, and the sibling `SubjectAnalysisCharts` component are untouched. +- Scope is limited to `ExperimentAnalysisCharts`. + +## Visibility model (decided) + +Visibility is **global per-subject**: toggling a subject anywhere — sidebar checkbox, +either chart's legend — hides or shows it in **both** charts simultaneously. "Per plot" +in the original request resolves to per-subject, not per-chart. + +## Shared state (lifted into `ExperimentAnalysisCharts`) + +- `hiddenSubjects: Set` — empty = all visible. Global across both charts. + Stale ids (subject removed) are harmless: they match no line. +- `highlightedSubject: animalId | null` — transient hover state, shared by both charts + and the sidebar. +- `sidebarOpen: boolean` — default `false`. + +Helpers: `toggleHidden(id)`, `setGroupHidden(groupSubjectIds, hidden)`, `showAll()`, +`setHighlight(id | null)`. + +## Layout + +A toggle button (`▸ Subjects`) is added to the existing global-controls row. When open, +the card body becomes a flex row: + +- Charts area: `flex-1`; the existing `ResponsiveContainer width="100%"` re-flows the + charts narrower automatically. +- Sidebar panel: ~210px fixed width, `border-l`, top-aligned with the card. + +``` +┌─ Cross-subject metrics ───────── X axis▾ Color by▾ Tick [▸ Subjects] ─┐ +│ │ SUBJECTS Show all │ +│ ┌──────────────────────────────┐ │ ┌───────────────────────┐ │ +│ │ Count chart (re-flows) │ │ │ ▣ Group A (3)│ │ +│ └──────────────────────────────┘ │ │ ☑ ● Mouse-A1 │ │ +│ ┌──────────────────────────────┐ │ │ ☑ ● Mouse-A2 │ │ +│ │ Success-rate chart │ │ │ ☐ ● Mouse-A3 │ │ +│ └──────────────────────────────┘ │ │ ▢ Group B (2)│ │ +│ │ │ ☐ ● Mouse-B1 │ │ +│ │ └───────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### Sidebar contents + +- **Grouping** reuses the current `Color by` field via the existing `lines[].group`. + With `Color by = none`, all subjects fall under a single "—" group. +- **Subject row**: checkbox (checked = visible) + color swatch (`lines[].color`) + name. + Hovering the row sets `highlightedSubject`. +- **Group row**: tri-state checkbox — checked if all in group visible, empty if all + hidden, indeterminate if mixed. Clicking toggles the whole group. Shows group name + + count. +- **"Show all"** header link — clears `hiddenSubjects`; shown only when something is + hidden. + +## Behavior details + +1. **Visibility** — every subject `` is rendered always (in both charts) with + `hide={hiddenSubjects.has(l.id)}` instead of filtering the `lines` array. Recharts + keeps a greyed, still-clickable legend entry for hidden lines and excludes them from + the tooltip payload automatically. + +2. **Legend click** — ` toggleHidden(o.dataKey)} />` on both + charts. `dataKey` is `l.id` (lines use `dataKey={l.id} name={l.id}`). + +3. **Tooltip ordering** — the existing custom `ExpTooltip` sorts `payload` by `value` + descending before rendering its rows, so the topmost line is first. Null/hidden + subjects are already excluded. Applies to both the count and rate tooltips. + +4. **Legend hover highlight** — ` setHighlight(o.dataKey)} + onMouseLeave={() => setHighlight(null)} />`. Each line's `strokeWidth` is `3.5` when + `highlightedSubject === l.id`, else `2` (other lines unchanged — "thicken hovered + only"). Highlight applies in **both** charts and is also driven by sidebar-row hover. + +## Testing + +- Manual verification in the running app (`ExperimentDetail` page with an experiment + that has animals and saved analysis metrics): + - Open/close sidebar; charts re-flow. + - Toggle a subject checkbox → line disappears in both charts; legend entry greys out. + - Group checkbox toggles all members; shows indeterminate when mixed. + - "Show all" clears hidden state. + - Click a legend entry → toggles in both charts. + - Hover tooltip rows are ordered highest-value-first. + - Hover a legend entry / sidebar row → that line thickens in both charts. +- Confirm existing controls (X axis, Color by, tick step, per-chart customize, Y-metric) + still work. +```