Skip to content

Upgrade d3 from 3 to 7 - #7858

Open
arikfr wants to merge 2 commits into
fe/07-routing-utility-libsfrom
fe/08-d3-7
Open

arikfr wants to merge 2 commits into
fe/07-routing-utility-libsfrom
fe/08-d3-7

Conversation

@arikfr

@arikfr arikfr commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

What type of PR is this?

  • Other (dependencies)

Description

Part 8 of the frontend dependency upgrade stack tracked in #7848. Stacked on #7857.

Ports the d3 3 code to d3 7: viz-lib's box plot, sankey, sunburst, word cloud, pie/heatmap colors, map colors and the Albers USA projection script, plus the client's Resizable. Plotly no longer holds this back: since plotly.js 2 (#7359), it depends on individual d3 modules rather than the d3 package.

API changes handled

  • Scales, axes, arcs and projections move to their d3 4+ names (scaleBand, scaleLinear, scaleOrdinal, axisBottom/Left/Right, arc, geoAlbersUsa/geoMercator).
  • d3.layout.partition becomes d3.hierarchy + d3.partition.
  • d3.nest becomes d3.group/groups. d3.functor and d3.timer.flush become an inline equivalent and timerFlush.
  • Event listeners receive (event, datum) since d3 6, which affects the sankey and sunburst mouseover handlers.
  • Enter selections are no longer merged into the update selection. Without an explicit merge(), the sunburst's breadcrumbs lost their positions and stacked on top of each other; the comparison below caught this.

Where d3 7 changed behavior, the port keeps d3 3's results

  • Sunburst: d3 3 sorted children by value (largest first), computed values from leaves only, and returned the nodes in pre-order of the sorted tree. That order sets the color domain, so nodes are collected after sorting. My first version collected them before sorting, which gave every segment a different color; the visual tests (Add Playwright visual tests for visualizations, remove Percy #7853) caught it.
  • Box plot with a single column: d3 3's ordinal scale returned 0 for a value outside its domain, and d3 7's band scale returns undefined, which produced a NaN offset. The offset uses 0 in that case, as before.
  • Box plot axes: d3 4+ axes draw with currentColor, which turned the tick labels gray. The box plot's axes are styled black, as before.
  • category20, which d3 removed, is kept as D3Category20 with d3 3's colors (sankey, word cloud).
  • Word cloud: when all words have the same count, d3 3 gave every word the smallest size and d3 4+ would use the middle one (10px vs 55px).
  • Resizable: it asked for ease("swing"), which d3 3 didn't know and treated as linear, so it now uses easeLinear.
  • convert-projection.ts (offline script): pinned to d3 3's default Mercator scale (150; d3 4+ uses ~152.6).

A latent bug, fixed: box-plot/d3box.ts used a global d3 it never imported. That global only existed because d3 3's UMD bundle takes its AMD branch under webpack and assigns this.d3. It now imports d3.

How is this tested?

  • Unit tests (pytest, jest)

  • Manually

  • Lint, type-check (client and viz-lib), jest (client 96, viz-lib 154, no snapshot changes; the pie and heatmap fixture tests cover the color scales) and the production build pass locally.

  • Rendering comparison: I rendered the sankey, the sunburst (both data formats, plus the hover state) and the box plot in jsdom with the same data, using the previous code on d3 3.5.17 and this code on d3 7.9.0, then diffed the SVG:

    • Sunburst: identical elements, attributes and breadcrumbs. Arc coordinates match within 0.0005px (d3 7 rounds path coordinates to 3 decimals). This comparison missed the color order bug above.
    • Sankey: identical link paths and nodes. Node stroke colors are now written as rgb(…) instead of hex, and d3 7 rounds darker() channels where d3 3 truncated, so some differ by 1.
    • Box plot: identical boxes, medians, whiskers, outliers and tick labels. d3 4+ axes set presentation attributes (overridden by our existing CSS), and ticks shift by 0.5px on non-HiDPI screens for crisp lines.
  • Visual tests (Add Playwright visual tests for visualizations, remove Percy #7853): pixel-identical to the previous step, except the box plot, whose grid lines shift by a sub-pixel amount (d3 4+ axis offsets). Its baseline is updated in a separate commit.

  • Regenerated usa-albers.geo.json with the ported script: all 13,380 coordinates match the committed file (max difference 2e-13).

Bundle size: the vendor chunk grows by about 158 KB (uncompressed). viz-lib's lib/ is CommonJS, so webpack can't tree-shake d3 and includes all of d3 7. Building lib/ as ES modules would fix that; it's a follow-up because the client's Jest setup currently relies on lib/ being CommonJS.

🤖 Generated with Claude Code

Review in cubic

@greptile-apps

greptile-apps Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[High risk] Major version upgrade of the charting library.

The PR appears safe to merge; no outstanding findings or new actionable issues were identified.

Summary

This PR migrates the frontend and visualization library from D3 3 to D3 7, ports affected visualizations and interactions, and updates the lockfile and visual baseline. Since the previous review, it also restores Jest routing configuration and adds route-ordering tests.

Reviews (6) · Last reviewed commit: "Update visual test baselines for d3 7" · Reviewed by Greptile

Comment thread viz-lib/src/visualizations/sunburst/initSunburst.ts

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 15 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="viz-lib/src/visualizations/sunburst/initSunburst.ts">

<violation number="1" location="viz-lib/src/visualizations/sunburst/initSunburst.ts:263">
P3: Add a Cypress interaction test that moves between sunburst segments and verifies highlighting and breadcrumb placement; a static snapshot does not exercise the migrated hover behavior.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread viz-lib/src/visualizations/box-plot/Renderer.tsx
Comment thread viz-lib/src/visualizations/choropleth/maps/convert-projection.ts
// helper function mouseover to handle mouseover events/animations and calculation
// of ancestor nodes etc
function mouseover(d: any) {
function mouseover(event: MouseEvent, d: any) {

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.

P3: Add a Cypress interaction test that moves between sunburst segments and verifies highlighting and breadcrumb placement; a static snapshot does not exercise the migrated hover behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At viz-lib/src/visualizations/sunburst/initSunburst.ts, line 263:

<comment>Add a Cypress interaction test that moves between sunburst segments and verifies highlighting and breadcrumb placement; a static snapshot does not exercise the migrated hover behavior.</comment>

<file context>
@@ -271,7 +260,7 @@ export default function initSunburst(data: any) {
     // helper function mouseover to handle mouseover events/animations and calculation
     // of ancestor nodes etc
-    function mouseover(d: any) {
+    function mouseover(event: MouseEvent, d: any) {
       // build percentage string
       const percentage = ((100 * d.value) / totalSize).toPrecision(3);
</file context>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Same as the other sunburst thread: reasonable, better placed in the Playwright suite.

@arikfr
arikfr force-pushed the fe/07-routing-utility-libs branch from ba68eec to 3d57946 Compare September 28, 2026 09:42
@arikfr
arikfr force-pushed the fe/07-routing-utility-libs branch 2 times, most recently from 17aacee to cb82389 Compare October 3, 2026 21:06
@arikfr
arikfr force-pushed the fe/08-d3-7 branch 2 times, most recently from 682aa1f to 900d1c1 Compare October 4, 2026 13:56
@arikfr
arikfr force-pushed the fe/07-routing-utility-libs branch from cb82389 to 5a1c8a9 Compare October 4, 2026 13:56
arikfr and others added 2 commits October 8, 2026 14:40
Port the d3 3 code in viz-lib (box plot, sankey, sunburst, word cloud,
pie and heatmap colors, map colors, the Albers USA projection script)
and client/app/components/Resizable to d3 7:

- scales, axes, arcs and projections move to their d3 4+ names
  (scaleBand/scaleLinear/scaleOrdinal, axisBottom/…, arc, geo*)
- d3.layout.partition → d3.hierarchy + d3.partition. Keep d3 3's
  behavior: children sorted by value, leaf-only values, and nodes
  collected in pre-order before sorting (that order decides the colors).
- d3.nest → d3.group(s); d3.functor, d3.timer.flush → inline, timerFlush
- event listeners get (event, datum) since d3 6
- enter selections are no longer merged into the update selection, so
  merge them for the sunburst breadcrumbs
- category20 was removed from d3; keep d3 3's list as D3Category20
- keep d3 3's results where d3 4+ changed defaults: the "swing" easing
  (which d3 3 treated as linear), word sizes when all counts are equal,
  and the Mercator scale the committed usa-albers.geo.json was built with
- d3box.ts used a global `d3` that only existed because d3 3's bundle
  took its AMD branch under webpack; import it instead

Jest: add d3 7 and its ESM-only dependencies to the transform lists, in
the client and in viz-lib.

Part of #7848.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
d3 4+ axes place tick and grid lines half a pixel differently, which
shifts the box plot's grid lines by a sub-pixel amount.
@arikfr
arikfr force-pushed the fe/07-routing-utility-libs branch from 5a1c8a9 to 109059d Compare October 8, 2026 12:36

This branch has not been deployed

No deployments
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