CODAP-1469: make residual-plot points marquee-selectable - #2677
Conversation
Move the delta hit-test helper (and its RTree / CaseObject types) out of background.tsx into data-display-utils.ts, next to the rectangleSubtract it calls, so the residual-plot marquee can reuse it. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the Residual Plot's SVG rendering, split-layout sync autorun, and selection-halo restyle out of scatter-plot.tsx into a useResidualPlot hook, slimming the core component. Behavior-preserving; existing residual tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
buildResidualPositions maps residual points to screen coords (matching the drawn circles); clampRectToLowerRegion confines a marquee rect to the residual strip. Both pure and unit-tested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add useResidualMarquee: a drag-rectangle selection confined to the residual strip, independent of the upper-plot marquee. Builds its own R-tree from the residual positions on pointerdown and selects via the shared delta hit-testing; dynamic while dragging, Shift adds. A no-drag click still deselects. Shares the display's MarqueeState with the plot via a new MarqueeStateContext. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drag a marquee across the residual strip and assert residual points become selected, alongside the existing click-select/deselect coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pointer-to-frame conversion used ownerSVGElement (origin outside the axes), offsetting the marquee — and its hit-testing — right by the left-axis margin. Anchor to the residual hit rect's own box instead: it is drawn at frame (0, plotHeight) in the same group as the residual points, so its screen top-left maps to that frame regardless of margins. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reindent the graph's returned JSX now that it is nested inside the MarqueeStateContext provider added for the residual marquee. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2677 +/- ##
===========================================
+ Coverage 72.82% 87.79% +14.96%
===========================================
Files 810 814 +4
Lines 46933 47045 +112
Branches 11999 12012 +13
===========================================
+ Hits 34179 41303 +7124
+ Misses 12721 5726 -6995
+ Partials 33 16 -17
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
codap-v3
|
||||||||||||||||||||||||||||
| Project |
codap-v3
|
| Branch Review |
CODAP-1469-residual-plot-marquee-selection
|
| Run status |
|
| Run duration | 08m 37s |
| Commit |
|
| Committer | null |
| View all properties for this run ↗︎ | |
| Test results | |
|---|---|
|
|
0
|
|
|
1
|
|
|
82
|
|
|
0
|
|
|
383
|
| View all changes introduced in this branch ↗︎ | |
There was a problem hiding this comment.
Pull request overview
Adds residual-strip marquee (drag-rectangle) selection to the scatter plot’s residual plot, sharing marquee state/logic with existing background selection and preserving click-to-deselect behavior (CODAP-1469).
Changes:
- Introduces a residual-specific marquee implementation (
useResidualMarquee) that builds an R-tree from residual point positions on pointerdown and updates selection dynamically while dragging. - Refactors residual plot rendering/sync/selection styling into
useResidualPlot, and shares delta hit-testing viagetCasesForDeltamoved intodata-display-utils.ts. - Adds MarqueeState context plumbing and new unit/Cypress coverage for residual marquee behavior and helper utilities.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| v3/src/components/graph/plots/scatter-plot/use-residual-plot.ts | New hook encapsulating residual plot rendering, axis/split-layout sync, and selection restyling. |
| v3/src/components/graph/plots/scatter-plot/use-residual-marquee.ts | New residual-strip-only marquee selection logic (R-tree build + delta hit-testing). |
| v3/src/components/graph/plots/scatter-plot/scatter-plot.tsx | Wires in useResidualPlot + useResidualMarquee and updates residual background hit-rect handlers. |
| v3/src/components/graph/plots/scatter-plot/residual-marquee-utils.ts | Pure helpers for residual point screen positions and lower-region rect clamping. |
| v3/src/components/graph/plots/scatter-plot/residual-marquee-utils.test.ts | Unit tests for residual marquee helper utilities. |
| v3/src/components/graph/components/graph.tsx | Provides MarqueeState via context in addition to existing prop threading. |
| v3/src/components/data-display/hooks/use-marquee-state.ts | Adds MarqueeStateContext + hook for descendant access. |
| v3/src/components/data-display/data-display-utils.ts | Moves/exports getCasesForDelta and related types for shared marquee delta selection. |
| v3/src/components/data-display/data-display-utils.test.ts | Adds unit tests for getCasesForDelta. |
| v3/src/components/data-display/components/background.tsx | Uses shared getCasesForDelta and shared CaseObject/RTree types. |
| v3/cypress/e2e/bivariate-adornments.spec.ts | Adds e2e coverage for residual-strip marquee selection. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- End the residual marquee drag on pointercancel too, so touch/gesture cancellation doesn't leave window listeners attached or the marquee stuck. - Drop the runtime `rtree` import from the widely-imported data-display-utils: getCasesForDelta only needs the tree's type, so declare a minimal RTree interface instead of ReturnType<typeof RTreeLib>. Tree construction stays in the callers that already depend on rtree. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
If a drag ended without a trailing click on the hit rect (e.g. pointerup off the rect), suppressClickRef stayed true and swallowed the next genuine click. Reset it at the start of each pointer interaction so suppression only applies to the click immediately following a drag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Adds marquee (drag-rectangle) selection to the residual plot, alongside the
existing click selection. Fixes CODAP-1469.
selection updates dynamically while dragging, and Shift adds to the current
selection.
marquee and the map marquee are unchanged. A rectangle spanning both the main
Y-axis space and the residual space has no coherent meaning, so the two zones
stay independent.
Implementation notes
track, so the shared
Backgroundmarquee (which hit-tests the renderer'sR-tree) can't see them. The new
useResidualMarqueehook builds its ownR-tree from the residual positions on pointerdown and selects via the same
delta hit-testing — the R-tree is built up front to stay efficient with large
datasets (one residual point per case).
scatter-plot.tsxinto auseResidualPlothook, andBackground'sgetCasesForDeltawas moved intodata-display-utils.tsso both marqueesshare it.
Reviewer notes
This PR is a mix of new logic and mechanical moves — the commit history is
structured so each is reviewable on its own.
New code (warrants closer review):
use-residual-marquee.ts— the new drag-select behavior: R-tree build,delta hit-testing, dynamic/Shift selection, the pointer→plot-area coordinate
conversion, and the click-vs-drag threshold that preserves click-to-deselect.
residual-marquee-utils.ts— new pure helpers (buildResidualPositions,clampRectToLowerRegion) + their unit tests.scatter-plot.tsx— the new bits only: theuseResidualMarqueecall andthe residual hit-rect's
onPointerDown/onClickwiring (the rest of the diffin this file is the extraction below).
use-marquee-state.ts+ theMarqueeStateContextprovider ingraph.tsx—new, but trivial context plumbing.
bivariate-adornments.spec.ts— new Cypress test.getCasesForDeltaunit test indata-display-utils.test.ts.Moved / mechanical (lighter review):
use-residual-plot.ts— a behavior-preserving extraction of the residualrendering/sync code out of
scatter-plot.tsx(no logic changes; the guardsand
untrackedwrapping were moved verbatim). Most of thescatter-plot.tsxdeletions are the other side of this move.
getCasesForDeltamoved frombackground.tsxintodata-display-utils.ts;background.tsxjust imports it now.graph.tsxreindent commit is whitespace only (JSX nested under the newprovider).
Testing
getCasesForDeltaand the pure residual-marqueegeometry helpers.
click select/deselect coverage).