Skip to content

fix(viz): follow-ups from #41714 self-review - #42530

Merged
rusackas merged 9 commits into
remove-legacy-viz-pipelinefrom
viz-pipeline-followups
Jul 28, 2026
Merged

fix(viz): follow-ups from #41714 self-review#42530
rusackas merged 9 commits into
remove-legacy-viz-pipelinefrom
viz-pipeline-followups

Conversation

@rusackas

Copy link
Copy Markdown
Member

SUMMARY

Follow-up hardening identified during a self-review of #41714 (the legacy viz pipeline removal). Targets that branch so it can land as a stack once #41714 merges. Each commit is independent and reviewable on its own:

  • deck.gl Multi: removed a dead code path that read a payload.data.slices/.features shape the v1 endpoint never returns, and fixed a real race where an out-of-order/stale async layer response could clobber a newer one — added a load-generation guard and rewrote the tests around the actual v1 fetch shape instead of the dead legacy one.
  • ECharts Timeseries: the percent-change draggable baseline read its own series data back out of chart.getOption() after setOption(), which is fragile and untested; switched to reading the echartOptions prop directly and added the plugin's first test coverage for the drag-to-rebase interaction.
  • Migration downgrade scoping: MigrateViz.downgrade matched slices by target_viz_type alone, so when two source viz types share one target (e.g. MigrateLineChart and MigrateCompareChart both migrate onto echarts_timeseries_line), downgrading one could revert slices the other had upgraded. Added a precise per-row check and removed the CLI's separate, now-inconsistent PREVIOUS_VERSION mapping in favor of deriving the source viz type from the stored backup.
  • Legend-row estimation: Bullet and TimePivot each estimated wrapped-legend row count with similar, subtly different logic; extracted one shared, tested helper and used it to fix a legend/chart overlap bug in TimePivot that Bullet's version didn't have.
  • Sort-metric/orderby: partition, paired_ttest, and parallel_coordinates each reimplemented "append the sort metric if not already selected, order by it" with slightly different rules; extracted a single shared, tested helper.
  • Rose: added test coverage for click-to-drill and, in particular, for the color-priming order invariant (colors come from priming the scale in seriesNames order, not the drilled pie's value-sorted order) -- a case that's easy to accidentally break during future refactors.
  • Migration hardening: upgrade_slice assumed a stored query_context always has a "queries" key; an atypical one (e.g. hand-edited via the API) missing it raised an uncaught KeyError inside the broad except, after viz_type had already been flipped, leaving the slice half-migrated. Now degrades gracefully.
  • Partition transformProps: added the plugin's first test coverage for transformProps.ts itself (as opposed to transformData.ts, which was already well covered) -- the v1-flat-record vs. legacy-nested-hierarchy branch, verboseMap level mapping, and partitionLimit/partitionThreshold parsing.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A -- no user-visible behavior changes; this is bug fixes and test hardening on top of #41714.

TESTING INSTRUCTIONS

  • pytest tests/unit_tests/migrations/ -- all migration unit tests, including two new regression tests (downgrade scoping, malformed query_context)
  • npm run test -- plugins/preset-chart-deckgl/src/Multi plugins/plugin-chart-echarts/src/utils/legendLayout.test.ts plugins/plugin-chart-echarts/test/TimePivot/transformProps.test.ts plugins/plugin-chart-echarts/test/Timeseries/EchartsTimeseries.test.tsx plugins/plugin-chart-echarts/test/Rose/EchartsRose.test.tsx packages/superset-ui-chart-controls/test/utils/buildSortMetricOrderby.test.ts plugins/plugin-chart-paired-t-test/test/buildQuery.test.ts plugins/plugin-chart-parallel-coordinates/test plugins/plugin-chart-partition/test
  • Two of the regression tests (Rose's color-priming test, the query_context KeyError fix) were verified by deliberately reverting the corresponding fix and confirming the new test fails, then restoring it and confirming it passes.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

Stacked on #41714 -- targets remove-legacy-viz-pipeline, not master. Should be reviewed/merged after (or alongside) that PR.

claude added 8 commits July 27, 2026 23:59
…acy branch

Two issues found in a post-merge self-review of the deck_multi rearchitecture:

- loadLayers() had no guard against a layer fetch that resolves after
  deck_slices (or the visibility filter) has already changed again. A stale
  response could still write into subSlicesLayers/layerErrors and pollute
  the autozoom feature accumulator for the new, already-reset generation.
  Fixed with a generation counter: each loadLayers call bumps it, each
  in-flight fetch captures the generation it started under, and its
  callbacks bail out if that generation is no longer current.

- deck_multi's buildQuery always returns an empty queries array (it issues
  no query of its own -- every layer self-fetches), so `payload` is always
  undefined in production. The `payload?.data?.slices`/`payload?.data?.features`
  reads in Multi.tsx were therefore dead code, reachable only because the
  Jest fixtures synthesized a payload shape the real transformProps can
  never produce. Removed the dead branches, made `payload` optional in
  DeckMultiProps to match reality, and converted the fixtures in
  Multi.test.tsx/Multi.color.test.tsx to mock SupersetClient.get/post and
  drive the real v1 fetch path instead.

Added two new tests: one confirming a stale, superseded layer response is
ignored rather than corrupting the current render, and one confirming
autozoom correctly accumulates points across layers that resolve out of
order (the scenario the original v1-autozoom fix was written for, but
never had a multi-layer test).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…dd its first test coverage

The draggable percent-change baseline had zero test coverage despite six
separate production fixes landing against it (crash from a non-group
graphic element, hardcoded colors, NaN from category-axis coercion, a
baseline that reset on every rerender, unthrottled per-pointer-move
setOption calls, and a chart.getOption() undefined crash on warm
navigation). That last one was patched as a symptom rather than a cause:
chart.getOption() reflects the live ECharts instance's internal state,
which can still be empty for a tick after mount.

Root-cause fix: read series data from the echartOptions prop instead --
it's already the source of truth this effect depends on, available
synchronously, and removes the mount race entirely rather than working
around it.

Also named the drag handle's pixel-geometry magic numbers
(BASELINE_HANDLE_WIDTH etc.) and added the first test suite for the
interaction itself: mount position, drag-to-rebase math, snap-to-nearest,
a no-op when dragging back onto the active baseline, the empty-series
edge case, ondragend redraw, and graphic cleanup on unmount.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ded a slice

Two related bugs found in a post-merge self-review, both stemming from
multiple MigrateViz subclasses sharing one target_viz_type (e.g. both
MigrateLineChart and MigrateCompareChart migrate onto
echarts_timeseries_line):

- MigrateViz.downgrade()'s SQL filter (target_viz_type + a form_data_bak
  marker) is coarse by necessity and matches slices from ANY subclass
  sharing that target. Running e.g. `compare`'s downgrade would therefore
  also revert already-settled `line`-sourced slices back to the legacy
  `line` viz_type -- a plugin this same PR deletes. Each row's own
  form_data_bak was still correct (no data corruption), but the migration
  wasn't independently revertible the way its name implies. Fixed by
  checking form_data_bak["viz_type"] == cls.source_viz_type in
  downgrade_slice() before touching a row -- the backup already records
  which migration produced it, no new field needed.

- The `superset viz-migrations downgrade --id` CLI path had the same root
  cause one level up: PREVIOUS_VERSION was keyed by target_viz_type, so
  building it from MIGRATIONS silently let the last-registered subclass
  for a shared target win the dict, and `migrate_by_id` would invoke the
  wrong class's downgrade_slice for any chart from the losing subclass.
  With the fix above, that would have started silently no-op'ing instead
  of misfiring, which just relocated the bug. Removed PREVIOUS_VERSION
  entirely and look the migration up by the slice's own backed-up source
  viz_type via MIGRATIONS (already correctly keyed 1:1 by source type).

Added a regression test exercising the exact MigrateLineChart /
MigrateCompareChart collision.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…overlap bug

Bullet's dynamic legend-row estimation (added to fix a legend-overlapping-
plot bug) was local to its own transformProps.ts. TimePivot has the exact
same class of bug: with period_limit unset a chart can have 50+ series
(current + many priors), and its grid.top was a fixed constant regardless
of legend size, so a wide legend can wrap onto extra rows with nothing
reserving space for them.

Extracted the estimator into utils/legendLayout.ts as
estimateWrappedLegendRowCount() (also naming its previously-inline magic
numbers), reused it from Bullet, and wired it into TimePivot's grid.top so
extra wrapped rows get extra height instead of overlapping the plot.
TimePivot's single-row case is unchanged (same fixed padding as before).

Added tests for the shared helper itself, and for TimePivot: a large
legend reserves more grid.top than a small one, and a hidden legend
reserves none of the extra space.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…igrated buildQuery.ts

partition, paired-t-test, and parallel-coordinates each independently
reimplemented the same ~15 lines of legacy-parity logic: resolve a sort
metric from timeseries_limit_metric, append it to the selected metrics if
missing, and build the orderby tuple. The three differ only in two real,
legacy-behavior-derived ways (verified against master:superset/viz.py
before extracting): whether an unset sort metric falls back to the first
selected metric (partition: yes; the other two: no), and whether ordering
only happens when order_desc is explicitly set (paired-t-test/
parallel-coordinates: yes; partition always orders, just flips direction).

Extracted `buildSortMetricOrderby()` into
@superset-ui/chart-controls/utils, parameterized on those two axes so the
three charts keep their real, intentional differences rather than being
forced into false uniformity. horizon was left alone -- its ordering
logic is simpler (orders directly by the first metric, no
timeseries_limit_metric override or metrics-list injection) and isn't
actually the same shape as the other three.

Also fills a test gap the extraction surfaced: paired-t-test's buildQuery
tests only exercised order_desc: true with a sort metric set, never the
"append to metrics but don't order" case that is the entire point of its
order-gating behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
EchartsRose.tsx had no component-level test at all: the click-to-drill
dataIndex mapping and, more importantly, the CategoricalColorNamespace
priming-order dependency (the scale must be primed in seriesNames order
before building the drilled pie's slices, since getScale() returns a
fresh scale each time and slices are sorted by value, a different order
than seriesNames) were both unverified -- easy to silently break into
wrong colors with no error.

Verified the color-priming test actually catches a regression by
temporarily reordering the priming call to happen after the value-sort
(reproducing the exact bug class described above): the test failed as
expected, then passed again once reverted. Getting there required also
resetting @superset-ui/core's process-wide label-color singleton between
tests -- it remembers a label's color per sliceId, so without the reset
whichever test ran first would permanently decide the colors for every
later test in the file, making a naive version of this test tautological
(always passing regardless of priming order).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
upgrade_slice assumed a stored query_context always carries a
"queries" key. An atypical one (e.g. hand-edited via the API) missing
it raised a bare KeyError inside the broad except, after viz_type had
already been flipped -- leaving the slice half-migrated (new
viz_type, but stale params/query_context in the old shape).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
transformData.ts had thorough tests, but transformProps.ts itself --
the glue that decides between the v1 flat-record path and the legacy
nested-hierarchy passthrough, maps groupby columns through verboseMap,
and parses partitionLimit/Threshold -- had none.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dosubot dosubot Bot added change:frontend Requires changing the frontend risk:refactor High risk as it involves large refactoring work viz:charts:deck.gl Related to deck.gl charts viz:charts:echarts Related to Echarts labels Jul 28, 2026
@bito-code-review

bito-code-review Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Bito Automatic Review Skipped - Branch Excluded

Bito didn't auto-review because the source or target branch is excluded from automatic reviews.
No action is needed if you didn't intend for the agent to review it. Otherwise, to manually trigger a review, type /review in a comment and save.
You can change the branch exclusion settings here, or contact your Bito workspace admin at evan@preset.io.

@github-actions github-actions Bot added risk:db-migration PRs that require a DB migration plugins packages labels Jul 28, 2026
@netlify

netlify Bot commented Jul 28, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 1ebad41
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a68680d840d1e000865220e
😎 Deploy Preview https://deploy-preview-42530--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Comment thread superset-frontend/plugins/plugin-chart-echarts/test/Rose/EchartsRose.test.tsx Outdated
Comment thread superset-frontend/plugins/preset-chart-deckgl/src/Multi/Multi.tsx
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.52632% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.91%. Comparing base (464674f) to head (121866f).

Files with missing lines Patch % Lines
superset/cli/viz_migrations.py 0.00% 6 Missing ⚠️
superset/migrations/shared/migrate_viz/base.py 0.00% 3 Missing ⚠️
...nd/plugins/preset-chart-deckgl/src/Multi/Multi.tsx 88.88% 2 Missing ⚠️
Additional details and impacted files
@@                      Coverage Diff                       @@
##           remove-legacy-viz-pipeline   #42530      +/-   ##
==============================================================
+ Coverage                       65.85%   65.91%   +0.06%     
==============================================================
  Files                            2807     2808       +1     
  Lines                          156075   156087      +12     
  Branches                        35849    35849              
==============================================================
+ Hits                           102781   102883     +102     
+ Misses                          51382    51292      -90     
  Partials                         1912     1912              
Flag Coverage Δ
hive 38.58% <0.00%> (-0.01%) ⬇️
javascript 72.48% <97.01%> (+0.12%) ⬆️
mysql 57.52% <0.00%> (-0.01%) ⬇️
postgres 57.56% <0.00%> (-0.01%) ⬇️
presto 40.54% <0.00%> (-0.01%) ⬇️
python 59.00% <0.00%> (-0.01%) ⬇️
sqlite 57.18% <0.00%> (-0.01%) ⬇️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Guard the deck.gl Multi metadata-fetch effect with its own generation
  ref so a slower, earlier fetchSubslices call can't clobber a newer
  generation's loadLayers state.
- Fix a divide-by-zero in Bullet transformProps that produced an
  Infinite marker symbolOffset for grouped empty results.
- Fix a stale-resolver bug in the Multi.test.tsx stale-response test
  that silently left the slice-1 mock promise pending.
- Rename a misleadingly-titled Rose test to match its actual assertion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rusackas
rusackas merged commit bbd0219 into remove-legacy-viz-pipeline Jul 28, 2026
58 checks passed
@rusackas
rusackas deleted the viz-pipeline-followups branch July 28, 2026 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:frontend Requires changing the frontend packages plugins risk:db-migration PRs that require a DB migration risk:refactor High risk as it involves large refactoring work size/XXL viz:charts:deck.gl Related to deck.gl charts viz:charts:echarts Related to Echarts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants