Skip to content

perf(charts): Speed up Heat Map drag-to-zoom#119346

Draft
gggritso wants to merge 4 commits into
masterfrom
georgegritsouk/dain-1772-heat-map-drag-to-select-box-is-janky
Draft

perf(charts): Speed up Heat Map drag-to-zoom#119346
gggritso wants to merge 4 commits into
masterfrom
georgegritsouk/dain-1772-heat-map-drag-to-select-box-is-janky

Conversation

@gggritso

@gggritso gggritso commented Jul 9, 2026

Copy link
Copy Markdown
Member

Replaces the metrics Heat Map's drag-to-zoom implementation. The selection box is now drawn as a lightweight DOM overlay instead of ECharts' brush component.

  • Brushing doesn't work because it's too slow. Every brush event (i.e., every mouse move) re-renders the entire heat map because it's trying to "brush" which ones are outside and inside the rectangle. There is not way to avoid this, and it causes bad jank
  • Data Zoom doesn't work because it cannot correctly resolve the double-axes of a Heat Map (we have both category and value axes).
  • Painting a polygon on the ECharts canvas doesn't work because it also re-renders the series, and that's too slow

The diff is useless here, it's basically a whole new file! The new implementation draws an HTML div over the chart as the drag, which is much faster.

If you're curious, here's a profile of a mouse move before:

Screenshot 2026-07-09 at 11 11 40 AM

You can clearly see a needless ECharts render, a re-layout, etc. It's like 75ms for all that. The after is trivially fast:

Screenshot 2026-07-09 at 4 14 21 PM

Closes DAIN-1772, DAIN-1776

The Heat Map's drag-to-select box was janky and got worse as the heat map
gained cells. Root cause: it used ECharts' `brush`, whose per-move `brush`
action forces a full `updateVisual` re-render of every series (an O(cells)
rebuild) on every mouse-move.

Replace the brush with a self-drawn selection overlay: a fixed-position element
tracks the drag with no chart re-render, and on mouse-up the pixel corners are
converted to time/value bounds via `convertFromPixel` on the readable axes. The
`onZoom` contract is unchanged, so navigation/refetch applies the zoom exactly
as before. The tooltip is hidden during the drag and restored shortly after
release, and Esc cancels an in-progress selection.

Removes the now-unused `pickBoxZoomRange` helper.

Refs DAIN-1772
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Jul 9, 2026

Copy link
Copy Markdown

DAIN-1772

DAIN-1776

@github-actions github-actions Bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Jul 9, 2026
@gggritso

gggritso commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

@cursor review

@gggritso gggritso changed the title perf(charts): Replace heat map drag-zoom brush with a DOM overlay perf(charts): Speed up Heat Map drag-to-zoom Jul 9, 2026
Comment thread static/app/components/charts/useChartBoxZoom.tsx
Comment thread static/app/components/charts/useChartBoxZoom.tsx
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

📊 Type Coverage Diff

Metric Before After Delta
Coverage 93.93% 93.93% ±0%
Typed 134,616 134,654 🟢 +38
Untyped 8,700 8,704 🔴 +4
🔍 4 new type safety issues introduced

Type assertions (as) (4 new)

File Line Detail
static/app/components/charts/useChartBoxZoom.tsx 282 as {getModel?: () => unknown}chart as unknown as {getModel?: () => unknown}
static/app/components/charts/useChartBoxZoom.tsx 282 as unknownchart as unknown
static/app/components/charts/useChartBoxZoom.tsx 285 `as {getComponent?: (mainType: string, idx: number) => unknown}
static/app/components/charts/useChartBoxZoom.tsx 289 `as {
  coordinateSystem?: {
    getRect?: () => {height: number; width: number; x: number; y: number};
  };
}` — `grid as { coordinateSystem?: { getRect?: () => {height: number; width: number; …` |

This is informational only and does not block the PR.

Address review + CI feedback on the box-zoom overlay:

- Reject selections that collapse to zero width on either axis after
  `convertFromPixel` (not just non-finite ones), so a drag can't navigate to
  bounds that match no data.
- End a stuck drag if a mousemove arrives with the primary button released
  (a missed mouseup, e.g. released outside the window), and tear down any
  prior drag on mousedown so overlays/listeners can't stack.
- Drop the now-unused `export` on `EChartBrushArea` (and its dead
  `coordRanges` field), left over from the removed `pickBoxZoomRange`, which
  knip flagged.

Refs DAIN-1772
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gggritso

gggritso commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit eb4a1f1. Configure here.

if (range) {
onZoomRef.current?.(range);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MouseUp ignores auxiliary buttons

Medium Severity

The document mouseup handler ends an active drag on any button release, but mousedown only starts a selection for the primary button. A secondary or middle mouseup while the primary button is still down tears down the overlay and can fire onZoom using the wrong pointer position, producing an incorrect heat map zoom.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit eb4a1f1. Configure here.

The document `mouseup` handler ended an active drag on any button release,
but a drag only starts on the primary button. Releasing a secondary/middle
button mid-drag (primary still held) would tear down the overlay and could
fire `onZoom` at the wrong pointer position. Ignore non-primary releases.

Refs DAIN-1772
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Story previews

Preview the stories changed in this PR on the Vercel deployment:

Preview deployment: https://sentry-c0w5jg7fj.sentry.dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant