docs: design for cross-subject plot interactions
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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<animalId>` — 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 `<Line>` 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** — `<Legend onClick={(o) => 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** — `<Legend onMouseEnter={(o) => 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.
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user