Compare commits

..
Author SHA1 Message Date
Claude Code fa52a38be9 Merge origin/master into misc-charts-examples 2026-08-14 18:01:35 -07:00
Claude Code 0ef7482713 fix(examples): fix broken histogram_v2 and deck_geojson thumbnails
Life Expectancy Histogram had the same bug as the earlier Bubble chart
fix: its adhoc filter pointed at 2014, which has zero SP_DYN_LE00_IN data
in the world_health dataset, so the thumbnail showed an empty chart.
Moved it to 2011, which has real data.

Deck.gl GeoJson rendered as a single solid-colored blob: the global
fill_color_picker (alpha=1) always overrides any per-feature fill color,
and line_width defaulted to ~1 meter, invisible at city zoom, so none of
the 25 zip-code boundaries were visible. Baked a population-ranked
choropleth fill-color into each zip code's GeoJSON Feature, set
fill_color_picker's alpha to 0 so the per-feature colors show through,
switched the stroke to a visible white pixel-width line, and turned off
the extruded flag since there's no per-feature elevation data backing it.
2026-08-14 17:51:56 -07:00
Claude Code 6828e17516 feat(examples): add a horizontally-oriented tree chart example
Sales Territory Tree Horizontal reuses the same sales_territory_hierarchy
dataset as the existing radial Sales Territory Tree example, but with
layout: orthogonal / orient: LR so the gallery shows tree_chart's other
common orientation instead of two renders of the same radial chart.
2026-08-14 16:33:41 -07:00
Claude Code d00bde7b8e Merge origin/master into misc-charts-examples
# Conflicts:
#	superset/examples/world_health/charts/Life_Expectancy_VS_Rural.yaml
2026-08-14 15:57:05 -07:00
Claude Code fb5029d06d feat(examples): capture every chart on every example dashboard, replacing hand-made art with live thumbnails
The capture tool discovered ~53 viz types across the example dashboards
but only ever captured one representative chart per type into
thumbnail.png; every gallery slot beyond that was hand-made art that
predated any of this, unrelated to what the example dashboards actually
contain. Two of those hand-made images turned out to be faked outright:
ag-grid-table's and cartodiagram's "dark" variants were a naive full-image
color inversion of the light one rather than a real dark-theme render
(confirmed by pixel-diffing every light/dark pair in the repo -- these
two matched a naive invert at 85-99%, every genuine capture matched at
under 2%).

To fix this without silently losing the old art or shipping anything
dishonest:

1. Every existing thumbnail/gallery image was renamed with a
   custom_thumb_ prefix (plugin imports updated to match), so it's
   preserved and clearly labeled as hand-made rather than captured.
2. The capture script (capture-viz-thumbnails.spec.ts) was generalized:
   it now captures every distinct chart per viz type, not just one --
   the preferred (or alphabetically-first) chart becomes the picker
   thumbnail, and every other distinct chart fills the next
   already-declared exampleGallery slot, via a new VIZ_TYPE_GALLERY map
   mirroring VIZ_TYPE_THUMBNAILS. Also fixed a pre-existing bug where
   VIZ_TYPE_THUMBNAILS.rose pointed at plugin-chart-rose, a package
   consolidated into plugin-chart-echarts/src/Rose long ago.
3. Ran it for real against a live instance with examples loaded
   (including the new Deck.gl GeoJson example). Every plugin whose
   real capture succeeded had its import flipped from the custom_thumb_
   file back to the plain, freshly-captured one.
4. For deck.gl chart types, the dark "capture" is confirmed genuinely
   pixel-identical to light (they render static map tiles that don't
   respond to prefers-color-scheme) rather than skipped/left stale, so
   the plain dark file is an honest copy of the real light capture
   instead of mismatched old art.

Four viz types still have zero chart on any example dashboard and keep
their custom_thumb_ imports as an honest label rather than a graduated
(but fake) real capture:
- cartodiagram: needs a hand-crafted embedded sub-chart config
- deck_multi: sub-layers are referenced by raw DB-assigned chart ids
  with no UUID-based remap path through the YAML importer -- a real
  gap needing an importer/exporter fix, not something a fixture alone
  can solve
- pop_kpi, ag-grid-table: gated behind ChartPluginsExperimental /
  AgGridTableEnabled and unregistered by default, so a default-visible
  dashboard chart would just show every regular user a broken card
2026-08-14 15:10:59 -07:00
Claude Code 041d735ddb feat(examples): add a Deck.gl GeoJson example chart
deck_geojson had zero chart on any example dashboard, needing an example
dataset with a serialized GeoJSON Feature column -- something no
existing example data had. Derived a `geojson` column for
sf_population_polygons (already used by the deck.gl demo dashboard for
deck_polygon/deck_contour) from its existing `contour` point-list
column, wrapping each zip code's boundary as a proper GeoJSON Feature.
Added the chart to Misc Charts and to the dashboard's cross-filter
scope list.

Also carries the world_health bubble_v2 migration + time_range fix
already sent separately to master (#43152); duplicated here so this
branch's own capture tooling and CI have working data independent of
that PR's merge timing.
2026-08-14 15:05:11 -07:00
Claude Code 49a4086715 fix(charts): fix Bubble chart axis-interval crash
xAxis.interval was set directly from the xAxisLabelInterval control
('auto'/'0'), but xAxis.interval forces echarts' IntervalScale into a
fixed-tick-spacing mode that expects a number and crashes the axis
"nice" tick calculation. Every bubble_v2 chart hit this at its default
control value; there was no existing test coverage for this file.

The interval belongs nested under axisLabel, where it only controls how
many labels are skipped, matching how every other echarts chart in this
codebase (e.g. Timeseries) wires it. Added a regression test.

Found while building out live thumbnail capture for example dashboards
(this PR) -- bubble_v2 apparently never had a real dashboard-placed
chart to exercise this path before.
2026-08-14 15:03:46 -07:00
rusackas 31218987ec Merge remote-tracking branch 'origin/master' into pr42328-local 2026-08-13 14:05:18 -07:00
rusackasandClaude Opus 4.8 de4def38cb fix: drop redundant alembic merge migration (rebase cruft)
aaed1198e21f duplicated master's own resolution (d7cecc48bd55) of the
same two heads (4f145192b583, c4a1b8e2d739); nothing in this branch's
chain depends on it, so it can be dropped outright.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-13 10:36:25 -07:00
Claude Code 4f77538bc2 feat(examples): add a Handlebars CSS showcase tab to the misc_charts dashboard
Addresses rusackas's leftover note on #42328: "The handlebars showcase tab
idea (keep the logo, add a Misc tab of CSS-heavy handlebars examples)."

- Kept the existing Sales Handlebars List example (the Superset logo demo)
  untouched, per "keep the logo".
- Added a new "Sales Handlebars Showcase" chart: a product-line leaderboard
  rendered as cards, using the plugin's actual CSS-heavy capabilities --
  the dedicated "CSS Styles" field for card/badge/gradient-bar styling, the
  registered `formatNumber` helper, and just-handlebars-helpers'
  conditional (`gt`, `eq`) and math (`sum`, `division`, `multiplication`)
  helpers for rank medals (top 3), tier badges (Top Seller/Strong/Growing),
  and proportional progress bars. The card markup, rank, and badge text
  are plain text/emoji and always render; the colors/shape/bars come from
  the CSS field and require an administrator to allow `style` elements via
  HTML_SANITIZATION_SCHEMA_EXTENSIONS (server-side sanitization strips
  `<style>` by default) -- same caveat the control's own tooltip warns
  about, documented in the chart's description.
- Restructured misc_charts/dashboard.yaml from a flat grid into two tabs
  ("Charts" holding all existing content unchanged, "Handlebars Showcase"
  holding the new chart), matching the TABS/TAB position-JSON schema real
  Superset-authored tabbed dashboards use (e.g. sales_dashboard).

Verified against a local build with the new content loaded: the "Charts"
tab still renders every pre-existing chart, and the new tab renders the
leaderboard correctly in both light and dark app themes (screenshotted,
not just structurally validated) -- caught and fixed one real issue this
way, a rank-number color that inherited the app's dark-mode text color and
went low-contrast against the card background.

Not part of this change: the handlebars plugin's own thumbnail.png
gallery art (the crawler has no VIZ_TYPE_THUMBNAILS entry for handlebars
by design, to preserve the existing logo art) and the separate "gallery
diversification" backlog item from the same PR comment.
2026-08-06 16:23:46 -07:00
Claude Code 5bbd4c6d23 fix(examples): show real up/down, hierarchy, density and variance in 4 misc-chart thumbnails
Addresses rusackas's own leftover checklist on #42328:

- Waterfall: pointed at a new quarterly_sales_delta dataset (Q2 2003 - Q2
  2005 quarter-over-quarter sales change) instead of raw quarterly totals,
  which are always positive and rendered as all-green bars regardless of
  whether sales actually grew or shrank. The delta metric produces genuine
  green/red bars.
- Tree: added a Territory > Country > City sales hierarchy (41 nodes, 3
  levels) built from cleaned_sales_data, replacing the tiny 12-node
  USA-only example that shipped with this PR.
- Horizon: new Population Growth Horizon example (20 most populous
  countries' annual population growth rate, 1960-2014) replacing the old
  7-band/2.5-year example that left most of the thumbnail empty; 20 bands
  at the default 25px row height now fill the full 512px canvas and show
  real oscillation (several countries dip negative).
- deck.gl Heatmap: fixed a real bug -- color_scheme_type was set to
  'default_palette', which isn't a valid value in this plugin's control
  panel (only 'fixed_color'/'linear_palette' are), so getColorRange()
  fell through every switch case and the layer silently rendered with an
  undefined colorRange. Set it to 'linear_palette' with the 'fire' scheme,
  and raised row_limit from 5,000 to 50,000 (of 261k available rows).

Recaptured thumbnails for exactly these 4 viz types (light + dark, deck.gl
heatmap dark correctly skipped as canvas/theme-invariant) via
capture-viz-thumbnails.spec.ts against a local build with the new examples
loaded, and visually confirmed each one before committing.
2026-08-06 15:55:12 -07:00
Claude Code 3b98869e0a Merge remote-tracking branch 'origin/master' into misc-charts-examples
# Conflicts:
#	superset-frontend/plugins/plugin-chart-calendar/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-calendar/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-chord/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-chord/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-country-map/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-country-map/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-echarts/src/Bullet/images/example-dark.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/Bullet/images/example.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/Bullet/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-echarts/src/Bullet/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/images/example-dark.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/images/example.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-horizon/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-horizon/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-paired-t-test/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-paired-t-test/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-parallel-coordinates/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-parallel-coordinates/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-partition/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-partition/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-world-map/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-world-map/src/images/thumbnail.png
#	superset-frontend/plugins/preset-chart-deckgl/src/Multi/Multi.tsx
#	superset-frontend/plugins/preset-chart-deckgl/src/Multi/MultiV1.test.tsx
#	superset/migrations/shared/migrate_viz/base.py
#	tests/unit_tests/migrations/viz/upgrade_malformed_query_context_test.py
2026-08-06 15:20:30 -07:00
Claude Code e7fa8529ce Merge remote-tracking branch 'origin/remove-legacy-viz-pipeline' into HEAD
# Conflicts:
#	UPDATING.md
#	superset-frontend/package-lock.json
#	superset-frontend/package.json
#	superset-frontend/packages/superset-ui-core/src/chart/clients/ChartClient.ts
#	superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.test.tsx
#	superset-frontend/playwright/components/dashboard/index.ts
#	superset-frontend/plugins/legacy-plugin-chart-chord/src/transformProps.ts
#	superset-frontend/plugins/legacy-plugin-chart-rose/src/controlPanel.tsx
#	superset-frontend/plugins/legacy-plugin-chart-rose/src/images/example1-dark.jpg
#	superset-frontend/plugins/legacy-plugin-chart-rose/src/images/example1.jpg
#	superset-frontend/plugins/legacy-plugin-chart-rose/src/images/example2-dark.jpg
#	superset-frontend/plugins/legacy-plugin-chart-rose/src/images/example2.jpg
#	superset-frontend/plugins/legacy-plugin-chart-rose/src/index.ts
#	superset-frontend/plugins/legacy-plugin-chart-rose/src/transformProps.ts
#	superset-frontend/plugins/legacy-plugin-chart-world-map/types/external.d.ts
#	superset-frontend/plugins/legacy-preset-chart-nvd3/src/Bubble/index.ts
#	superset-frontend/plugins/plugin-chart-calendar/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-calendar/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-calendar/src/images/thumbnailLarge.png
#	superset-frontend/plugins/plugin-chart-chord/package.json
#	superset-frontend/plugins/plugin-chart-chord/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-chord/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-chord/src/images/thumbnailLarge.png
#	superset-frontend/plugins/plugin-chart-chord/test/index.test.ts
#	superset-frontend/plugins/plugin-chart-country-map/package.json
#	superset-frontend/plugins/plugin-chart-country-map/src/countries/chile.geojson
#	superset-frontend/plugins/plugin-chart-country-map/src/countries/fiji.geojson
#	superset-frontend/plugins/plugin-chart-country-map/src/countries/french_polynesia.geojson
#	superset-frontend/plugins/plugin-chart-country-map/src/countries/philippines_regions.geojson
#	superset-frontend/plugins/plugin-chart-country-map/src/countries/united_states_minor_outlying_islands.geojson
#	superset-frontend/plugins/plugin-chart-country-map/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-country-map/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-country-map/src/images/thumbnailLarge.png
#	superset-frontend/plugins/plugin-chart-echarts/src/Bullet/images/example-dark.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/Bullet/images/example.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/Bullet/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-echarts/src/Bullet/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-echarts/src/Bullet/images/thumbnailLarge.png
#	superset-frontend/plugins/plugin-chart-echarts/src/Bullet/index.ts
#	superset-frontend/plugins/plugin-chart-echarts/src/Bullet/transformProps.ts
#	superset-frontend/plugins/plugin-chart-echarts/src/Rose/controlPanel.tsx
#	superset-frontend/plugins/plugin-chart-echarts/src/Rose/images/example1-dark.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/Rose/images/example1.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/Rose/images/example2-dark.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/Rose/images/example2.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/Rose/images/thumbnailLarge.png
#	superset-frontend/plugins/plugin-chart-echarts/src/Rose/index.ts
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/EchartsTimePivot.tsx
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/images/example-dark.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/images/example.jpg
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/images/thumbnailLarge.png
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/index.ts
#	superset-frontend/plugins/plugin-chart-echarts/src/TimePivot/transformProps.ts
#	superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx
#	superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
#	superset-frontend/plugins/plugin-chart-echarts/src/index.ts
#	superset-frontend/plugins/plugin-chart-echarts/test/TimePivot/transformProps.test.ts
#	superset-frontend/plugins/plugin-chart-horizon/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-horizon/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-horizon/src/images/thumbnailLarge.png
#	superset-frontend/plugins/plugin-chart-paired-t-test/package.json
#	superset-frontend/plugins/plugin-chart-paired-t-test/src/buildQuery.ts
#	superset-frontend/plugins/plugin-chart-paired-t-test/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-paired-t-test/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-paired-t-test/test/buildQuery.test.ts
#	superset-frontend/plugins/plugin-chart-parallel-coordinates/package.json
#	superset-frontend/plugins/plugin-chart-parallel-coordinates/src/buildQuery.ts
#	superset-frontend/plugins/plugin-chart-parallel-coordinates/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-parallel-coordinates/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-parallel-coordinates/src/images/thumbnailLarge.png
#	superset-frontend/plugins/plugin-chart-partition/src/buildQuery.ts
#	superset-frontend/plugins/plugin-chart-partition/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-partition/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-partition/src/images/thumbnailLarge.png
#	superset-frontend/plugins/plugin-chart-rose/CHANGELOG.md
#	superset-frontend/plugins/plugin-chart-rose/README.md
#	superset-frontend/plugins/plugin-chart-rose/package.json
#	superset-frontend/plugins/plugin-chart-rose/src/ReactRose.tsx
#	superset-frontend/plugins/plugin-chart-rose/src/Rose.ts
#	superset-frontend/plugins/plugin-chart-rose/src/controlPanel.tsx
#	superset-frontend/plugins/plugin-chart-rose/src/images/example1-dark.jpg
#	superset-frontend/plugins/plugin-chart-rose/src/images/example1.jpg
#	superset-frontend/plugins/plugin-chart-rose/src/images/example2-dark.jpg
#	superset-frontend/plugins/plugin-chart-rose/src/images/example2.jpg
#	superset-frontend/plugins/plugin-chart-rose/src/index.ts
#	superset-frontend/plugins/plugin-chart-rose/src/stories/Rose.stories.tsx
#	superset-frontend/plugins/plugin-chart-rose/src/stories/data.ts
#	superset-frontend/plugins/plugin-chart-rose/src/transformProps.ts
#	superset-frontend/plugins/plugin-chart-rose/tsconfig.json
#	superset-frontend/plugins/plugin-chart-rose/types/external.d.ts
#	superset-frontend/plugins/plugin-chart-world-map/package.json
#	superset-frontend/plugins/plugin-chart-world-map/src/images/thumbnail-dark.png
#	superset-frontend/plugins/plugin-chart-world-map/src/images/thumbnail.png
#	superset-frontend/plugins/plugin-chart-world-map/src/images/thumbnailLarge.png
#	superset-frontend/plugins/preset-chart-deckgl/src/Multi/Multi.color.test.tsx
#	superset-frontend/plugins/preset-chart-deckgl/src/Multi/Multi.test.tsx
#	superset-frontend/plugins/preset-chart-deckgl/src/Multi/Multi.tsx
#	superset-frontend/plugins/preset-chart-deckgl/src/Multi/buildQuery.ts
#	superset-frontend/scripts/check-custom-rules.js
#	superset-frontend/src/components/Chart/chartAction.ts
#	superset-frontend/src/explore/components/ExploreChartPanel/index.tsx
#	superset-frontend/src/visualizations/presets/MainPreset.ts
#	superset-frontend/tsconfig.json
#	superset/async_events/async_query_manager.py
#	superset/migrations/versions/2026-07-02_21-00_d4e5f6a7b8c9_migrate_bubble_chart_to_echarts.py
#	superset/views/utils.py
#	tests/unit_tests/async_events/async_query_manager_tests.py
2026-08-04 22:53:51 -07:00
Claude Code c487c006e9 fix(migrations): merge report-retry-state-columns with compare-chart merge revision
master added a new migration head (f3a8c1d2e9b7) branching from the same
revision already folded into 9d744c5dd981, producing two heads again.
2026-08-04 10:18:55 -07:00
Claude Code fd27a0c2fd Merge remote-tracking branch 'origin/master' into HEAD
# Conflicts:
#	superset-frontend/package-lock.json
2026-08-04 10:13:25 -07:00
Claude Code 42c6746339 Merge remote-tracking branch 'origin/master' into remove-legacy-viz-pipeline
A second, smaller conflict landed while pushing the previous rebase
fix: master's Prettier-to-Oxfmt migration touched every plugin
package.json, colliding with this branch's own renames/deletions of
the legacy-prefixed packages.

Resolved:
- Two modify/delete conflicts (legacy-plugin-chart-rose,
  legacy-preset-chart-nvd3): kept deleted. Both packages are fully
  removed on this branch with no replacement package (Rose now lives
  inside plugin-chart-echarts/src/Rose/).
- Five package.json field-reordering conflicts (chord, country-map,
  paired-t-test, parallel-coordinates, world-map): master's migration
  reordered/added fields (keywords, homepage) on the old legacy-
  prefixed paths while this branch had independently reordered the
  same renamed files. Reconstructed each to the canonical field order
  already used by sibling packages that merged cleanly (partition,
  echarts).
- One content conflict in paired-t-test/src/TTestTable.tsx: discarded
  master's dead legacy reactable-based sortConfig block, already fully
  replaced by this branch's own COMPARATORS object earlier in the same
  file.

package-lock.json regenerated via a clean npm install (no
--legacy-peer-deps, after clearing corrupted npm cache entries) to
correctly reflect both the dependency-tree changes from this rebase
and the Oxfmt migration's own devDependency swap (prettier/eslint-
plugin-prettier out, oxfmt in).
2026-08-03 10:51:30 -07:00
Claude Code fbd2e6032e fix(deps): regenerate package-lock.json, previous regeneration was incomplete
The lockfile committed while resolving the last rebase conflict against
master was missing several real transitive dependencies (preact,
@react-spring/*, polished, react-ace, @deck.gl/widgets), which failed
npm ci in CI's frontend-build/docker-build with "Missing: X from lock
file" errors, cascading into pre-commit/cypress/playwright/docker
failures.

Root-caused to two compounding issues on the machine that regenerated
it: a corrupted local npm cache (npm cache verify found and cleaned up
643 bad entries) and the --legacy-peer-deps flag, which does not
auto-install a peer dependency's own transitive deps the way a plain
npm install does -- @deck.gl/widgets is itself a peer dependency, and
preact is one of its own dependencies, so it was silently dropped.

Regenerated cleanly with a plain npm install after clearing the cache;
verified node_modules/preact, node_modules/@react-spring/core, and
node_modules/@deck.gl/widgets are all present this time.
2026-08-03 10:33:07 -07:00
Claude Code e66b2f8be5 Merge remote-tracking branch 'origin/master' into remove-legacy-viz-pipeline
Routine rebase; the only conflict was package-lock.json, regenerated
via npm install against the already-cleanly-merged package.json.
Everything else (package.json, plugin-chart-partition's package.json,
superset/views/base.py, superset/views/core.py,
tests/integration_tests/security_tests.py) merged automatically.
2026-08-03 10:04:50 -07:00
Claude Code 04bd07ad6e fix(migrations): don't discard an original empty query_context on downgrade
Addresses @sadpandajoe's follow-up review comment on #41714: an
original query_context with "queries": [] backs up as a falsy-but-
present empty list, not None. downgrade_slice's truthiness check
(`if queries_bak:`) treated that the same as "no context was ever
stored" and set query_context to None, discarding the slice's
original datasource and form_data instead of restoring it.

Also fixes the same ambiguity at the source: QUERIES_BAK_FIELD_NAME
defaulted to {} rather than None when absent from form_data, which
could be misread as a real (if malformed) backup rather than "key not
present". Changed to the natural None default so "no backup" and "an
empty list backup" stay distinguishable, and dedented the
query_context assignment that was incorrectly nested inside the
"form_data" in query_context check (a context missing "form_data"
would restore params/viz_type but silently leave query_context at its
upgraded value).

Also updates the stale UPDATING.md note for this PR: the percent-
re-basing and deck_multi autozoom limitations it warned about were
both resolved earlier in this branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-02 09:27:28 -07:00
rusackasandClaude Opus 4.8 1c98c0711e fix(deckgl): address review feedback on Multi.tsx v1 rewrite
- Enforce DECK_MULTI_MAX_SLICES client-side before fanning out
  per-layer metadata/data requests, mirroring the cap the removed
  viz.py pipeline used to enforce server-side.
- Accumulate autozoom features by slice_id (bucketed by viz_type)
  instead of overwriting by viz_type, so two layers sharing a
  viz_type no longer clobber each other's points.
- ChartClient.loadQueryData now posts the built query context at
  the top level of the request body (as /api/v1/chart/data expects)
  instead of nesting it under a query_context key, and unwraps the
  {result: [...]} response shape correctly.
- migrate_viz downgrade no longer discards a hand-edited
  query_context that lacks a "queries" key; the whole context is
  backed up and restored verbatim in that case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-31 17:02:54 -07:00
Claude Code 13dd061907 fix(migrations): merge alembic heads after master rebase
The merge onto master left two migration heads: this branch's
88360afb61ed (migrate_compare_chart_to_echarts) and master's
e7d93a524ff6 (add_purge_audit_log), from parallel migration work
landing on both sides. Adds the standard no-op merge revision joining
them, generated via `superset db merge` and verified with a clean
`superset db upgrade` from an empty database.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 12:57:39 -07:00
Claude Code bf47750682 Merge remote-tracking branch 'origin/master' into remove-legacy-viz-pipeline
The only real conflict was an import-list collision in
Timeseries/transformProps.ts (this branch's DTTM_ALIAS next to master's
DataRecordValue); kept both. Country-map's regenerated GeoJSON files and
notebook edits from master merged cleanly via rename detection onto this
branch's already-renamed plugin-chart-country-map/ package, with no
overlapping edits to reconcile.
2026-07-31 10:47:57 -07:00
Claude Code 990785cfa4 Merge remote-tracking branch 'origin/master' into remove-legacy-viz-pipeline
Resolves a conflict in ExploreChartPanel/index.tsx between master's new
standalone-download-control feature and this branch's earlier removal
of the raw core Alert import in favor of the ExploreAlert component:
kept master's URL_PARAMS/getUrlParam/StandaloneDownloadControl wiring,
dropped the reintroduced Alert import since it's unused on this branch.
2026-07-28 22:16:23 -07:00
Claude Code 7174af24ca Merge remote-tracking branch 'origin/master' into remove-legacy-viz-pipeline
Resolves conflicts between the async-job-cancellation feature added on
master and this branch's removal of the legacy explore_json pipeline:
kept the cancellation feature (_register_cancellable_job, cancel_job,
is_job_cancelled, the task_id/SIGUSR1 revoke wiring, and its tests) and
dropped the parts of it that only applied to submit_explore_json_job /
load_explore_json_into_cache, which this branch already deleted. Also
merged master's get_request_json_body() hardening in views/utils.py
and its abort-signal wiring in chartAction.ts (adapted to this
branch's already-simplified, useLegacyApi-free handleChartDataResponse
signature).
2026-07-28 09:50:12 -07:00
Evan RusackasandClaude Code bbd021925a fix(viz): follow-ups from #41714 self-review (#42530)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-28 09:14:15 -07:00
Claude Code 464674f193 fix(migrations): re-point bubble-chart migration onto master's current alembic head
master added version_transaction_issued_at_index (d3b9a1f6c204) on top of
add_extension_storage_table (e5f6a7b8c9d0), which our bubble-chart migration
was still pointing at as its down_revision -- producing two heads and
failing "superset db upgrade" (and everything downstream: load_examples,
postgres/mysql/sqlite integration tests, cypress, playwright) with
"Multiple head revisions are present". Re-point onto the new head so the
chain is linear again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-27 20:35:30 -07:00
Claude Code c7a83794d2 Merge remote-tracking branch 'origin/master' into remove-legacy-viz-pipeline 2026-07-27 20:22:58 -07:00
Evan RusackasandClaude Code dbc561f80f chore(viz)!: rebuild Nightingale Rose on ECharts, removing nvd3 entirely (#42381)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 20:19:17 -07:00
rusackasandClaude Opus 4.8 86003221bc test: remove test_viz_query_obj.py leftover import of deleted viz module
This test module imported `superset.viz`, which this PR removes, breaking
unit-test collection. The jinja-preservation regression it guarded is
already covered by
core_tests.py::test_split_adhoc_filters_preserves_jinja_templates against
the new (non-viz.py) code path, so the file is redundant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 19:04:14 -07:00
rusackasandClaude Opus 4.8 9615e7c024 fix(viz): drop dead legacy-API dataset-required banner left over from rebase
The explore_json/viz.py removal commit deleted this banner's supporting
state and imports (it only applied to useLegacyApi chart types), but a
rebase conflict against master reintroduced the JSX referencing the
now-undefined vizTypeNeedsDataset/setShowDatasetModal/Alert. Also picks
up a prettier formatting fixup in StatefulChart.tsx.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:41:43 -07:00
rusackasandClaude Opus 4.8 80c8d9f388 fix(plugins): drop stale "Legacy" wording from renamed chart package descriptions
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:24:00 -07:00
Claude Code 59f1d91b07 chore(explore): drop comments pointing at the long-gone nvd3_vis.css
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:23:59 -07:00
Claude Code a4fc9ae493 chore(viz)!: delete the unregistered preset-chart-nvd3 package
Nothing has registered this package since the Bullet and Time-series
Period Pivot charts moved to ECharts; remove the workspace, its
tsconfig reference, the custom-rules grandfather entry, and its
lockfile entries (which also sweeps the nvd3-only fast-safe-stringify
dependency). The nvd3-fork library itself remains a dependency of
plugin-chart-rose, whose legend and tooltip still use it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:23:58 -07:00
Claude Code 5bbe3ac750 fix(rose): use the theme text color for chart and legend text
The nvd3-fork legend and axis labels render SVG text with the default
black fill, which is unreadable against a dark theme background.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:23:58 -07:00
Claude Code 1aeff038d7 test(timeseries): cover the percent-change flag end-to-end from migrated params
Adds transformProps-level coverage for rebase_percent_change: series
rebased from the first point with a forced percent axis format, and the
snake_case key the compare-chart migration stores read through real
ChartProps camelization. Also guards the rebase against form data with
no explicit x-axis by falling back to the temporal alias, matching
extractSeries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:23:57 -07:00
Claude Code b73f6a8bd5 fix(bullet): range labels inside their bands, always-on tooltip, legend-aware grid
Range labels move from mid-bar scatter points onto the markArea bands
(insideTopRight), so each label sits inside the range it names and the
rightmost label no longer clips at the chart edge. The tooltip stays
enabled regardless of the Show labels/legend toggles and the measure bar
tooltip names the range the value falls within. The grid top now
estimates wrapped legend rows from item text widths instead of assuming
a single row.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:23:57 -07:00
Claude Code 1859b375a7 chore(migrations): re-id compare-chart migration and re-point bubble migration after rebase
Master's add_extension_storage_table migration landed with revision id
e5f6a7b8c9d0, colliding with the compare-chart migration's id; re-id it
to 88360afb61ed and chain the bubble migration off master's head.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:23:56 -07:00
Claude Code b85468ee17 chore: drop unused feature_flag_manager import after rebase
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:23:55 -07:00
Claude Code 2261c61791 docs(embedded): document the iframe session cookie race in the pivot spec
Also retriggers CI so the merge ref picks up the master fix from #42274
(an empty commit does not trigger the path-gated workflows).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:23:55 -07:00
Claude Code 9fe31e3ec5 test(embedded): store a query_context on the pivot collapse fixture chart
The spec's fixture chart was created with params only. Real charts saved
through Explore always persist a query_context, and guest (embedded)
chart data requests are validated against the stored chart: with no
stored query_context and no columns/groupby params keys, the pivot
payload's query columns read as tampering and every request 403s.

The suite previously passed only when the embedded iframe happened to
ride the admin session from storageState instead of the guest token (a
cookie race between the /embedded response's anonymous session and the
SDK's csrf/guest-token fetches), which masked the 403 on master.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:23:54 -07:00
Claude Code c7f0167a28 test(charts): update warm-up cache assertions for the actionable message
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:54 -07:00
Claude Code 52b5110a53 style: prettier formatting for UPDATING.md and SavedQueries test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:53 -07:00
Claude Code a33ab097e1 fix(time-table): escape backslashes in flattened labels; actionable null-query-context errors
Escaping only commas let group tuples like (a\\, b) and (a-comma, b)
flatten to identical column keys; backslashes are now escaped first, with
a regression test pinning the collision pair. The cache warm-up error for
charts whose query context has not been generated yet tells the operator
how to resolve it, and UPDATING.md documents the one-time lazy generation
for charts migrated in place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:53 -07:00
Evan RusackasandClaude Code 05ce4d41bf feat(bullet): rebuild the Bullet Chart on ECharts (#42225)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 15:23:53 -07:00
Evan RusackasandClaude Code 8cd636bcd0 feat(time-pivot): rebuild Time-series Period Pivot on ECharts (#42245)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 15:23:53 -07:00
Evan RusackasandClaude Code 18231d9bd4 feat(timeseries): percent-change rebasing with a draggable baseline (#42247)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 15:23:52 -07:00
EvanandClaude Opus 4.8 ca17830175 fix: cast Arc test color accessors through unknown for tsc
lint-frontend's tsc build failed with TS2352 on the direct Accessor ->
ColorAccessor cast; go through unknown first as the compiler suggests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:23:52 -07:00
EvanandClaude Opus 4.8 2be95c6283 fix: repair type errors from removed useLegacyApi metadata field
Three plugin tests still asserted on ChartMetadata.useLegacyApi, which
this PR drops entirely, and the Arc layer test called deck.gl color
accessors without narrowing their Accessor union type. Both broke
lint-frontend's tsc check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:23:51 -07:00
Claude Code 6edeafff6b fix(deck-arc): default missing layer colors instead of crashing
The Arc layer read color_picker/target_color_picker straight off form data
and dereferenced .r/.g/.b, so a saved chart without those controls crashed
the ArcLayer on init ("Cannot read properties of undefined (reading 'r')").
This surfaced in deck.gl Multiple Layers, where sub-slices arrive as raw
saved form data without control-default hydration and the Arc example never
persisted target_color_picker. Fall back to the control-panel default color
(PRIMARY_COLOR) when a picker is absent, matching how Path and Scatter
already guard, and harden Polygon's fill/stroke pickers the same way. Adds
Arc color-accessor tests for the missing and present cases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:51 -07:00
Claude Code b2d2aa4452 feat(deck-multi): surface layer load failures in the chart
When a deck.gl Multiple Layers sub-layer fails to load (e.g. it is bound to
a dataset that lacks the columns it needs), the error previously only threw
to the browser console, leaving the map looking silently empty. Capture the
per-slice failure and render a warning Alert over the map, normalized via
getClientErrorObject so the server message (e.g. "Columns missing in
dataset") is shown. Errors reset on each reload.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:51 -07:00
Claude Code 09a034c573 test(plugins): cover the migrated calendar/chord/country-map registration
Codecov flagged the calendar transformProps and the calendar/chord/
country-map plugin index modules as uncovered. Add plugin-registration
tests (instantiate the plugin, assert v1 metadata and the buildQuery
loader), a calendar transformProps test (v1 reshape plus display-option
passthrough, and the non-array passthrough branch), and a calendar
buildQuery case for an unrecognized subdomain granularity falling back to
minutes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:51 -07:00
Claude Code f2fd536823 fix(deck-multi): query layers against their real datasource, not stale params
Each deck.gl Multiple Layers sub-layer is an independent saved chart with
its own dataset, and its layer query must hit that dataset. fetchSubslices
rebuilt the layer form_data from the saved params, whose `datasource`
string can be stale -- example charts hardcode a datasource id that does
not match the imported dataset's real id. The layer query then went to the
wrong dataset and the server rejected it with 400 "Columns missing in
dataset" (e.g. LON/LAT), so no layer rendered. Standalone the chart works
because it resolves its datasource from the slice relationship, not params.

Use the chart's authoritative datasource_id/datasource_type from the chart
API to build the layer datasource, falling back to params only when absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:51 -07:00
Claude Code 9c6b1f0eb6 fix(deck-multi): correct the points-collection typing to unblock tsc
The GET_POINTS_BY_VIZ_TYPE map annotated its values as
(features: JsonObject[]) => [number, number][], but the per-layer
getPoints helpers have looser, inconsistent signatures (getPointsGeojson
takes Point[]), so the map failed tsc. Replace it with an explicit-spread
collectPoints(features) mirroring the original getAdjustedViewport, which
type-checks and works for both the payload and the per-layer accumulator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:51 -07:00
Claude Code f2055d8446 fix(deck-multi): autozoom to layer data in the v1 fetch path
deck.gl Multiple Layers computed its viewport once, from
props.payload.data.features, and never again. In the v1 path that payload
is empty (each layer is fetched client-side after mount), so autozoom had
no points to fit and the camera stayed at the default location while the
layers rendered off-screen -- the map showed but appeared to have no data.

Accumulate each layer's features as it is fetched and refit the viewport
to the combined points (when autozoom is enabled), mirroring what the
legacy pre-merged payload allowed. Also factor the per-viz_type point
collection into a shared helper. Adds a v1-path test covering both layer
rendering and the viewport refit, which the existing suite (all legacy
payload.data.slices) did not exercise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:50 -07:00
Claude Code fbd088af48 chore(paired-t-test): drop the unused prop-types dependency
Nothing in the plugin imports prop-types since the class components were
migrated to function components. Remove it and its lockfile reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:50 -07:00
Claude Code d7c34215a2 fix(paired-t-test): bound the scroll container to the chart height
The container relied on height: 100%, which does not resolve because the
chart's parent is not height-bounded in the v1 render path, so tall output
(many groups across multiple metrics) still clipped without scrolling.
Thread the explicit height SuperChart passes via chartProps through
transformProps and apply it to the scroll container, matching the pattern
used by the pivot-table chart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:50 -07:00
Claude Code a82128cde0 refactor(paired-t-test): use the antd Table and drop the reactable dependency
Replace the interim plain-table rendering with the design-system antd Table
from @superset-ui/core/components: native column sorting via the ported
comparators, per-cell lift/p-value/significance coloring through semantic
theme tokens (no bespoke CSS classes), and row-click control selection via
onRow. Removes the reactable dependency entirely (its fragile, React-18-
incompatible rendering was the production bug) and the leftover reactable-*
styling. Also drops the now-stale distributions/cephes lockfile entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:50 -07:00
Claude Code 9ff15cb53e fix(paired-t-test): scroll the chart when tables overflow its height
The container styled the non-existent .scrollbar-container class, so the
chart never scrolled and tall output (many groups across multiple metrics)
was clipped with no way to reach the rest. Make the chart root fill its
allotted height and scroll on overflow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:49 -07:00
Claude Code ac7f53326f fix(paired-t-test): render the table without the reactable dependency
The migrated chart returned correct data (verified end-to-end) yet showed
table headers with no body rows in production builds, while every jest
path rendered rows. The common factor was reactable, an unmaintained 2016
library that relies on React-18-incompatible legacy lifecycles
(componentWillMount/componentWillReceiveProps) and identifies its row
children by stringifying components -- fragile in a production bundle.

Replace it with a plain semantic table that reproduces every behavior
(control-row selection, per-cell lift/p-value/significance coloring, and
the same column sort comparators), reusing the existing CSS class hooks so
the surrounding styles are unchanged. Rendering is now deterministic and
fully unit-testable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:49 -07:00
EvanandClaude Opus 4.8 feb224686b fix(tests): drop removed useLegacyApi prop from ChartRenderer test metadata
The legacy-pipeline removal deleted useLegacyApi from ChartMetadataConfig;
the enableNoResults test still passed it, breaking tsc in lint-frontend.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:23:49 -07:00
Claude Code f93cc1a67b test(paired-t-test): prove the v1 pipeline renders table rows end-to-end
Guards the "headers render but no body rows" failure mode by driving the
real transformProps -> PairedTTest pipeline with a realistic flat
/api/v1/chart/data timeseries response (main.birth_names shape): asserts
transformProps keys the reshaped data by metric label with one series per
group tuple, and that the rendered tables contain a body row per group,
including when optional precision/significance controls are absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:49 -07:00
Claude Code d2911672bf fix(explore): honor chart enableNoResults metadata in ChartRenderer
ChartRenderer computed the SuperChart enableNoResults prop purely from
server-pagination + AG Grid filter state (bypassNoResult) and never read
the chart plugin's own enableNoResults metadata. Charts that fetch their
own data and issue no top-level query, like deck.gl Multiple Layers, set
enableNoResults: false but still hit the "No results were returned for
this query" empty state in explore, which pre-empts their self-fetching
renderer from ever mounting.

AND the chart's enableNoResults metadata (defaulting to true) into
bypassNoResult so deck_multi and similar self-fetching charts render.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:49 -07:00
Evan RusackasandClaude Opus 4.8 f9dfea8791 test(plugins): regression guards from the migration bug sweep
Adds low-risk import-and-assert tests across migrated plugins, guarding the
classes of bugs found while testing this branch:
- is_timeseries charts must expose a datetime control (calendar, horizon, rose,
  partition). Partition genuinely lacked one, so add sections.legacyTimeseriesTime
  to its control panel (same 500 as paired-t-test otherwise).
- deck.gl Multiple Layers must issue an empty query (self-fetches its layers).
- deck.gl Arc is timeseries only when time_grain_sqla is set.
- Time Pivot's numeric x-axis format must render a number, not the literal
  SMART_NUMBER string.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:48 -07:00
Evan RusackasandClaude Opus 4.8 a5aa619668 test(paired-t-test): mock the statistics module instead of removed distributions
Removing the distributions dependency left TTestTable.test.tsx mocking a module
the component no longer imports, so the p-value/significance tests failed. Mock
./statistics.studentTwoSidedPValue to a deterministic 0.02 instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:48 -07:00
Evan RusackasandClaude Opus 4.8 dfc745c922 fix(nvd3,controls): time-pivot x-axis number format + single-line async multiselect
- Time Pivot's x-axis is a numeric offset within the period (its x_axis_format
  defaults to SMART_NUMBER), but NVD3Vis ran it through getTimeFormatter, which
  made d3 print the literal string "SMART_NUMBER" on every tick. Format it as a
  number instead. (Pre-existing bug, bycatch.)
- SelectAsyncControl never forwarded the Select 'oneLine' prop, so many selected
  tags wrapped outside the control's fixed-height box (visible on deck.gl
  Multiple Layers). Pass oneLine for multi mode so tags collapse to a '+N' tag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:48 -07:00
Evan RusackasandClaude Opus 4.8 936ec951d5 fix(paired-t-test): compute p-values without the Node-only distributions dep
The chart imported 'distributions' to compute the Student's t p-value, but that
package references Node's Buffer global, which is only polyfilled in dev builds
-- so the chart rendered in dev and threw "ReferenceError: Buffer is not
defined" in production. Replace it with a self-contained, browser-safe
two-sided p-value via the regularized incomplete beta function, verified
against standard t-table critical values, and drop the dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:48 -07:00
Evan RusackasandClaude Opus 4.8 acc93dfe33 fix(deck-multi): render the map instead of the no-results empty state
deck.gl Multiple Layers issues no query of its own -- each layer is a saved
chart that fetches its data client-side, so buildQuery is intentionally empty.
Dropping useLegacyApi exposed the default enableNoResults=true, so the empty
query response now shows "No results were returned for this query" and the map
never renders. Set enableNoResults: false, matching the other self-fetching
charts (Select filter, DeckglLayerVisibility).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:47 -07:00
Evan RusackasandClaude Opus 4.8 a973290890 fix(paired-t-test): restore the datetime column control
The paired t-test chart's buildQuery sends is_timeseries, so the backend
requires a datetime column, but its migrated control panel has no time
section. Creating the chart fails with "Datetime column not provided as part
table configuration and is required by this type of chart" with no way to
supply one. Add the shared legacyTimeseriesTime section, matching its sibling
timeseries charts (rose, horizon), so a granularity column can be selected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:47 -07:00
EvanandClaude Opus 4.8 377e8e6345 fix: rebase bubble-chart migration onto master's current alembic head
master added strip_metricsqlexpressions_from_ag_grid_params (d24e6b0a9c7f)
on top of shadow_live_row_indexes (8f3a1b2c4d5e) after this branch's
bubble-chart migration was last pointed at 8f3a1b2c4d5e, producing two
alembic heads and failing `db upgrade` across every integration/E2E job.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:23:47 -07:00
EvanandClaude Opus 4.8 71443460d1 fix: resolve oxlint unused-var/param errors and remove orphaned legacy test
Drops the dead `method` param from getChartDataRequest (the legacy
GET/POST branching it supported no longer exists now that chart data
always POSTs to /api/v1/chart/data), removes bubble-only NVD3Vis props
(entity/maxBubbleSize/xField/yField/sizeField) and the unused module-level
`formatter` left over from the bubble->bubble_v2 migration, and deletes
plugins/legacy-preset-chart-nvd3/test/TimePivot/controlPanel.test.ts, an
orphaned test for a src/ directory that no longer exists after the
rename to preset-chart-nvd3 (superseded by
plugins/preset-chart-nvd3/test/TimePivot.test.ts).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:23:47 -07:00
EvanandClaude Opus 4.8 dcda71e869 fix: point bubble-chart migration at master's actual alembic head
down_revision on d4e5f6a7b8c9 pointed at b1c2d3e4f5a6
(add_subjects_tables), which already has a child on master
(56cd24c07170 -> 8f3a1b2c4d5e). That left two alembic heads and
broke `superset db upgrade` in every DB-backed CI job. Chain the
migration after 8f3a1b2c4d5e, master's real current head, instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:23:46 -07:00
EvanandClaude Opus 4.8 d59120a632 fix: resolve alembic multiple-heads conflict in migration chain
down_revision on d4e5f6a7b8c9 still pointed at 3a8e6f2c1b95, which
master's add_subjects_tables migration (b1c2d3e4f5a6) also forked
from, leaving two alembic heads and breaking `superset db upgrade`
in every DB-backed CI job.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:23:46 -07:00
EvanandClaude Opus 4.8 ba795ccfa0 fix: format Multi.tsx and update Multi.color.test.tsx for registry-based layer loading
Multi.tsx now resolves sublayer buildQuery/transformProps via the chart
plugin registries instead of the removed legacy explore.ts helper; update
the color test's mocks to register deck_scatter/deck_arc stubs and mock
SupersetClient.post (matching the new fetch call) instead of .get.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:23:45 -07:00
EvanandClaude Opus 4.8 6a5049daed fix: repair stale package-lock.json and alembic migration branch
- Remove dangling package-lock.json link entries for the removed
  legacy-* plugin workspaces and regenerate entries for their
  renamed plugin-chart-* counterparts; npm ci was failing with
  EUSAGE because the lockfile referenced workspace paths that no
  longer exist on disk.
- Re-parent the bubble-chart-to-echarts migration
  (d4e5f6a7b8c9) onto the current single migration head
  (3a8e6f2c1b95) instead of a now-superseded ancestor, resolving
  the "Multiple head revisions" alembic error that was failing
  test-sqlite/test-postgres/test-mysql/cypress/playwright.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:23:45 -07:00
EvanandClaude Opus 4.8 83e7098ee8 fix: restore OAuth2RedirectError import after rebase
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 15:23:44 -07:00
Claude Code b71ea77080 docs: add UPDATING.md entries for the legacy viz pipeline removal and drop the working trackers
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:23:43 -07:00
Evan RusackasandClaude Code b3435a04cf chore(viz)!: remove explore_json endpoints, viz.py and the legacy chart data pipeline (#41750)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 15:23:41 -07:00
Evan RusackasandClaude Code 1771f05d33 chore(viz): drop the legacy- prefix from migrated chart plugin packages (#41751)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 15:05:59 -07:00
Claude Code 3dfe193ca0 chore(viz): update SCOPE tracker — all charts migrated, phases 2-3 in review
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 14:54:57 -07:00
Evan RusackasandClaude Code c54abcfda8 feat(compare): migrate compare charts to echarts_timeseries_line and drop the legacy plugin (#41738)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:57 -07:00
Evan RusackasandClaude Code b2849d826a feat(deck-multi): migrate deck_multi chart to the v1 chart data API (#41730)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:57 -07:00
Evan RusackasandClaude Code 2cd9873540 feat(bubble): migrate saved bubble charts to bubble_v2 and drop the legacy plugin (#41728)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:56 -07:00
Evan RusackasandClaude Code 8948b5af94 fix(legacy-viz): restore line-engine default ordering for horizon and rose (#41732)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:55 -07:00
Evan RusackasandClaude Code 3bcb003eeb feat(partition): migrate partition chart to v1 chart data API (#41729)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:54 -07:00
Claude Code 480fbe0e06 chore(viz): update SCOPE tracker after tier 2 completion
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 14:54:54 -07:00
Evan RusackasandClaude Code 3abbe1c84a feat(time-pivot): migrate time_pivot chart to v1 chart data API (#41727)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:53 -07:00
Evan RusackasandClaude Code 311b0087f3 feat(rose): migrate rose chart to v1 chart data API (#41726)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:53 -07:00
Evan RusackasandClaude Code 0fea3c88a5 feat(horizon): migrate horizon chart to v1 chart data API (#41725)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:53 -07:00
Claude Code e61610bd98 chore(viz): update SCOPE tracker after tier 1-2 merges
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 14:54:52 -07:00
Evan RusackasandClaude Code 9729306c06 feat(time-table): migrate time_table chart to v1 chart data API (#41723)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:51 -07:00
Evan RusackasandClaude Code 93a02449db feat(world-map): migrate world_map chart to v1 chart data API (#41720)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:51 -07:00
Evan RusackasandClaude Code cbbcf1bed2 feat(calendar): migrate cal_heatmap chart to v1 chart data API (#41724)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:51 -07:00
Evan RusackasandClaude Code 218c64296f feat(paired-t-test): migrate paired_ttest chart to v1 chart data API (#41721)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:50 -07:00
Evan RusackasandClaude Code 39eced2c2e feat(chord): migrate chord chart to v1 chart data API (#41719)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:50 -07:00
Claude Code bef4680ffd chore(viz): update SCOPE tracker after phase 0 + tier 1 merges
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 14:54:50 -07:00
Evan RusackasandClaude Code 5870259219 feat(bullet): migrate bullet chart to v1 chart data API (#41718)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:50 -07:00
Evan RusackasandClaude Code a4bcd2cc87 feat(parallel-coordinates): migrate para chart to v1 chart data API (#41716)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:50 -07:00
Evan RusackasandClaude Code f8dd65cc0b feat(country-map): migrate country_map chart to v1 chart data API (#41717)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:50 -07:00
Evan RusackasandClaude Code b1bc8fa8f2 chore(viz): phase 0 — remove orphaned viz.py classes and nvd3 BoxPlot leftovers (#41715)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-27 14:54:49 -07:00
Claude Code 5d86215a9d chore(viz): add SCOPE/UPDATES trackers for legacy viz pipeline removal
Working documents for the remove-legacy-viz-pipeline feature branch;
stripped before final merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 14:54:49 -07:00
Claude Code f866f25546 Merge branch 'remove-legacy-viz-pipeline' into misc-charts-examples
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 02:59:41 -07:00
Claude Code 3392576923 chore(explore): drop comments pointing at the long-gone nvd3_vis.css
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 02:59:10 -07:00
Claude Code e95b37450e chore(viz)!: delete the unregistered preset-chart-nvd3 package
Nothing has registered this package since the Bullet and Time-series
Period Pivot charts moved to ECharts; remove the workspace, its
tsconfig reference, the custom-rules grandfather entry, and its
lockfile entries (which also sweeps the nvd3-only fast-safe-stringify
dependency). The nvd3-fork library itself remains a dependency of
plugin-chart-rose, whose legend and tooltip still use it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 02:57:52 -07:00
Claude Code ff73f2c070 Merge branch 'remove-legacy-viz-pipeline' into misc-charts-examples
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 00:21:13 -07:00
Claude Code 612fd0ac5d Merge branch 'master' into remove-legacy-viz-pipeline
Master's #41996 relocated legacy filter sanitization out of viz.py and
utils/core.py into the legacy explore path this branch removes; the
remaining utils signature change merges cleanly, viz.py stays deleted,
and the new test file importing superset.viz is removed with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 00:20:12 -07:00
Claude Code 83dbdfa355 Merge remote-tracking branch 'origin/misc-charts-examples'
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 22:38:53 -07:00
Claude Code fc3872fe9e Merge branch 'remove-legacy-viz-pipeline' into misc-charts-examples
Brings in the rose dark-theme text fix and the master sync.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 22:38:04 -07:00
Claude Code 0946df50a1 chore(viz): recapture all gallery thumbnails on dashboard-context backgrounds
Every thumbnail regenerated from the tuned example charts on the
white/dark card background users actually see, including the restored
generic handlebars logo and a predictive-forecast generic time-series
thumbnail.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 22:37:48 -07:00
Claude Code d9c3a2cf4c feat(examples): purpose-built thumbnail charts for the viz gallery
Nineteen additional Misc Charts examples modeling each chart type at
its most recognizable: simpler chord, YoY big number, triangular
funnel, multi-range gauge, compact heatmap grid, bell-curve histogram,
dual-axis bar+line mixed chart, labeled donut, video-game-sales radar,
sunburst with total, stacked area and bars, low-series marker lines
(straight, smooth, stepped), high-cardinality treemap, quarterly
waterfall, multi-metric table and a cell-highlighted pivot. Also tunes
existing examples: monthly-grain period pivot, sales-based time table,
tighter parallel coordinates, recentered point cluster map, and
deck.gl hexagon/scatter viewports pinned (autozoom off) for the
downtown 3D view.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 22:37:46 -07:00
Claude Code f84d7c664e feat(playwright): dashboard-context backgrounds, hover captures, curated slice picks
Captures now paint the dashboard card background (white or dark) over
explore's gray layout background, hover a populated cell before the
still for charts whose identity benefits from a visible tooltip
(calendar heatmap), and prefer the purpose-built thumbnail examples for
every tuned viz type. Handlebars is intentionally unmapped so its
generic logo survives.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 22:37:38 -07:00
Claude Code 45fd4117a9 fix(rose): use the theme text color for chart and legend text
The nvd3-fork legend and axis labels render SVG text with the default
black fill, which is unreadable against a dark theme background.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 22:36:32 -07:00
Evan Rusackas 79acc3a712 Merge branch 'remove-legacy-viz-pipeline' into misc-charts-examples 2026-07-23 21:09:22 -07:00
Claude Code 62d0cfb6d3 Merge branch 'master' into remove-legacy-viz-pipeline
Resolves StatefulChart/chartAction conflicts by keeping master's async
(202) handling and staleness guards with the legacy explore_json paths
stripped: the endpoint is always /api/v1/chart/data, the async handler
loses its useLegacyApi parameter, and the legacy body-wrapping branch
and its test are removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 19:45:23 -07:00
Evan Rusackas b885657ad0 Merge branch 'master' into remove-legacy-viz-pipeline 2026-07-23 16:40:39 -07:00
Claude Code a191f72698 fix(playwright): treat AG Grid and period-over-period KPI as markup-only captures
Both render without svg/canvas/table, so the crawler needs the looser
rendered signal when capturing them from a flag-enabled environment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:35:04 -07:00
Claude Code 498400e974 chore(viz): delete dead thumbnailLarge images and capture art for newly exemplified types
Removes 30 thumbnailLarge.png files no plugin imports, and captures
live thumbnails (light and dark) for the generic/smooth/step time
series, handlebars, deck.gl contour and heatmap, and point cluster map
from their new example charts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:35:03 -07:00
Claude Code b9a8321802 feat(examples): example charts for every remaining unexemplified viz type
Adds Misc Charts examples for the generic, smooth and stepped
time-series lines, handlebars, deck.gl contour and heatmap layers, and
the point cluster map (on the keyless maplibre renderer). Flag-gated
types (pop_kpi behind ChartPluginsExperimental, ag-grid-table behind
AgGridTableEnabled) are deliberately excluded: their plugins are
unregistered on default deployments, where their charts would render as
errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:34:49 -07:00
Claude Code 902ebcdf72 chore(viz): refresh viz-picker gallery art from live example charts
Regenerates thumbnails (light and dark) for every viz type on the
example dashboards via the thumbnail crawler, replaces the obsolete
nvd3-era Bullet/TimePivot/TimeTable example gallery screenshots, and
adds a percent-change example (Line3) to the Line chart gallery.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:06:25 -07:00
Claude Code f76471f30f fix(playwright): harden the thumbnail crawler against hangs and text-only charts
Adds an explicit navigation timeout so one hung load cannot stall the
crawl until the test timeout, raises the overall budget to 90 minutes,
and gives markup-only charts (big_number_total, handlebars) a looser
rendered signal since they produce no svg/canvas/table for the default
selector to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:06:12 -07:00
Claude Code b671356748 fix(chart): guard the percent-change baseline against an unapplied chart option
On warm explore navigations getOption() can be undefined or empty when
the baseline effect first runs, crashing the chart render with 'Cannot
read properties of undefined (reading series)'. Guard the read and retry
installation once the chart finishes rendering.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:05:32 -07:00
Claude Code ff273478ff docs(playwright): update thumbnail crawler header for dark-variant capture
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:29:30 -07:00
Claude Code f1cc9367de feat(playwright): capture dark variants and obsolete gallery examples in the thumbnail crawler
Every capture now also renders a dark version via prefers-color-scheme
emulation when the plugin ships a -dark sibling (or the image is new),
skipping the write when the app ignores the emulation so real dark art
is never clobbered with light captures. The Bullet, TimePivot and
TimeTable example.jpg gallery images — still screenshots of the removed
nvd3 renderers — are refreshed as wide extra captures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:28:55 -07:00
Claude Code 02e4b4be7d feat(playwright): turn the thumbnail capture tool into a dashboard crawler
Instead of enumerating charts, the tool discovers every chart on every
dashboard via the API and captures one representative per viz type, so
it keeps working as example dashboards evolve. The only static piece is
the viz-type-to-image-path map (one line per plugin); unmapped viz types
are reported without failing. Supports VIZ_TYPES=a,b filtering, a
preferred-slice override per type, and named extra captures for gallery
images like the Line percent-change example.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:19:48 -07:00
Claude Code ecb4aa93f6 feat(playwright): add a viz-picker thumbnail capture tool
A CAPTURE_THUMBNAILS-gated Playwright spec that renders each migrated
legacy chart's example standalone at 512x512 and overwrites the plugin's
gallery thumbnail, plus an npm run playwright:thumbnails entry point.
Covers the Misc Charts examples and the existing chord, horizon,
parallel coordinates, country map and world map examples; the Line
percent-change capture lands as a new gallery image to be registered in
the plugin metadata after capture.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 22:53:57 -07:00
Claude Code 4e1913d436 feat(examples): add migrated legacy chart examples to the Misc Charts dashboard
Adds example charts for every migrated legacy visualization that had no
example instance: calendar heatmap, nightingale rose, partition, paired
t-test, bullet, time-series period pivot, a percent-change line, and a
time table. Country map, parallel coordinates, chord, horizon and world
map already have examples in other dashboards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 22:51:56 -07:00
Claude Code b6ae8fb25d test(timeseries): cover the percent-change flag end-to-end from migrated params
Adds transformProps-level coverage for rebase_percent_change: series
rebased from the first point with a forced percent axis format, and the
snake_case key the compare-chart migration stores read through real
ChartProps camelization. Also guards the rebase against form data with
no explicit x-axis by falling back to the temporal alias, matching
extractSeries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 19:11:42 -07:00
Claude Code 10f2461b5f fix(bullet): range labels inside their bands, always-on tooltip, legend-aware grid
Range labels move from mid-bar scatter points onto the markArea bands
(insideTopRight), so each label sits inside the range it names and the
rightmost label no longer clips at the chart edge. The tooltip stays
enabled regardless of the Show labels/legend toggles and the measure bar
tooltip names the range the value falls within. The grid top now
estimates wrapped legend rows from item text widths instead of assuming
a single row.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 18:56:12 -07:00
Claude Code 4e715f66dc chore(migrations): re-id compare-chart migration and re-point bubble migration after rebase
Master's add_extension_storage_table migration landed with revision id
e5f6a7b8c9d0, colliding with the compare-chart migration's id; re-id it
to 88360afb61ed and chain the bubble migration off master's head.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:52:46 -07:00
Claude Code 6dfae09cc5 chore: drop unused feature_flag_manager import after rebase
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:50:45 -07:00
Claude Code a4f74b09bb docs(embedded): document the iframe session cookie race in the pivot spec
Also retriggers CI so the merge ref picks up the master fix from #42274
(an empty commit does not trigger the path-gated workflows).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:50:44 -07:00
Claude Code 9f39fe3e10 test(embedded): store a query_context on the pivot collapse fixture chart
The spec's fixture chart was created with params only. Real charts saved
through Explore always persist a query_context, and guest (embedded)
chart data requests are validated against the stored chart: with no
stored query_context and no columns/groupby params keys, the pivot
payload's query columns read as tampering and every request 403s.

The suite previously passed only when the embedded iframe happened to
ride the admin session from storageState instead of the guest token (a
cookie race between the /embedded response's anonymous session and the
SDK's csrf/guest-token fetches), which masked the 403 on master.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:50:44 -07:00
Claude Code c6f8706aa1 test(charts): update warm-up cache assertions for the actionable message
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:44 -07:00
Claude Code 07a6406d97 style: prettier formatting for UPDATING.md and SavedQueries test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:44 -07:00
Claude Code a585a0901e fix(time-table): escape backslashes in flattened labels; actionable null-query-context errors
Escaping only commas let group tuples like (a\\, b) and (a-comma, b)
flatten to identical column keys; backslashes are now escaped first, with
a regression test pinning the collision pair. The cache warm-up error for
charts whose query context has not been generated yet tells the operator
how to resolve it, and UPDATING.md documents the one-time lazy generation
for charts migrated in place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:44 -07:00
Evan RusackasandClaude Code fff5351a33 feat(bullet): rebuild the Bullet Chart on ECharts (#42225)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:44 -07:00
Evan RusackasandClaude Code b4f8d87a49 feat(time-pivot): rebuild Time-series Period Pivot on ECharts (#42245)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:44 -07:00
Evan RusackasandClaude Code 3256ee4fbb feat(timeseries): percent-change rebasing with a draggable baseline (#42247)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:44 -07:00
EvanandClaude Opus 4.8 e5b3ba9efc fix: cast Arc test color accessors through unknown for tsc
lint-frontend's tsc build failed with TS2352 on the direct Accessor ->
ColorAccessor cast; go through unknown first as the compiler suggests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:50:43 -07:00
EvanandClaude Opus 4.8 ae93dc2041 fix: repair type errors from removed useLegacyApi metadata field
Three plugin tests still asserted on ChartMetadata.useLegacyApi, which
this PR drops entirely, and the Arc layer test called deck.gl color
accessors without narrowing their Accessor union type. Both broke
lint-frontend's tsc check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:50:43 -07:00
Claude Code 792d0fd2b6 fix(deck-arc): default missing layer colors instead of crashing
The Arc layer read color_picker/target_color_picker straight off form data
and dereferenced .r/.g/.b, so a saved chart without those controls crashed
the ArcLayer on init ("Cannot read properties of undefined (reading 'r')").
This surfaced in deck.gl Multiple Layers, where sub-slices arrive as raw
saved form data without control-default hydration and the Arc example never
persisted target_color_picker. Fall back to the control-panel default color
(PRIMARY_COLOR) when a picker is absent, matching how Path and Scatter
already guard, and harden Polygon's fill/stroke pickers the same way. Adds
Arc color-accessor tests for the missing and present cases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:43 -07:00
Claude Code 8dcb83fd3c feat(deck-multi): surface layer load failures in the chart
When a deck.gl Multiple Layers sub-layer fails to load (e.g. it is bound to
a dataset that lacks the columns it needs), the error previously only threw
to the browser console, leaving the map looking silently empty. Capture the
per-slice failure and render a warning Alert over the map, normalized via
getClientErrorObject so the server message (e.g. "Columns missing in
dataset") is shown. Errors reset on each reload.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:43 -07:00
Claude Code 8f07ab4840 test(plugins): cover the migrated calendar/chord/country-map registration
Codecov flagged the calendar transformProps and the calendar/chord/
country-map plugin index modules as uncovered. Add plugin-registration
tests (instantiate the plugin, assert v1 metadata and the buildQuery
loader), a calendar transformProps test (v1 reshape plus display-option
passthrough, and the non-array passthrough branch), and a calendar
buildQuery case for an unrecognized subdomain granularity falling back to
minutes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:43 -07:00
Claude Code 89d64b89b8 fix(deck-multi): query layers against their real datasource, not stale params
Each deck.gl Multiple Layers sub-layer is an independent saved chart with
its own dataset, and its layer query must hit that dataset. fetchSubslices
rebuilt the layer form_data from the saved params, whose `datasource`
string can be stale -- example charts hardcode a datasource id that does
not match the imported dataset's real id. The layer query then went to the
wrong dataset and the server rejected it with 400 "Columns missing in
dataset" (e.g. LON/LAT), so no layer rendered. Standalone the chart works
because it resolves its datasource from the slice relationship, not params.

Use the chart's authoritative datasource_id/datasource_type from the chart
API to build the layer datasource, falling back to params only when absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:43 -07:00
Claude Code 4ce589dcda fix(deck-multi): correct the points-collection typing to unblock tsc
The GET_POINTS_BY_VIZ_TYPE map annotated its values as
(features: JsonObject[]) => [number, number][], but the per-layer
getPoints helpers have looser, inconsistent signatures (getPointsGeojson
takes Point[]), so the map failed tsc. Replace it with an explicit-spread
collectPoints(features) mirroring the original getAdjustedViewport, which
type-checks and works for both the payload and the per-layer accumulator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:43 -07:00
Claude Code a31210df08 fix(deck-multi): autozoom to layer data in the v1 fetch path
deck.gl Multiple Layers computed its viewport once, from
props.payload.data.features, and never again. In the v1 path that payload
is empty (each layer is fetched client-side after mount), so autozoom had
no points to fit and the camera stayed at the default location while the
layers rendered off-screen -- the map showed but appeared to have no data.

Accumulate each layer's features as it is fetched and refit the viewport
to the combined points (when autozoom is enabled), mirroring what the
legacy pre-merged payload allowed. Also factor the per-viz_type point
collection into a shared helper. Adds a v1-path test covering both layer
rendering and the viewport refit, which the existing suite (all legacy
payload.data.slices) did not exercise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:43 -07:00
Claude Code 00e95dcb87 chore(paired-t-test): drop the unused prop-types dependency
Nothing in the plugin imports prop-types since the class components were
migrated to function components. Remove it and its lockfile reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:42 -07:00
Claude Code c9960002f1 fix(paired-t-test): bound the scroll container to the chart height
The container relied on height: 100%, which does not resolve because the
chart's parent is not height-bounded in the v1 render path, so tall output
(many groups across multiple metrics) still clipped without scrolling.
Thread the explicit height SuperChart passes via chartProps through
transformProps and apply it to the scroll container, matching the pattern
used by the pivot-table chart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:42 -07:00
Claude Code dbac244857 refactor(paired-t-test): use the antd Table and drop the reactable dependency
Replace the interim plain-table rendering with the design-system antd Table
from @superset-ui/core/components: native column sorting via the ported
comparators, per-cell lift/p-value/significance coloring through semantic
theme tokens (no bespoke CSS classes), and row-click control selection via
onRow. Removes the reactable dependency entirely (its fragile, React-18-
incompatible rendering was the production bug) and the leftover reactable-*
styling. Also drops the now-stale distributions/cephes lockfile entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:42 -07:00
Claude Code 4483cb09f0 fix(paired-t-test): scroll the chart when tables overflow its height
The container styled the non-existent .scrollbar-container class, so the
chart never scrolled and tall output (many groups across multiple metrics)
was clipped with no way to reach the rest. Make the chart root fill its
allotted height and scroll on overflow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:42 -07:00
Claude Code 28d977bc0c fix(paired-t-test): render the table without the reactable dependency
The migrated chart returned correct data (verified end-to-end) yet showed
table headers with no body rows in production builds, while every jest
path rendered rows. The common factor was reactable, an unmaintained 2016
library that relies on React-18-incompatible legacy lifecycles
(componentWillMount/componentWillReceiveProps) and identifies its row
children by stringifying components -- fragile in a production bundle.

Replace it with a plain semantic table that reproduces every behavior
(control-row selection, per-cell lift/p-value/significance coloring, and
the same column sort comparators), reusing the existing CSS class hooks so
the surrounding styles are unchanged. Rendering is now deterministic and
fully unit-testable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:42 -07:00
EvanandClaude Opus 4.8 b248c4d7c0 fix(tests): drop removed useLegacyApi prop from ChartRenderer test metadata
The legacy-pipeline removal deleted useLegacyApi from ChartMetadataConfig;
the enableNoResults test still passed it, breaking tsc in lint-frontend.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:50:42 -07:00
Claude Code 3e9fdc4cdf test(paired-t-test): prove the v1 pipeline renders table rows end-to-end
Guards the "headers render but no body rows" failure mode by driving the
real transformProps -> PairedTTest pipeline with a realistic flat
/api/v1/chart/data timeseries response (main.birth_names shape): asserts
transformProps keys the reshaped data by metric label with one series per
group tuple, and that the rendered tables contain a body row per group,
including when optional precision/significance controls are absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:42 -07:00
Claude Code 387b742303 fix(explore): honor chart enableNoResults metadata in ChartRenderer
ChartRenderer computed the SuperChart enableNoResults prop purely from
server-pagination + AG Grid filter state (bypassNoResult) and never read
the chart plugin's own enableNoResults metadata. Charts that fetch their
own data and issue no top-level query, like deck.gl Multiple Layers, set
enableNoResults: false but still hit the "No results were returned for
this query" empty state in explore, which pre-empts their self-fetching
renderer from ever mounting.

AND the chart's enableNoResults metadata (defaulting to true) into
bypassNoResult so deck_multi and similar self-fetching charts render.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:41 -07:00
Evan RusackasandClaude Opus 4.8 533f90e4c6 test(plugins): regression guards from the migration bug sweep
Adds low-risk import-and-assert tests across migrated plugins, guarding the
classes of bugs found while testing this branch:
- is_timeseries charts must expose a datetime control (calendar, horizon, rose,
  partition). Partition genuinely lacked one, so add sections.legacyTimeseriesTime
  to its control panel (same 500 as paired-t-test otherwise).
- deck.gl Multiple Layers must issue an empty query (self-fetches its layers).
- deck.gl Arc is timeseries only when time_grain_sqla is set.
- Time Pivot's numeric x-axis format must render a number, not the literal
  SMART_NUMBER string.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:41 -07:00
Evan RusackasandClaude Opus 4.8 b887c30573 test(paired-t-test): mock the statistics module instead of removed distributions
Removing the distributions dependency left TTestTable.test.tsx mocking a module
the component no longer imports, so the p-value/significance tests failed. Mock
./statistics.studentTwoSidedPValue to a deterministic 0.02 instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:41 -07:00
Evan RusackasandClaude Opus 4.8 2cd369d02a fix(nvd3,controls): time-pivot x-axis number format + single-line async multiselect
- Time Pivot's x-axis is a numeric offset within the period (its x_axis_format
  defaults to SMART_NUMBER), but NVD3Vis ran it through getTimeFormatter, which
  made d3 print the literal string "SMART_NUMBER" on every tick. Format it as a
  number instead. (Pre-existing bug, bycatch.)
- SelectAsyncControl never forwarded the Select 'oneLine' prop, so many selected
  tags wrapped outside the control's fixed-height box (visible on deck.gl
  Multiple Layers). Pass oneLine for multi mode so tags collapse to a '+N' tag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:41 -07:00
Evan RusackasandClaude Opus 4.8 4b3bbe19f6 fix(paired-t-test): compute p-values without the Node-only distributions dep
The chart imported 'distributions' to compute the Student's t p-value, but that
package references Node's Buffer global, which is only polyfilled in dev builds
-- so the chart rendered in dev and threw "ReferenceError: Buffer is not
defined" in production. Replace it with a self-contained, browser-safe
two-sided p-value via the regularized incomplete beta function, verified
against standard t-table critical values, and drop the dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:41 -07:00
Evan RusackasandClaude Opus 4.8 28eae5956e fix(deck-multi): render the map instead of the no-results empty state
deck.gl Multiple Layers issues no query of its own -- each layer is a saved
chart that fetches its data client-side, so buildQuery is intentionally empty.
Dropping useLegacyApi exposed the default enableNoResults=true, so the empty
query response now shows "No results were returned for this query" and the map
never renders. Set enableNoResults: false, matching the other self-fetching
charts (Select filter, DeckglLayerVisibility).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:41 -07:00
Evan RusackasandClaude Opus 4.8 239e3a4323 fix(paired-t-test): restore the datetime column control
The paired t-test chart's buildQuery sends is_timeseries, so the backend
requires a datetime column, but its migrated control panel has no time
section. Creating the chart fails with "Datetime column not provided as part
table configuration and is required by this type of chart" with no way to
supply one. Add the shared legacyTimeseriesTime section, matching its sibling
timeseries charts (rose, horizon), so a granularity column can be selected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:50:41 -07:00
EvanandClaude Opus 4.8 573054346f fix: rebase bubble-chart migration onto master's current alembic head
master added strip_metricsqlexpressions_from_ag_grid_params (d24e6b0a9c7f)
on top of shadow_live_row_indexes (8f3a1b2c4d5e) after this branch's
bubble-chart migration was last pointed at 8f3a1b2c4d5e, producing two
alembic heads and failing `db upgrade` across every integration/E2E job.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:50:41 -07:00
EvanandClaude Opus 4.8 a42c7e3be0 fix: resolve oxlint unused-var/param errors and remove orphaned legacy test
Drops the dead `method` param from getChartDataRequest (the legacy
GET/POST branching it supported no longer exists now that chart data
always POSTs to /api/v1/chart/data), removes bubble-only NVD3Vis props
(entity/maxBubbleSize/xField/yField/sizeField) and the unused module-level
`formatter` left over from the bubble->bubble_v2 migration, and deletes
plugins/legacy-preset-chart-nvd3/test/TimePivot/controlPanel.test.ts, an
orphaned test for a src/ directory that no longer exists after the
rename to preset-chart-nvd3 (superseded by
plugins/preset-chart-nvd3/test/TimePivot.test.ts).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:50:40 -07:00
EvanandClaude Opus 4.8 935b4acdbd fix: point bubble-chart migration at master's actual alembic head
down_revision on d4e5f6a7b8c9 pointed at b1c2d3e4f5a6
(add_subjects_tables), which already has a child on master
(56cd24c07170 -> 8f3a1b2c4d5e). That left two alembic heads and
broke `superset db upgrade` in every DB-backed CI job. Chain the
migration after 8f3a1b2c4d5e, master's real current head, instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:50:40 -07:00
EvanandClaude Opus 4.8 bba2d5f812 fix: resolve alembic multiple-heads conflict in migration chain
down_revision on d4e5f6a7b8c9 still pointed at 3a8e6f2c1b95, which
master's add_subjects_tables migration (b1c2d3e4f5a6) also forked
from, leaving two alembic heads and breaking `superset db upgrade`
in every DB-backed CI job.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:50:40 -07:00
EvanandClaude Opus 4.8 7d45b8ae7e fix: format Multi.tsx and update Multi.color.test.tsx for registry-based layer loading
Multi.tsx now resolves sublayer buildQuery/transformProps via the chart
plugin registries instead of the removed legacy explore.ts helper; update
the color test's mocks to register deck_scatter/deck_arc stubs and mock
SupersetClient.post (matching the new fetch call) instead of .get.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:50:40 -07:00
EvanandClaude Opus 4.8 5942d570c3 fix: repair stale package-lock.json and alembic migration branch
- Remove dangling package-lock.json link entries for the removed
  legacy-* plugin workspaces and regenerate entries for their
  renamed plugin-chart-* counterparts; npm ci was failing with
  EUSAGE because the lockfile referenced workspace paths that no
  longer exist on disk.
- Re-parent the bubble-chart-to-echarts migration
  (d4e5f6a7b8c9) onto the current single migration head
  (3a8e6f2c1b95) instead of a now-superseded ancestor, resolving
  the "Multiple head revisions" alembic error that was failing
  test-sqlite/test-postgres/test-mysql/cypress/playwright.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:50:40 -07:00
EvanandClaude Opus 4.8 45adb8429d fix: restore OAuth2RedirectError import after rebase
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:50:39 -07:00
Claude Code 3a03f0a820 docs: add UPDATING.md entries for the legacy viz pipeline removal and drop the working trackers
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:50:39 -07:00
Evan RusackasandClaude Code 105f6d0bc7 chore(viz)!: remove explore_json endpoints, viz.py and the legacy chart data pipeline (#41750)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:39 -07:00
Evan RusackasandClaude Code c8d6faf7e8 chore(viz): drop the legacy- prefix from migrated chart plugin packages (#41751)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:35 -07:00
Claude Code ae15dd883d chore(viz): update SCOPE tracker — all charts migrated, phases 2-3 in review
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:50:34 -07:00
Evan RusackasandClaude Code 2afa775db5 feat(compare): migrate compare charts to echarts_timeseries_line and drop the legacy plugin (#41738)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:34 -07:00
Evan RusackasandClaude Code e17d49bec0 feat(deck-multi): migrate deck_multi chart to the v1 chart data API (#41730)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:34 -07:00
Evan RusackasandClaude Code 68324050cd feat(bubble): migrate saved bubble charts to bubble_v2 and drop the legacy plugin (#41728)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:34 -07:00
Evan RusackasandClaude Code 2a55ad77f4 fix(legacy-viz): restore line-engine default ordering for horizon and rose (#41732)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:34 -07:00
Evan RusackasandClaude Code cd535f89e2 feat(partition): migrate partition chart to v1 chart data API (#41729)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:34 -07:00
Claude Code 8b2c07f5f3 chore(viz): update SCOPE tracker after tier 2 completion
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:50:34 -07:00
Evan RusackasandClaude Code a9045296d9 feat(time-pivot): migrate time_pivot chart to v1 chart data API (#41727)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:34 -07:00
Evan RusackasandClaude Code 32cbc4a7a6 feat(rose): migrate rose chart to v1 chart data API (#41726)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:33 -07:00
Evan RusackasandClaude Code 4f47eda5de feat(horizon): migrate horizon chart to v1 chart data API (#41725)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:33 -07:00
Claude Code ba2b0d6ea3 chore(viz): update SCOPE tracker after tier 1-2 merges
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:50:33 -07:00
Evan RusackasandClaude Code df176cfa57 feat(time-table): migrate time_table chart to v1 chart data API (#41723)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:33 -07:00
Evan RusackasandClaude Code 66e911eeea feat(world-map): migrate world_map chart to v1 chart data API (#41720)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:33 -07:00
Evan RusackasandClaude Code 759457264d feat(calendar): migrate cal_heatmap chart to v1 chart data API (#41724)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:33 -07:00
Evan RusackasandClaude Code 5722b96280 feat(paired-t-test): migrate paired_ttest chart to v1 chart data API (#41721)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:33 -07:00
Evan RusackasandClaude Code deb6cacea7 feat(chord): migrate chord chart to v1 chart data API (#41719)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:33 -07:00
Claude Code 0f8e2a274d chore(viz): update SCOPE tracker after phase 0 + tier 1 merges
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:50:33 -07:00
Evan RusackasandClaude Code 7e0de356d4 feat(bullet): migrate bullet chart to v1 chart data API (#41718)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:33 -07:00
Evan RusackasandClaude Code 4e93b58a61 feat(parallel-coordinates): migrate para chart to v1 chart data API (#41716)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:32 -07:00
Evan RusackasandClaude Code 45b232cba0 feat(country-map): migrate country_map chart to v1 chart data API (#41717)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:32 -07:00
Evan RusackasandClaude Code bcc58d27f8 chore(viz): phase 0 — remove orphaned viz.py classes and nvd3 BoxPlot leftovers (#41715)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 17:50:32 -07:00
Claude Code b5df673d21 chore(viz): add SCOPE/UPDATES trackers for legacy viz pipeline removal
Working documents for the remove-legacy-viz-pipeline feature branch;
stripped before final merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:50:32 -07:00
626 changed files with 3923 additions and 1644 deletions
+3 -3
View File
@@ -42,7 +42,7 @@ dependencies = [
# ``google-auth`` 2.53+ dropped it, so Superset must declare it
# explicitly to keep fresh ``pip install apache-superset`` working
# without the ``base.txt`` lock file (#40962).
"cachetools>=7.1.7, <8",
"cachetools>=7.1.6, <8",
"celery>=5.6.3, <6.0.0",
"click>=8.4.2",
"click-option-group",
@@ -75,7 +75,7 @@ dependencies = [
"humanize",
"isodate",
"jsonpath-ng>=1.8.0, <2",
"Mako>=1.4.1",
"Mako>=1.2.2",
"markdown>=3.10.3",
# marshmallow 4 compatibility: see superset/marshmallow_compatibility.py for a
# Flask-AppBuilder workaround. Tracking issue:
@@ -221,7 +221,7 @@ ocient = [
# unpinned sqlalchemy>=1.4 declared, but SQLAlchemy 2.0 support is
# unverified. Lower confidence than the other bumps in this PR.
"sqlalchemy-ocient>=3.0.0, <4",
"pyocient>=3.9.0, <4",
"pyocient>=1.0.15, <4",
"shapely",
"geojson",
]
+1 -1
View File
@@ -28,7 +28,7 @@ numexpr>=2.9.0
# Security: CVE-2026-34073 (MEDIUM) - Improper Certificate Validation
cryptography>=50.0.0,<51.0.0
# Security: Snyk - XSS vulnerability in Mako templates
mako>=1.4.1,<2.0.0
mako>=1.3.11,<2.0.0
# Security: CVE-2024-52338 (CRITICAL) - Deserialization of untrusted data in IPC/Parquet readers
pyarrow>=24.0.0,<26.0.0
# Security: CVE-2026-27459 - pyopenssl certificate validation
+2 -2
View File
@@ -44,7 +44,7 @@ cachelib==0.13.0
# via
# flask-caching
# flask-session
cachetools==7.1.7
cachetools==7.1.6
# via apache-superset (pyproject.toml)
cattrs==25.1.1
# via requests-cache
@@ -207,7 +207,7 @@ kombu==5.6.2
# via celery
limits==5.1.0
# via flask-limiter
mako==1.4.1
mako==1.3.12
# via
# -r requirements/base.in
# apache-superset (pyproject.toml)
+2 -2
View File
@@ -99,7 +99,7 @@ cachelib==0.13.0
# -c requirements/base-constraint.txt
# flask-caching
# flask-session
cachetools==7.1.7
cachetools==7.1.6
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -510,7 +510,7 @@ limits==5.1.0
# flask-limiter
lz4==4.4.5
# via trino
mako==1.4.1
mako==1.3.12
# via
# -c requirements/base-constraint.txt
# alembic
-506
View File
@@ -19,7 +19,6 @@
"@babel/preset-typescript": "7.29.7",
"@types/node": "^25.4.0",
"babel-loader": "^9.1.3",
"jsdom": "^26.1.0",
"tscw-config": "^1.1.2",
"typescript": "^5.9.3",
"vitest": "^4.0.18",
@@ -27,27 +26,6 @@
"webpack-cli": "^5.1.4"
}
},
"node_modules/@asamuzakjp/css-color": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
"integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@csstools/css-calc": "^2.1.3",
"@csstools/css-color-parser": "^3.0.9",
"@csstools/css-parser-algorithms": "^3.0.4",
"@csstools/css-tokenizer": "^3.0.3",
"lru-cache": "^10.4.3"
}
},
"node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/@babel/cli": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.29.7.tgz",
@@ -1678,121 +1656,6 @@
"node": ">=6.9.0"
}
},
"node_modules/@csstools/color-helpers": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
"integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT-0",
"engines": {
"node": ">=18"
}
},
"node_modules/@csstools/css-calc": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
"integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@csstools/css-parser-algorithms": "^3.0.5",
"@csstools/css-tokenizer": "^3.0.4"
}
},
"node_modules/@csstools/css-color-parser": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
"integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"dependencies": {
"@csstools/color-helpers": "^5.1.0",
"@csstools/css-calc": "^2.1.4"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@csstools/css-parser-algorithms": "^3.0.5",
"@csstools/css-tokenizer": "^3.0.4"
}
},
"node_modules/@csstools/css-parser-algorithms": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
"integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@csstools/css-tokenizer": "^3.0.4"
}
},
"node_modules/@csstools/css-tokenizer": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
"integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
@@ -2638,16 +2501,6 @@
"acorn": "^8.14.0"
}
},
"node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/ajv": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
@@ -3015,34 +2868,6 @@
"node": ">= 8"
}
},
"node_modules/cssstyle": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
"integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@asamuzakjp/css-color": "^3.2.0",
"rrweb-cssom": "^0.8.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/data-urls": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
"integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
"dev": true,
"license": "MIT",
"dependencies": {
"whatwg-mimetype": "^4.0.0",
"whatwg-url": "^14.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -3061,13 +2886,6 @@
}
}
},
"node_modules/decimal.js": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
"dev": true,
"license": "MIT"
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
@@ -3097,19 +2915,6 @@
"node": ">=10.13.0"
}
},
"node_modules/entities": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/envinfo": {
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz",
@@ -3511,60 +3316,6 @@
"node": ">= 0.4"
}
},
"node_modules/html-encoding-sniffer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
"integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"whatwg-encoding": "^3.1.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/import-local": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
@@ -3687,13 +3438,6 @@
"node": ">=0.10.0"
}
},
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
"dev": true,
"license": "MIT"
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -3754,46 +3498,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/jsdom": {
"version": "26.1.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
"integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
"dev": true,
"license": "MIT",
"dependencies": {
"cssstyle": "^4.2.1",
"data-urls": "^5.0.0",
"decimal.js": "^10.5.0",
"html-encoding-sniffer": "^4.0.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"is-potential-custom-element-name": "^1.0.1",
"nwsapi": "^2.2.16",
"parse5": "^7.2.1",
"rrweb-cssom": "^0.8.0",
"saxes": "^6.0.0",
"symbol-tree": "^3.2.4",
"tough-cookie": "^5.1.1",
"w3c-xmlserializer": "^5.0.0",
"webidl-conversions": "^7.0.0",
"whatwg-encoding": "^3.1.1",
"whatwg-mimetype": "^4.0.0",
"whatwg-url": "^14.1.1",
"ws": "^8.18.0",
"xml-name-validator": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"canvas": "^3.0.0"
},
"peerDependenciesMeta": {
"canvas": {
"optional": true
}
}
},
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -4273,13 +3977,6 @@
"node": ">=0.10.0"
}
},
"node_modules/nwsapi": {
"version": "2.2.24",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.24.tgz",
"integrity": "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==",
"dev": true,
"license": "MIT"
},
"node_modules/obug": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
@@ -4336,19 +4033,6 @@
"node": ">=6"
}
},
"node_modules/parse5": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"entities": "^6.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4458,16 +4142,6 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -4640,33 +4314,6 @@
"@rolldown/binding-win32-x64-msvc": "1.1.3"
}
},
"node_modules/rrweb-cssom": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
"integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
"dev": true,
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"license": "MIT"
},
"node_modules/saxes": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
"integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
"dev": true,
"license": "ISC",
"dependencies": {
"xmlchars": "^2.2.0"
},
"engines": {
"node": ">=v12.22.7"
}
},
"node_modules/schema-utils": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
@@ -4800,13 +4447,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
"dev": true,
"license": "MIT"
},
"node_modules/tapable": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
@@ -4952,26 +4592,6 @@
"node": ">=14.0.0"
}
},
"node_modules/tldts": {
"version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
"integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"tldts-core": "^6.1.86"
},
"bin": {
"tldts": "bin/cli.js"
}
},
"node_modules/tldts-core": {
"version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
"integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
"dev": true,
"license": "MIT"
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -4985,32 +4605,6 @@
"node": ">=8.0"
}
},
"node_modules/tough-cookie": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
"integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"tldts": "^6.1.32"
},
"engines": {
"node": ">=16"
}
},
"node_modules/tr46": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
"dev": true,
"license": "MIT",
"dependencies": {
"punycode": "^2.3.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/tscw-config": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/tscw-config/-/tscw-config-1.1.2.tgz",
@@ -5337,19 +4931,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
"dev": true,
"license": "MIT",
"dependencies": {
"xml-name-validator": "^5.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/watchpack": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
@@ -5363,16 +4944,6 @@
"node": ">=10.13.0"
}
},
"node_modules/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
}
},
"node_modules/webpack": {
"version": "5.105.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz",
@@ -5499,44 +5070,6 @@
"node": ">=10.13.0"
}
},
"node_modules/whatwg-encoding": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
"deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
"dev": true,
"license": "MIT",
"dependencies": {
"iconv-lite": "0.6.3"
},
"engines": {
"node": ">=18"
}
},
"node_modules/whatwg-mimetype": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/whatwg-url": {
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"tr46": "^5.1.0",
"webidl-conversions": "^7.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -5581,45 +5114,6 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"node_modules/ws": {
"version": "8.21.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz",
"integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xml-name-validator": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18"
}
},
"node_modules/xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true,
"license": "MIT"
},
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-1
View File
@@ -43,7 +43,6 @@
"@babel/preset-typescript": "7.29.7",
"@types/node": "^25.4.0",
"babel-loader": "^9.1.3",
"jsdom": "^26.1.0",
"tscw-config": "^1.1.2",
"typescript": "^5.9.3",
"vitest": "^4.0.18",
-213
View File
@@ -1,213 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// @vitest-environment jsdom
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { Switchboard } from "@superset-ui/switchboard";
import { embedDashboard } from "./index";
vi.mock("@superset-ui/switchboard");
function makeFakeJWT(claims: any) {
// not a valid jwt, but close enough for this code
const tokenifiedClaims = Buffer.from(JSON.stringify(claims)).toString(
"base64",
);
return `abc.${tokenifiedClaims}.xyz`;
}
describe("embedDashboard", () => {
let mountPoint: HTMLElement;
let mockSwitchboard: Switchboard;
beforeEach(() => {
mountPoint = document.createElement("div");
document.body.appendChild(mountPoint);
mockSwitchboard = {
emit: vi.fn(),
get: vi.fn(),
start: vi.fn(),
defineMethod: vi.fn(),
} as any;
// Constructor mocks must use `function`, since arrow functions cannot be
// invoked with `new`.
vi.mocked(Switchboard).mockImplementation(function () {
return mockSwitchboard;
} as any);
// Mock MessageChannel API
globalThis.MessageChannel = vi.fn(function (this: any) {
this.port1 = {};
this.port2 = {};
}) as any;
// Mock iframe load event and sandbox
const originalCreateElement = document.createElement.bind(document);
vi.spyOn(document, "createElement").mockImplementation(tagName => {
const element = originalCreateElement(tagName);
if (tagName === "iframe") {
// Mock sandbox DOMTokenList
(element as any).sandbox = {
add: vi.fn(),
};
// Mock contentWindow for postMessage
Object.defineProperty(element, "contentWindow", {
writable: true,
value: {
postMessage: vi.fn(),
},
});
setTimeout(() => {
element.dispatchEvent(new Event("load"));
}, 0);
}
return element;
});
});
afterEach(() => {
document.body.removeChild(mountPoint);
vi.restoreAllMocks();
});
test("setDataMask sends dataMask to iframe", async () => {
const fakeToken = makeFakeJWT({ exp: Date.now() / 1000 + 300 });
const mockFetchGuestToken = vi.fn().mockResolvedValue(fakeToken);
const testDataMask = {
"NATIVE_FILTER-1": {
filterState: {
value: ["value1", "value2"],
},
},
};
const dashboard = await embedDashboard({
id: "test-id",
supersetDomain: "https://superset.example.com",
mountPoint,
fetchGuestToken: mockFetchGuestToken,
});
dashboard.setDataMask(testDataMask);
expect(mockSwitchboard.get).toHaveBeenCalledWith("setDataMask", {
dataMask: testDataMask,
});
});
test("setDataMask sends empty dataMask", async () => {
const fakeToken = makeFakeJWT({ exp: Date.now() / 1000 + 300 });
const mockFetchGuestToken = vi.fn().mockResolvedValue(fakeToken);
const emptyDataMask = {};
const dashboard = await embedDashboard({
id: "test-id",
supersetDomain: "https://superset.example.com",
mountPoint,
fetchGuestToken: mockFetchGuestToken,
});
dashboard.setDataMask(emptyDataMask);
expect(mockSwitchboard.get).toHaveBeenCalledWith("setDataMask", {
dataMask: emptyDataMask,
});
});
test("setDataMask drops the change-trigger flags observeDataMask adds", async () => {
const fakeToken = makeFakeJWT({ exp: Date.now() / 1000 + 300 });
const mockFetchGuestToken = vi.fn().mockResolvedValue(fakeToken);
const observedMask = {
"NATIVE_FILTER-1": {
filterState: {
value: ["CA"],
},
},
crossFiltersChanged: false,
nativeFiltersChanged: true,
};
const dashboard = await embedDashboard({
id: "test-id",
supersetDomain: "https://superset.example.com",
mountPoint,
fetchGuestToken: mockFetchGuestToken,
});
dashboard.setDataMask(observedMask);
expect(mockSwitchboard.get).toHaveBeenCalledWith("setDataMask", {
dataMask: {
"NATIVE_FILTER-1": observedMask["NATIVE_FILTER-1"],
},
});
});
test("setDataMask sends complex dataMask with multiple filters", async () => {
const fakeToken = makeFakeJWT({ exp: Date.now() / 1000 + 300 });
const mockFetchGuestToken = vi.fn().mockResolvedValue(fakeToken);
const complexDataMask = {
"NATIVE_FILTER-1": {
filterState: {
value: ["CA", "NY"],
},
},
"NATIVE_FILTER-2": {
filterState: {
value: [2023, 2024],
},
},
};
const dashboard = await embedDashboard({
id: "test-id",
supersetDomain: "https://superset.example.com",
mountPoint,
fetchGuestToken: mockFetchGuestToken,
});
dashboard.setDataMask(complexDataMask);
expect(mockSwitchboard.get).toHaveBeenCalledWith("setDataMask", {
dataMask: complexDataMask,
});
});
test("setDataMask rejects when the embedded page does not support it", async () => {
const fakeToken = makeFakeJWT({ exp: Date.now() / 1000 + 300 });
const mockFetchGuestToken = vi.fn().mockResolvedValue(fakeToken);
vi.mocked(mockSwitchboard.get).mockRejectedValue(
new Error('Method "setDataMask" is not defined'),
);
const dashboard = await embedDashboard({
id: "test-id",
supersetDomain: "https://superset.example.com",
mountPoint,
fetchGuestToken: mockFetchGuestToken,
});
await expect(dashboard.setDataMask({})).rejects.toThrow(
'Method "setDataMask" is not defined',
);
});
});
-22
View File
@@ -115,12 +115,6 @@ export type EmbeddedDashboard = {
getActiveTabs: () => Promise<string[]>;
observeDataMask: (callbackFn: ObserveDataMaskCallbackFn) => void;
getDataMask: () => Promise<Record<string, any>>;
/**
* Applies a data mask to the dashboard.
* Rejects if the embedded Superset page does not support `setDataMask`,
* so a version mismatch surfaces instead of silently doing nothing.
*/
setDataMask: (dataMask: Record<string, any>) => Promise<void>;
getChartStates: () => Promise<Record<string, any>>;
getChartDataPayloads: (params?: {
chartId?: number;
@@ -361,21 +355,6 @@ export async function embedDashboard({
ourPort.get<string>("getDashboardPermalink", { anchor });
const getActiveTabs = () => ourPort.get<string[]>("getActiveTabs");
const getDataMask = () => ourPort.get<Record<string, any>>("getDataMask");
// `observeDataMask` hands the host a mask with the change-trigger booleans
// mixed in, so feeding that payload straight back into `setDataMask` is a
// natural thing for a host to do. Keep only the entries that look like a
// filter's mask, so those flags never reach the dashboard as filter ids.
// Sent with `get` rather than `emit` so the iframe acknowledges the call:
// an embedded page that predates `setDataMask` replies with an error instead
// of dropping the message silently.
const setDataMask = (dataMask: Record<string, any>) =>
ourPort.get<void>("setDataMask", {
dataMask: Object.fromEntries(
Object.entries(dataMask).filter(
([, mask]) => typeof mask === "object" && mask !== null,
),
),
});
const getChartStates = () =>
ourPort.get<Record<string, any>>("getChartStates");
const getChartDataPayloads = (params?: { chartId?: number }) =>
@@ -417,7 +396,6 @@ export async function embedDashboard({
getActiveTabs,
observeDataMask,
getDataMask,
setDataMask,
getChartStates,
getChartDataPayloads,
setThemeConfig,
+4 -4
View File
@@ -33,7 +33,7 @@
"@fontsource/fira-code": "^5.3.0",
"@fontsource/ibm-plex-mono": "^5.3.0",
"@fontsource/inter": "^5.3.0",
"@googleapis/sheets": "^14.0.0",
"@googleapis/sheets": "^13.0.2",
"@great-expectations/jsonforms-antd-renderers": "^2.2.10",
"@jsonforms/core": "^3.7.0",
"@jsonforms/react": "^3.7.0",
@@ -4142,9 +4142,9 @@
}
},
"node_modules/@googleapis/sheets": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/@googleapis/sheets/-/sheets-14.0.0.tgz",
"integrity": "sha512-fANEl4RQohsPYUWhcLSYyUyE8A8bRfvw/bp8h0t8VDQqTgdQ3itZBkty4nddtdqCAvNDpA+KM66OejVKDd6aFg==",
"version": "13.0.2",
"resolved": "https://registry.npmjs.org/@googleapis/sheets/-/sheets-13.0.2.tgz",
"integrity": "sha512-b1tBlMcfvNEziM4DZCikLOc9iqSlgCK1e5bMKtNQIADRXr1CQmbkHV3ZBVvTsFsjLErgihqO58Itn/kzCnSZ0A==",
"license": "Apache-2.0",
"dependencies": {
"googleapis-common": "^8.0.0"
+2 -1
View File
@@ -78,6 +78,7 @@
"playwright:debug": "playwright test --debug",
"playwright:report": "playwright show-report",
"docs:screenshots": "playwright test --config=playwright/generators/playwright.config.ts docs/",
"playwright:thumbnails": "CAPTURE_THUMBNAILS=1 playwright test tests/tools/capture-viz-thumbnails.spec.ts --project chromium",
"prod": "npm run build",
"prune": "rm -rf ./{packages,plugins}/*/{node_modules,lib,esm,tsconfig.tsbuildinfo,package-lock.json} ./.temp_cache",
"storybook": "cross-env NODE_ENV=development BABEL_ENV=development storybook dev -p 6006",
@@ -110,7 +111,7 @@
"@fontsource/fira-code": "^5.3.0",
"@fontsource/ibm-plex-mono": "^5.3.0",
"@fontsource/inter": "^5.3.0",
"@googleapis/sheets": "^14.0.0",
"@googleapis/sheets": "^13.0.2",
"@great-expectations/jsonforms-antd-renderers": "^2.2.10",
"@jsonforms/core": "^3.7.0",
"@jsonforms/react": "^3.7.0",
-4
View File
@@ -47,10 +47,6 @@ export default defineConfig({
// Retry logic - 2 retries in CI, 0 locally
retries: process.env.CI ? 2 : 0,
// Disable capturing Git commit info as the project's history is increasingly dense
// and breach Playwright's default 3-seconds `git` command timeout limit
captureGitInfo: { commit: false, diff: false },
// Reporter configuration - multiple reporters for better visibility
reporter: process.env.CI
? [
@@ -0,0 +1,630 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Crawls every dashboard on the target instance and refreshes viz-picker
* thumbnails and example galleries from live example charts.
*
* This is a maintenance tool, not a test: charts are DISCOVERED via the
* API (dashboards -> charts), so it keeps working as example dashboards
* evolve. Every distinct chart found for a viz type gets captured — the
* preferred (or alphabetically-first) chart becomes the 512x512
* `thumbnail.png`, and each additional distinct chart fills the next
* `exampleGallery` slot declared for that viz type, so the gallery shows
* real variety instead of the same chart resized. The two static pieces
* are the viz type -> image path maps below (VIZ_TYPE_THUMBNAILS,
* VIZ_TYPE_GALLERY), which change when a plugin's images or gallery
* shape change — never when examples change. Viz types found on
* dashboards but missing from both maps, and gallery slots left unfilled
* for lack of enough distinct example charts, are reported at the end
* without failing the run.
*
* It only runs when CAPTURE_THUMBNAILS=1 is set, so the regular
* Playwright suites never execute it.
*
* Usage (requires a running Superset with examples loaded):
* npm run playwright:thumbnails
* VIZ_TYPES=bullet,rose npm run playwright:thumbnails # subset
*
* Notes:
* - Dark variants (`thumbnail-dark.png`, `example-dark.jpg`) are captured
* via prefers-color-scheme emulation whenever the sibling file exists;
* if the app ignores the emulation (dark theming disabled) the dark
* file is left untouched.
* - A brand-new gallery image (rather than an existing file being
* refreshed) still needs to be registered in the plugin's metadata
* before it renders in the gallery; the capture logs a reminder.
*/
import * as fs from 'fs';
import * as path from 'path';
import { test, expect, Page } from '@playwright/test';
const THUMBNAIL_SIZE = 512;
const RENDERED_CHART_SELECTOR =
'[data-test="chart-container"]:has(svg, canvas, table):not(:has([data-test="loading-indicator"]))';
/**
* Charts that render plain markup — no svg/canvas/table for the rendered
* selector to key on — get a looser signal plus a longer settle.
*/
const TEXT_ONLY_VIZ_TYPES = new Set([
'ag-grid-table',
'big_number_total',
'handlebars',
'pop_kpi',
]);
/**
* Hover this element just before the screenshot, so charts whose identity
* benefits from an interaction (a visible tooltip) capture mid-hover.
*/
const HOVER_BEFORE_CAPTURE: Record<string, string> = {
cal_heatmap: '[data-test="chart-container"] svg rect[class*=" r"]',
};
const TEXT_RENDERED_CHART_SELECTOR =
'[data-test="chart-container"]:not(:has([data-test="loading-indicator"]))';
/** superset-frontend root, resolved from this spec's location. */
const FRONTEND_ROOT = path.resolve(__dirname, '..', '..', '..');
const ECHARTS = 'plugins/plugin-chart-echarts/src';
const DECKGL = 'plugins/preset-chart-deckgl/src/layers';
/**
* Where each viz type's gallery thumbnail lives, relative to
* superset-frontend. One entry per registered viz type with a thumbnail;
* add a line when a new plugin ships.
*/
const VIZ_TYPE_THUMBNAILS: Record<string, string> = {
'ag-grid-table':
'plugins/plugin-chart-ag-grid-table/src/images/thumbnail.png',
big_number: `${ECHARTS}/BigNumber/BigNumberWithTrendline/images/thumbnail.png`,
big_number_total: `${ECHARTS}/BigNumber/BigNumberTotal/images/thumbnail.png`,
box_plot: `${ECHARTS}/BoxPlot/images/thumbnail.png`,
bubble_v2: `${ECHARTS}/Bubble/images/thumbnail.png`,
bullet: `${ECHARTS}/Bullet/images/thumbnail.png`,
cal_heatmap: 'plugins/plugin-chart-calendar/src/images/thumbnail.png',
cartodiagram: 'plugins/plugin-chart-cartodiagram/src/images/thumbnail.png',
chord: 'plugins/plugin-chart-chord/src/images/thumbnail.png',
country_map: 'plugins/plugin-chart-country-map/src/images/thumbnail.png',
deck_arc: `${DECKGL}/Arc/images/thumbnail.png`,
deck_contour: `${DECKGL}/Contour/images/thumbnail.png`,
deck_geojson: `${DECKGL}/Geojson/images/thumbnail.png`,
deck_grid: `${DECKGL}/Grid/images/thumbnail.png`,
deck_heatmap: `${DECKGL}/Heatmap/images/thumbnail.png`,
deck_hex: `${DECKGL}/Hex/images/thumbnail.png`,
deck_multi: 'plugins/preset-chart-deckgl/src/Multi/images/thumbnail.png',
deck_path: `${DECKGL}/Path/images/thumbnail.png`,
deck_polygon: `${DECKGL}/Polygon/images/thumbnail.png`,
deck_scatter: `${DECKGL}/Scatter/images/thumbnail.png`,
deck_screengrid: `${DECKGL}/Screengrid/images/thumbnail.png`,
echarts_area: `${ECHARTS}/Timeseries/Area/images/thumbnail.png`,
echarts_timeseries: `${ECHARTS}/Timeseries/images/thumbnail.png`,
echarts_timeseries_bar: `${ECHARTS}/Timeseries/Regular/Bar/images/thumbnail.png`,
echarts_timeseries_line: `${ECHARTS}/Timeseries/Regular/Line/images/thumbnail.png`,
echarts_timeseries_scatter: `${ECHARTS}/Timeseries/Regular/Scatter/images/thumbnail.png`,
echarts_timeseries_smooth: `${ECHARTS}/Timeseries/Regular/SmoothLine/images/thumbnail.png`,
echarts_timeseries_step: `${ECHARTS}/Timeseries/Step/images/thumbnail.png`,
funnel: `${ECHARTS}/Funnel/images/thumbnail.png`,
gantt_chart: `${ECHARTS}/Gantt/images/thumbnail.png`,
gauge_chart: `${ECHARTS}/Gauge/images/thumbnail.png`,
graph_chart: `${ECHARTS}/Graph/images/thumbnail.png`,
// handlebars intentionally unmapped: its generic logo represents the
// template-anything nature of the chart better than any one example.
heatmap_v2: `${ECHARTS}/Heatmap/images/thumbnail.png`,
histogram_v2: `${ECHARTS}/Histogram/images/thumbnail.png`,
horizon: 'plugins/plugin-chart-horizon/src/images/thumbnail.png',
mixed_timeseries: `${ECHARTS}/MixedTimeseries/images/thumbnail.png`,
paired_ttest: 'plugins/plugin-chart-paired-t-test/src/images/thumbnail.png',
para: 'plugins/plugin-chart-parallel-coordinates/src/images/thumbnail.png',
partition: 'plugins/plugin-chart-partition/src/images/thumbnail.png',
pie: `${ECHARTS}/Pie/images/thumbnail.png`,
pivot_table_v2: 'plugins/plugin-chart-pivot-table/src/images/thumbnail.png',
point_cluster_map:
'plugins/plugin-chart-point-cluster-map/src/images/thumbnail.png',
pop_kpi: `${ECHARTS}/BigNumber/BigNumberPeriodOverPeriod/images/thumbnail.png`,
radar: `${ECHARTS}/Radar/images/thumbnail.png`,
rose: `${ECHARTS}/Rose/images/thumbnail.png`,
sankey_v2: `${ECHARTS}/Sankey/images/thumbnail.png`,
sunburst_v2: `${ECHARTS}/Sunburst/images/thumbnail.png`,
table: 'plugins/plugin-chart-table/src/images/thumbnail.png',
time_pivot: `${ECHARTS}/TimePivot/images/thumbnail.png`,
time_table: 'src/visualizations/TimeTable/images/thumbnail.png',
tree_chart: `${ECHARTS}/Tree/images/thumbnail.png`,
treemap_v2: `${ECHARTS}/Treemap/images/thumbnail.png`,
waterfall: `${ECHARTS}/Waterfall/images/thumbnail.png`,
word_cloud: 'plugins/plugin-chart-word-cloud/src/images/thumbnail.png',
world_map: 'plugins/plugin-chart-world-map/src/images/thumbnail.png',
};
/**
* When several example charts share a viz type, prefer these slices over
* the default alphabetically-first pick. Missing slices fall back to the
* default, so stale entries degrade gracefully.
*/
const PREFERRED_SLICES: Record<string, string> = {
big_number: 'Sales Year over Year',
bubble_v2: 'Life Expectancy VS Rural %',
bullet: 'Total Sales Bullet',
cal_heatmap: 'Sales Calendar Heatmap',
chord: 'Product Line Chord',
echarts_area: 'Sales Stacked Area',
echarts_timeseries_bar: 'Sales Stacked Bars',
echarts_timeseries_line: 'Monthly Sales Line',
echarts_timeseries_smooth: 'Monthly Sales Smooth',
echarts_timeseries_step: 'Quarterly Sales Steps',
funnel: 'Population Funnel',
gauge_chart: 'Rural Population Gauge',
heatmap_v2: 'Sales Grid Heatmap',
histogram_v2: 'Life Expectancy Histogram',
horizon: 'Population Growth Horizon',
mixed_timeseries: 'Sales Mixed Chart',
paired_ttest: 'Population Paired t-Test',
partition: 'Population Partition',
pie: 'Product Line Donut',
pivot_table_v2: 'Sales Pivot Highlights',
radar: 'Game Sales Radar',
rose: 'Population Nightingale Rose',
sunburst_v2: 'Population Sunburst',
table: 'Sales Summary Table',
time_pivot: 'Sales Period Pivot',
time_table: 'Product Line Time Table',
tree_chart: 'Sales Territory Tree',
treemap_v2: 'Population Treemap',
waterfall: 'Quarterly Sales Waterfall',
};
/** Gallery example images use a wide aspect, matching the existing art. */
const EXAMPLE_WIDTH = 800;
const EXAMPLE_HEIGHT = 460;
/**
* Every plugin's `exampleGallery` image slots (never including
* `thumbnail.png`/`thumbnail-dark.png`, which stays governed by
* VIZ_TYPE_THUMBNAILS), in the order they appear in that plugin's
* `index.ts`. One entry per viz type that declares a gallery — including
* `handlebars`, whose gallery is filled even though its picker thumbnail
* intentionally stays the generic logo (unmapped in VIZ_TYPE_THUMBNAILS).
*
* For a viz type with N slots here, the crawler assigns its (N+1)
* distinct dashboard charts (the thumbnail's pick, then this many more)
* to thumbnail, slot 1, slot 2, ... in that order, so gallery images show
* different charts than the thumbnail and each other rather than the same
* chart resized. Fewer distinct charts than slots just leaves the
* trailing slots untouched (reported, not failed) — add more example
* charts to fill them.
*
* Update this when a plugin's exampleGallery array changes shape.
*/
const VIZ_TYPE_GALLERY: Record<string, string[]> = {
'ag-grid-table': [
'plugins/plugin-chart-ag-grid-table/src/images/Table.jpg',
'plugins/plugin-chart-ag-grid-table/src/images/Table2.jpg',
'plugins/plugin-chart-ag-grid-table/src/images/Table3.jpg',
],
big_number: [
`${ECHARTS}/BigNumber/BigNumberWithTrendline/images/Big_Number_Trendline.jpg`,
],
big_number_total: [
`${ECHARTS}/BigNumber/BigNumberTotal/images/BigNumber.jpg`,
`${ECHARTS}/BigNumber/BigNumberTotal/images/BigNumber2.jpg`,
],
box_plot: [`${ECHARTS}/BoxPlot/images/BoxPlot.jpg`],
bubble_v2: [
`${ECHARTS}/Bubble/images/example1.png`,
`${ECHARTS}/Bubble/images/example2.png`,
],
bullet: [`${ECHARTS}/Bullet/images/example.jpg`],
cal_heatmap: ['plugins/plugin-chart-calendar/src/images/example.jpg'],
cartodiagram: [
'plugins/plugin-chart-cartodiagram/src/images/example1.png',
'plugins/plugin-chart-cartodiagram/src/images/example2.png',
],
chord: ['plugins/plugin-chart-chord/src/images/chord.jpg'],
country_map: [
'plugins/plugin-chart-country-map/src/images/exampleUsa.jpg',
'plugins/plugin-chart-country-map/src/images/exampleGermany.jpg',
],
echarts_area: [`${ECHARTS}/Timeseries/Area/images/Area1.png`],
echarts_timeseries: [`${ECHARTS}/Timeseries/images/Time-series_Chart.jpg`],
echarts_timeseries_bar: [
`${ECHARTS}/Timeseries/Regular/Bar/images/Bar1.png`,
`${ECHARTS}/Timeseries/Regular/Bar/images/Bar2.png`,
`${ECHARTS}/Timeseries/Regular/Bar/images/Bar3.png`,
],
echarts_timeseries_line: [
`${ECHARTS}/Timeseries/Regular/Line/images/Line1.png`,
`${ECHARTS}/Timeseries/Regular/Line/images/Line2.png`,
`${ECHARTS}/Timeseries/Regular/Line/images/Line3.png`,
],
echarts_timeseries_scatter: [
`${ECHARTS}/Timeseries/Regular/Scatter/images/Scatter1.png`,
],
echarts_timeseries_smooth: [
`${ECHARTS}/Timeseries/Regular/SmoothLine/images/SmoothLine1.png`,
],
echarts_timeseries_step: [
`${ECHARTS}/Timeseries/Step/images/Step1.png`,
`${ECHARTS}/Timeseries/Step/images/Step2.png`,
],
funnel: [`${ECHARTS}/Funnel/images/example.jpg`],
gantt_chart: [
`${ECHARTS}/Gantt/images/example1.png`,
`${ECHARTS}/Gantt/images/example2.png`,
],
gauge_chart: [
`${ECHARTS}/Gauge/images/example1.jpg`,
`${ECHARTS}/Gauge/images/example2.jpg`,
],
graph_chart: [`${ECHARTS}/Graph/images/example.jpg`],
handlebars: [
'plugins/plugin-chart-handlebars/src/images/example1.jpg',
'plugins/plugin-chart-handlebars/src/images/example2.jpg',
],
heatmap_v2: [
`${ECHARTS}/Heatmap/images/example1.png`,
`${ECHARTS}/Heatmap/images/example2.png`,
`${ECHARTS}/Heatmap/images/example3.png`,
],
histogram_v2: [
`${ECHARTS}/Histogram/images/example1.png`,
`${ECHARTS}/Histogram/images/example2.png`,
],
horizon: ['plugins/plugin-chart-horizon/src/images/Horizon_Chart.jpg'],
mixed_timeseries: [`${ECHARTS}/MixedTimeseries/images/example.jpg`],
paired_ttest: ['plugins/plugin-chart-paired-t-test/src/images/example.jpg'],
para: [
'plugins/plugin-chart-parallel-coordinates/src/images/example1.jpg',
'plugins/plugin-chart-parallel-coordinates/src/images/example2.jpg',
],
partition: ['plugins/plugin-chart-partition/src/images/example.jpg'],
pie: [
`${ECHARTS}/Pie/images/Pie1.jpg`,
`${ECHARTS}/Pie/images/Pie2.jpg`,
`${ECHARTS}/Pie/images/Pie3.jpg`,
`${ECHARTS}/Pie/images/Pie4.jpg`,
],
pivot_table_v2: ['plugins/plugin-chart-pivot-table/src/images/example.jpg'],
point_cluster_map: [
'plugins/plugin-chart-point-cluster-map/src/images/MapBox.jpg',
'plugins/plugin-chart-point-cluster-map/src/images/MapBox2.jpg',
],
radar: [
`${ECHARTS}/Radar/images/example1.jpg`,
`${ECHARTS}/Radar/images/example2.jpg`,
],
rose: [
`${ECHARTS}/Rose/images/example1.jpg`,
`${ECHARTS}/Rose/images/example2.jpg`,
],
sankey_v2: [
`${ECHARTS}/Sankey/images/example1.png`,
`${ECHARTS}/Sankey/images/example2.png`,
],
sunburst_v2: [
`${ECHARTS}/Sunburst/images/Sunburst1.png`,
`${ECHARTS}/Sunburst/images/Sunburst2.png`,
],
table: [
'plugins/plugin-chart-table/src/images/Table.jpg',
'plugins/plugin-chart-table/src/images/Table2.jpg',
'plugins/plugin-chart-table/src/images/Table3.jpg',
],
time_pivot: [`${ECHARTS}/TimePivot/images/example.jpg`],
time_table: ['src/visualizations/TimeTable/images/example.jpg'],
tree_chart: [`${ECHARTS}/Tree/images/tree.png`],
treemap_v2: [
`${ECHARTS}/Treemap/images/treemap_v2_1.png`,
`${ECHARTS}/Treemap/images/treemap_v2_2.jpg`,
],
waterfall: [
`${ECHARTS}/Waterfall/images/example1.png`,
`${ECHARTS}/Waterfall/images/example2.png`,
`${ECHARTS}/Waterfall/images/example3.png`,
],
word_cloud: [
'plugins/plugin-chart-word-cloud/src/images/Word_Cloud.jpg',
'plugins/plugin-chart-word-cloud/src/images/Word_Cloud_2.jpg',
],
world_map: [
'plugins/plugin-chart-world-map/src/images/WorldMap1.jpg',
'plugins/plugin-chart-world-map/src/images/WorldMap2.jpg',
],
deck_arc: [`${DECKGL}/Arc/images/example.png`],
deck_contour: [`${DECKGL}/Contour/images/example.png`],
deck_geojson: [`${DECKGL}/Geojson/images/example.png`],
deck_grid: [`${DECKGL}/Grid/images/example.png`],
deck_heatmap: [`${DECKGL}/Heatmap/images/example.png`],
deck_hex: [`${DECKGL}/Hex/images/example.png`],
deck_multi: ['plugins/preset-chart-deckgl/src/Multi/images/example.png'],
deck_path: [`${DECKGL}/Path/images/example.png`],
deck_polygon: [`${DECKGL}/Polygon/images/example.png`],
deck_scatter: [`${DECKGL}/Scatter/images/example.png`],
deck_screengrid: [`${DECKGL}/Screengrid/images/example.png`],
};
interface ExampleChart {
id: number;
sliceName: string;
vizType: string;
}
interface DashboardRow {
id: number;
}
interface DashboardChartRow {
id?: number;
slice_name?: string;
form_data?: { viz_type?: string };
}
/** Pages through a list endpoint, returning every result row. */
async function fetchAllPages<T>(page: Page, endpoint: string): Promise<T[]> {
const rows: T[] = [];
const pageSize = 100;
for (let pageNum = 0; ; pageNum += 1) {
const q = encodeURIComponent(
`(page_size:${pageSize},page:${pageNum},order_direction:asc)`,
);
const response = await page.request.get(`${endpoint}?q=${q}`);
expect(response.ok(), `GET ${endpoint} page ${pageNum}`).toBeTruthy();
const { result }: { result: T[] } = await response.json();
rows.push(...result);
if (result.length < pageSize) return rows;
}
}
/** Discovers every chart placed on any dashboard. */
async function discoverDashboardCharts(page: Page): Promise<ExampleChart[]> {
const dashboards = await fetchAllPages<DashboardRow>(
page,
'/api/v1/dashboard/',
);
const chartsById = new Map<number, ExampleChart>();
for (const dashboard of dashboards) {
const response = await page.request.get(
`/api/v1/dashboard/${dashboard.id}/charts`,
);
if (!response.ok()) continue;
const { result }: { result: DashboardChartRow[] } = await response.json();
for (const chart of result) {
const vizType = chart.form_data?.viz_type;
if (chart.id && chart.slice_name && vizType) {
chartsById.set(chart.id, {
id: chart.id,
sliceName: chart.slice_name,
vizType,
});
}
}
}
return [...chartsById.values()];
}
/** thumbnail.png -> thumbnail-dark.png, example.jpg -> example-dark.jpg */
function darkSibling(output: string): string {
return output.replace(/\.(png|jpg)$/, '-dark.$1');
}
async function renderAndShoot(
page: Page,
chart: ExampleChart,
colorScheme: 'light' | 'dark',
): Promise<Buffer> {
// An explicit navigation timeout keeps one hung load from stalling the
// whole crawl until the test timeout.
await page.goto(`/explore/?slice_id=${chart.id}&standalone=1`, {
timeout: 60_000,
});
// Dashboards render charts on colorBgContainer cards, but the standalone
// explore page paints the gray colorBgLayout (via the antd Layout
// wrapper); match the dashboard context so thumbnails look like charts
// do where users see them.
await page
.addStyleTag({
content: `body, .ant-layout { background: ${
colorScheme === 'dark' ? '#141414' : '#ffffff'
} !important; }`,
})
.catch(() => {});
const textOnly = TEXT_ONLY_VIZ_TYPES.has(chart.vizType);
await page
.locator(textOnly ? TEXT_RENDERED_CHART_SELECTOR : RENDERED_CHART_SELECTOR)
.first()
.waitFor({ state: 'visible', timeout: 60_000 });
// Give animations/map tiles (or text-only chart data) time to settle
await page.waitForTimeout(textOnly ? 4_000 : 2_000);
// Some thumbnails read better mid-interaction (e.g. the calendar heatmap
// showing its tooltip); hover the configured element before the still.
const hoverSelector = HOVER_BEFORE_CAPTURE[chart.vizType];
if (hoverSelector) {
// Hover a mid-chart element rather than the first (often an empty
// corner cell), falling back to the first when there are few.
const cells = page.locator(hoverSelector);
const count = await cells.count().catch(() => 0);
await cells
.nth(Math.floor(count / 2))
.hover({ timeout: 5_000 })
.catch(() => {});
await page.waitForTimeout(500);
}
return page.screenshot();
}
/**
* Captures a chart light and (when a dark variant is wanted) dark. Dark
* rendering relies on the app following prefers-color-scheme (theme mode
* SYSTEM); if the dark render is byte-identical to the light one the app
* ignored the emulation, and the dark file is left untouched rather than
* overwritten with light-theme art.
*/
async function captureChart(
page: Page,
chart: ExampleChart,
output: string,
size: { width: number; height: number },
): Promise<void> {
const outputPath = path.join(FRONTEND_ROOT, output);
const darkPath = path.join(FRONTEND_ROOT, darkSibling(output));
const isNewImage = !fs.existsSync(outputPath);
const wantDark = fs.existsSync(darkPath) || isNewImage;
await page.setViewportSize(size);
await page.emulateMedia({ colorScheme: 'light' });
const lightShot = await renderAndShoot(page, chart, 'light');
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, lightShot);
// eslint-disable-next-line no-console
console.log(`captured ${chart.sliceName} (${chart.vizType}) -> ${output}`);
if (wantDark) {
await page.emulateMedia({ colorScheme: 'dark' });
const darkShot = await renderAndShoot(page, chart, 'dark');
if (darkShot.equals(lightShot)) {
// eslint-disable-next-line no-console
console.log(
`SKIPPED dark variant for ${chart.sliceName}: the app ignored the dark color-scheme emulation (is dark theming enabled?)`,
);
} else {
fs.writeFileSync(darkPath, darkShot);
// eslint-disable-next-line no-console
console.log(
`captured ${chart.sliceName} (dark) -> ${darkSibling(output)}`,
);
}
}
if (isNewImage) {
// eslint-disable-next-line no-console
console.log(
`NOTE: ${output} is a new gallery image — register it in the plugin metadata (exampleGallery) to surface it.`,
);
}
}
test.describe('capture viz thumbnails', () => {
test.skip(
!process.env.CAPTURE_THUMBNAILS,
'Thumbnail capture only runs with CAPTURE_THUMBNAILS=1',
);
test('crawls example dashboards and refreshes gallery thumbnails', async ({
page,
}) => {
test.setTimeout(90 * 60_000);
const vizTypeFilter = process.env.VIZ_TYPES
? new Set(process.env.VIZ_TYPES.split(',').map(v => v.trim()))
: null;
const charts = await discoverDashboardCharts(page);
expect(
charts.length,
'no dashboard charts found — are examples loaded?',
).toBeGreaterThan(0);
// Every distinct chart per viz type gets captured: the preferred (or
// alphabetically-first) chart becomes the picker thumbnail, and each
// subsequent distinct chart fills the next declared gallery slot, so
// gallery images show real variety instead of the same chart resized.
const byVizType = new Map<string, ExampleChart[]>();
for (const chart of charts) {
const group = byVizType.get(chart.vizType) ?? [];
group.push(chart);
byVizType.set(chart.vizType, group);
}
const thumbnailSize = { width: THUMBNAIL_SIZE, height: THUMBNAIL_SIZE };
const exampleSize = { width: EXAMPLE_WIDTH, height: EXAMPLE_HEIGHT };
const unmapped: string[] = [];
const underfilledGalleries: string[] = [];
const failures: string[] = [];
for (const [vizType, group] of [...byVizType.entries()].sort()) {
if (vizTypeFilter && !vizTypeFilter.has(vizType)) continue;
const thumbOutput = VIZ_TYPE_THUMBNAILS[vizType];
const galleryOutputs = VIZ_TYPE_GALLERY[vizType] ?? [];
if (!thumbOutput && !galleryOutputs.length) {
unmapped.push(vizType);
continue;
}
// Preferred slice (if present) leads; the rest follow alphabetically.
group.sort((a, b) => a.sliceName.localeCompare(b.sliceName));
const preferredIndex = group.findIndex(
c => c.sliceName === PREFERRED_SLICES[vizType],
);
const ordered =
preferredIndex > 0
? [
group[preferredIndex],
...group.slice(0, preferredIndex),
...group.slice(preferredIndex + 1),
]
: group;
// Only advance past the thumbnail's chart when a thumbnail is
// actually captured (e.g. handlebars has no VIZ_TYPE_THUMBNAILS
// entry by design, so its gallery gets the full ordered list).
let nextIndex = 0;
if (thumbOutput) {
const chart = ordered[0];
nextIndex = 1;
try {
await captureChart(page, chart, thumbOutput, thumbnailSize);
} catch (error) {
failures.push(`${vizType} (${chart.sliceName}): ${error}`);
}
}
const missingSlots: string[] = [];
for (const output of galleryOutputs) {
const chart = ordered[nextIndex];
nextIndex += 1;
if (!chart) {
missingSlots.push(output);
continue;
}
try {
await captureChart(page, chart, output, exampleSize);
} catch (error) {
failures.push(`${vizType} gallery (${chart.sliceName}): ${error}`);
}
}
if (missingSlots.length) {
underfilledGalleries.push(
`${vizType}: not enough distinct dashboard charts for ${missingSlots.join(', ')}`,
);
}
}
if (unmapped.length) {
// eslint-disable-next-line no-console
console.log(
`viz types on dashboards with no thumbnail or gallery mapping (add to VIZ_TYPE_THUMBNAILS/VIZ_TYPE_GALLERY if wanted): ${unmapped.join(', ')}`,
);
}
if (underfilledGalleries.length) {
// eslint-disable-next-line no-console
console.log(
`gallery slots left untouched for lack of distinct example charts:\n${underfilledGalleries.join('\n')}`,
);
}
expect(failures, failures.join('\n')).toEqual([]);
});
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

@@ -19,14 +19,14 @@
import { t } from '@apache-superset/core/translation';
import { Behavior, ChartMetadata, ChartPlugin } from '@superset-ui/core';
import transformProps from './transformProps';
import thumbnail from './images/thumbnail.png';
import thumbnailDark from './images/thumbnail-dark.png';
import example1 from './images/Table.jpg';
import example1Dark from './images/Table-dark.jpg';
import example2 from './images/Table2.jpg';
import example2Dark from './images/Table2-dark.jpg';
import example3 from './images/Table3.jpg';
import example3Dark from './images/Table3-dark.jpg';
import thumbnail from './images/custom_thumb_thumbnail.png';
import thumbnailDark from './images/custom_thumb_thumbnail-dark.png';
import example1 from './images/custom_thumb_Table.jpg';
import example1Dark from './images/custom_thumb_Table-dark.jpg';
import example2 from './images/custom_thumb_Table2.jpg';
import example2Dark from './images/custom_thumb_Table2-dark.jpg';
import example3 from './images/custom_thumb_Table3.jpg';
import example3Dark from './images/custom_thumb_Table3-dark.jpg';
import controlPanel from './controlPanel';
import buildQuery from './buildQuery';
import { TableChartFormData, TableChartProps } from './types';
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

@@ -19,8 +19,8 @@
import { ChartMetadata, ChartPlugin } from '@superset-ui/core';
import { t } from '@apache-superset/core/translation';
import transformProps from './transformProps';
import example from './images/example.jpg';
import exampleDark from './images/example-dark.jpg';
import example from './images/custom_thumb_example.jpg';
import exampleDark from './images/custom_thumb_example-dark.jpg';
import controlPanel from './controlPanel';
import thumbnail from './images/thumbnail.png';
import thumbnailDark from './images/thumbnail-dark.png';
@@ -21,12 +21,12 @@ import { ChartMetadata, ChartPlugin } from '@superset-ui/core';
import buildQuery from './buildQuery';
import controlPanel from './controlPanel';
import transformProps from './transformProps';
import thumbnail from '../images/thumbnail.png';
import thumbnailDark from '../images/thumbnail-dark.png';
import example1 from '../images/example1.png';
import example1Dark from '../images/example1-dark.png';
import example2 from '../images/example2.png';
import example2Dark from '../images/example2-dark.png';
import thumbnail from '../images/custom_thumb_thumbnail.png';
import thumbnailDark from '../images/custom_thumb_thumbnail-dark.png';
import example1 from '../images/custom_thumb_example1.png';
import example1Dark from '../images/custom_thumb_example1-dark.png';
import example2 from '../images/custom_thumb_example2.png';
import example2Dark from '../images/custom_thumb_example2-dark.png';
import { CartodiagramPluginConstructorOpts } from '../types';
import { getLayerConfig } from '../util/controlPanelUtil';
Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

@@ -19,10 +19,10 @@
import { t } from '@apache-superset/core/translation';
import { ChartMetadata, ChartPlugin, Behavior } from '@superset-ui/core';
import transformProps from './transformProps';
import exampleUsa from './images/exampleUsa.jpg';
import exampleUsaDark from './images/exampleUsa-dark.jpg';
import exampleGermany from './images/exampleGermany.jpg';
import exampleGermanyDark from './images/exampleGermany-dark.jpg';
import exampleUsa from './images/custom_thumb_exampleUsa.jpg';
import exampleUsaDark from './images/custom_thumb_exampleUsa-dark.jpg';
import exampleGermany from './images/custom_thumb_exampleGermany.jpg';
import exampleGermanyDark from './images/custom_thumb_exampleGermany-dark.jpg';
import thumbnail from './images/thumbnail.png';
import thumbnailDark from './images/thumbnail-dark.png';
import controlPanel from './controlPanel';
@@ -21,8 +21,8 @@ import { ChartMetadata, ChartPlugin } from '@superset-ui/core';
import buildQuery from './buildQuery';
import controlPanel from './controlPanel';
import transformProps from './transformProps';
import thumbnail from './images/thumbnail.png';
import thumbnailDark from './images/thumbnail-dark.png';
import thumbnail from './images/custom_thumb_thumbnail.png';
import thumbnailDark from './images/custom_thumb_thumbnail-dark.png';
export default class PopKPIPlugin extends ChartPlugin {
constructor() {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 27 KiB

@@ -25,8 +25,8 @@ import buildQuery from './buildQuery';
import controlPanel from './controlPanel';
import example1 from './images/example1.png';
import example1Dark from './images/example1-dark.png';
import example2 from './images/example2.png';
import example2Dark from './images/example2-dark.png';
import example2 from './images/custom_thumb_example2.png';
import example2Dark from './images/custom_thumb_example2-dark.png';
import { EchartsBubbleChartProps, EchartsBubbleFormData } from './types';
// TODO: Implement cross filtering

Some files were not shown because too many files have changed in this diff Show More