Skip to content

Keep wheel zoom and double-click reset alive under the box-zoom tool - #344

Merged
FarhanAliRaza merged 2 commits into
mainfrom
fix/box-zoom-tool-wheel-reset
Jul 27, 2026
Merged

Keep wheel zoom and double-click reset alive under the box-zoom tool#344
FarhanAliRaza merged 2 commits into
mainfrom
fix/box-zoom-tool-wheel-reset

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Problem

After selecting the modebar Box Zoom tool (drag mode "zoom"), cursor-anchored wheel zoom and double-click view reset both stop working — the chart can only be navigated back out via the toolbar.

Cause

The toolbar restyle (#198) added if (this.dragMode !== "pan") return; to the plot-canvas wheel and dblclick handlers in js/src/53_interaction.ts. The intent was the deliberate pan ↔ none modebar escape hatch (embedded charts release page scroll), but the guard also blocked the zoom and select tools — contradicting the pan/zoom design spec (§5.5: "zoom means box zoom for drag only. It does not control wheel zoom…") and the axis-band wheel handler, which has no drag-tool guard.

Fix

  • Guard both handlers on the actual exception, dragMode === "none", instead of !== "pan". Wheel zoom now works under the pan, box-zoom, and select tools; double-click reset works under pan and box-zoom. "none" stays fully inert so the modebar pan toggle still hands wheel scroll back to the page.
  • Record the contract in spec/api/interaction.md (flag table + gesture map) and spec/design/pan-and-zoom-configuration.md §5.5, including fixing the stale default_drag_action="none", wheel_zoom=True example that had promised behavior ENG-10514: Update toolbar style #198 removed.
  • New regression test tests/test_boxzoom_tool_keeps_wheel_and_reset.py: a headless-Chromium probe that activates the box-zoom tool, completes a box zoom, then asserts wheel still zooms (and preventDefaults) and double-click still resets — plus the "none" carve-out in both directions. Verified it fails against the old guard.

Testing

  • node js/build.mjs (typecheck + bundle) clean
  • New probe passes; fails when the old guard is reinstated
  • All 64 tests touching wheel/dblclick behavior pass (test_wheel_gesture_end, test_modebar_select_drill, test_view_state_client, …)
  • pre-commit run --all-files, ruff check ., ruff format --check . all pass

Summary by CodeRabbit

  • New Features

    • Wheel zooming and double-click view reset now remain available while using box zoom and other drag tools.
    • Added support for creating inert plots that allow page scrolling while retaining toolbar navigation.
  • Bug Fixes

    • Corrected interaction behavior when drag mode is set to none.
  • Documentation

    • Updated interaction and configuration guidance to clarify zoom, reset, and scrolling behavior.
  • Performance

    • Reduced the native release library size by approximately 15% without measured runtime impact.

Whole-program LTO plus strip cuts the cdylib ~15% (1.51 -> 1.29 MB); the
native scatter bench shows no runtime change beyond +/-15% run-to-run
noise. .dynsym survives stripping, so ctypes binding is unaffected
(abi_smoke passes 140/140 against the stripped build). panic stays
unwinding because lib.rs catches panics at the C ABI; the wasm target
keeps its explicit -C panic=abort in release.yml. Profile rationale and
the not-adopted PGO lever are recorded in spec/design/rust-engine.md.
The dragMode guard added with the toolbar restyle (#198) blocked the plot
wheel and dblclick handlers for every tool except pan, so activating the
modebar Box Zoom tool killed cursor-anchored wheel zoom and double-click
reset. Per the pan/zoom design spec (§5.5), the drag tool selects the drag
gesture only; guard on the deliberate exception instead — 'none', the
modebar escape hatch that releases page scroll for embedded charts.

Records the contract in spec/api/interaction.md and
spec/design/pan-and-zoom-configuration.md (whose 'none' + wheel_zoom
example had drifted since #198), and adds a headless-Chromium regression
probe covering both the box-zoom tool and the 'none' carve-out.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR preserves wheel zoom and double-click reset across active drag tools except none, adds browser regression coverage and updated interaction specifications, and changes the Rust release profile to use fat LTO with symbol stripping.

Changes

Interaction behavior

Layer / File(s) Summary
Drag-mode interaction gating
js/src/53_interaction.ts
Wheel zoom and double-click reset now operate in active drag modes, while none continues to disable them or pass wheel input to the page.
Interaction contracts and regression validation
spec/api/interaction.md, spec/design/pan-and-zoom-configuration.md, tests/test_boxzoom_tool_keeps_wheel_and_reset.py
Specifications and a Chromium regression test cover box zoom, wheel zoom, double-click reset, and the none escape behavior.

Rust release profile

Layer / File(s) Summary
Rust release configuration and documentation
Cargo.toml, spec/design/rust-engine.md, CHANGELOG.md
Release builds now use fat LTO and symbol stripping, with documentation covering retained dynamic symbols, panic handling, PGO status, and artifact-size measurements.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • reflex-dev/xy#343: Updates the Rust release profile with fat LTO, symbol stripping, and matching design and changelog documentation.

Suggested reviewers: alek99

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: preserving wheel zoom and double-click reset while the box-zoom tool is active.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/box-zoom-tool-wheel-reset

Comment @coderabbitai help to get the list of available commands.

@codspeed-hq

codspeed-hq Bot commented Jul 27, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 103 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing fix/box-zoom-tool-wheel-reset (e1c2aae) with main (4c26d97)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
js/src/53_interaction.ts (1)

371-385: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Wheel can now fire mid box-zoom/select drag.

Broadening the guard to dragMode === "none" means wheel zoom now runs while band is active (box-zoom or select-* drag in progress) — previously blocked since band only exists when dragMode isn't "pan". band.d0 is captured under the pre-wheel view; if the wheel changes the view mid-drag, the release-time d1 is computed under a different mapping while the on-screen rubber-band overlay (tracked in raw screen coordinates) visually desyncs from the repainted chart, producing a box-zoom target that doesn't match what was visually dragged.

🐛 Proposed guard
     this._listen(c, "wheel", (e) => {
       // The drag tool never disables the wheel (box-zoom/select drags are
       // drag-only tools) — except `none`, the modebar's escape hatch that
       // releases page scroll for embedded charts.
       if (this.dragMode === "none") return;
+      // Don't let wheel zoom mutate the view out from under an
+      // in-progress box-zoom/select rectangle; band.d0 was captured
+      // under the pre-zoom view.
+      if (band) return;
       if (!this._interactionFlag("navigation", true)) return;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/src/53_interaction.ts` around lines 371 - 385, Prevent the wheel handler
from processing events while a box-zoom or select drag is active by adding the
active band state to the early-return guard alongside dragMode "none". Keep
wheel zoom available when no drag band is active, and preserve the existing
navigation, zoom, and wheel_zoom checks and _queueWheelZoom flow.
🧹 Nitpick comments (1)
Cargo.toml (1)

20-21: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Pin release panic to unwind. Cargo.toml leaves the strategy implicit; add panic = "unwind" in [profile.release] so the C-ABI panic-catching path stays explicit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Cargo.toml` around lines 20 - 21, Add panic = "unwind" to the
[profile.release] section in Cargo.toml, keeping the release profile’s panic
strategy explicit and preserving lib.rs C-ABI panic catching behavior.

Source: MCP tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@js/src/53_interaction.ts`:
- Around line 371-385: Prevent the wheel handler from processing events while a
box-zoom or select drag is active by adding the active band state to the
early-return guard alongside dragMode "none". Keep wheel zoom available when no
drag band is active, and preserve the existing navigation, zoom, and wheel_zoom
checks and _queueWheelZoom flow.

---

Nitpick comments:
In `@Cargo.toml`:
- Around line 20-21: Add panic = "unwind" to the [profile.release] section in
Cargo.toml, keeping the release profile’s panic strategy explicit and preserving
lib.rs C-ABI panic catching behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 75bdda14-6578-46b5-bf08-531b5cc5913a

📥 Commits

Reviewing files that changed from the base of the PR and between 4c26d97 and e1c2aae.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • Cargo.toml
  • js/src/53_interaction.ts
  • spec/api/interaction.md
  • spec/design/pan-and-zoom-configuration.md
  • spec/design/rust-engine.md
  • tests/test_boxzoom_tool_keeps_wheel_and_reset.py

@FarhanAliRaza
FarhanAliRaza merged commit 9fdc236 into main Jul 27, 2026
28 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