Skip to content

fix(dashboard): initialize label colors before mounting charts - #44557

Merged
rusackas merged 3 commits into
apache:masterfrom
deepujain:fix-40708-initial-label-colors
Sep 24, 2026
Merged

rusackas merged 3 commits into
apache:masterfrom
deepujain:fix-40708-initial-label-colors

Conversation

@deepujain

@deepujain deepujain commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

SUMMARY

A chart with cached data can create its color scale during the dashboard's first render, before the dashboard effect installs label_colors. Its initial output then keeps palette colors even though the metadata specifies custom colors.

Keep the grid unmounted until that dashboard's color initialization has run. Track the initialized dashboard ID so navigating to another dashboard also waits for its colors. The existing color-selection and synchronization algorithms stay unchanged.

Fixes #40708.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

The screenshots use the production DashboardContainer, ECharts Timeseries transform, and ECharts renderer in an isolated Storybook fixture with an already-cached query. They demonstrate initial rendering, not the reporter's authenticated deployment. The metadata requests dark red for 60_Crashed, red for 50_Error, and #008000 for 20_Passed.

Before: the first render uses palette colors.

Before: cached chart ignores metadata colors

After: the same fixture uses the metadata colors on its first render.

After: cached chart uses metadata colors

Fixture and recorded browser series colors.

TESTING INSTRUCTIONS

Run the component and dashboard action suites:

cd superset-frontend
npm run test -- --maxWorkers=2 src/dashboard/components/DashboardBuilder/DashboardContainer.test.tsx src/dashboard/actions/dashboardState.test.ts

59 tests pass. Four regression cases fail on unmodified 61fffbe0c0: first color consumption with and without StrictMode, plus navigation that reuses or changes the color namespace. The tests use the real color initialization action and namespace. npm run plugins:build and staged-file pre-commit run also pass, including frontend type checking.

For browser reproduction, follow the linked fixture README and open a fresh page for each version. The baseline's ECharts series colors are #1f77b4, #ff7f0e, and #2ca02c; the patched version produces #8b0000, #ff0000, and #008000 without a legend click or refresh.

The grid now mounts one effect later; this does not add a network wait. Full application E2E coverage was not run.

CI note: babel-extract currently fails on the existing OAuth2 connection-move message drift in the base catalog. The same missing/obsolete message is corrected by the separate upstream PR #44547; this change adds no translated strings.

ADDITIONAL INFORMATION

Signed-off-by: Deepak Jain <deepujain@gmail.com>
@bito-code-review

bito-code-review Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #c475fe

Actionable Suggestions - 0
Additional Suggestions - 2
  • superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx - 2
    • Undefined sentinel collides · Line 167-170
      With `useState()`, `colorInitializedDashboardId` starts as `undefined`, and `dashboardInfo?.id` is also `undefined` before hydration (`dashboardInfoReducer` initial state is `{}`), so `dashboardLabelsColorInitiated` evaluates `true` on first mount and the line 385 gate renders children before `applyDashboardLabelsColorOnLoad` runs. Initialize with `null` so the sentinel cannot collide with an absent dashboard id.
    • Gate intent under-documented · Line 384-385
      The new gate `{dashboardLabelsColorInitiated && renderParentSizeChildren({ width })}` intentionally withholds the grid for one commit after hydration until the mount effect sets `colorInitializedDashboardId`. That behavior is correct, but the terse comment explains only the cached-chart motivation; a reader may misread the conditional as removable. Consider stating the deferral explicitly.
Review Details
  • Files reviewed - 2 · Commit Range: 4316a97..4316a97
    • superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.test.tsx
    • superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@codecov

codecov Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.18%. Comparing base (61fffbe) to head (13bf670).
⚠️ Report is 24 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #44557      +/-   ##
==========================================
- Coverage   66.22%   66.18%   -0.04%     
==========================================
  Files        2949     2949              
  Lines      177381   177383       +2     
  Branches    41107    41108       +1     
==========================================
- Hits       117469   117407      -62     
- Misses      57409    57467      +58     
- Partials     2503     2509       +6     
Flag Coverage Δ
javascript 76.37% <100.00%> (-0.01%) ⬇️
mysql ?

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

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

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

Upstream apache#44574 landed the messages.pot regeneration on master; the
babel-extract failure on this PR was base drift, not contributor code.
Fresh CI will run against the new merge commit with the fixed base pot.
@netlify

netlify Bot commented Sep 23, 2026

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 7cb92a8
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6ab3d741fba78000081f8e5c
😎 Deploy Preview https://deploy-preview-44557--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

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

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

Signed-off-by: Deepak Jain <deepujain@gmail.com>
@deepujain

Copy link
Copy Markdown
Contributor Author

Addressed both additional suggestions in Bito review #c475fe. The initialization sentinel is now null, so an absent dashboard ID cannot satisfy the render gate before hydration. The comment now explains that the grid waits for hydration and color initialization.

The new regression fails on the previous head because the grid mounts before hydration, then passes after the fix and verifies mounting after the dashboard ID arrives. All 20 DashboardContainer tests and the full plugin build pass.

@gabotorresruiz gabotorresruiz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @deepujain, thanks for chasing this one down. It is a fiddly corner of the dashboard and the change is small and well scoped, so this is an approve from me.

What I ran locally against 13bf6706af and its parent 61fffbe0c0: all five new cases in DashboardContainer.test.tsx fail on the parent and pass on this head, so they are real regression tests and not invariants that would have held either way. The DashboardContainer, DashboardBuilder, DashboardWrapper, DashboardPage, dashboardState and colorScheme suites are green on this head, 129 tests. I also drove an in-place dashboardInfo.id change through a mounted DashboardContainer on both branches and compared the dispatches, which is what the one inline note is about. On the sentinel change you just pushed, I checked every dashboardInfoChanged caller and they all run after hydration, and DashboardPage only mounts DashboardBuilder once dashboardInfo is non-empty, so null cannot leave the grid withheld in the app.

CI is fully green on this head, 69 passing and 14 skipped, including babel-extract, all eight jest shards, cypress and both playwright matrices, so the message drift you flagged in the description looks resolved.

One non-blocking comment inline, nothing that needs to hold the merge.

dispatch(applyDashboardLabelsColorOnLoad(dashboardInfo.metadata));
// apply labels color as dictated by stored metadata (if any)
setDashboardLabelsColorInitiated(true);
setColorInitializedDashboardId(dashboardInfo.id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not a blocker, just a note for the description: I think this line is the load-bearing half of the fix, and the summary currently reads as if the first-render race were the main story.

I ran both branches side by side. With DashboardContainer mounted and dashboardInfo.id changing in place, this branch dispatches applyDashboardLabelsColorOnLoad a second time with the new label_colors; on 61fffbe0c0 it is never dispatched again, because dashboardLabelsColorInitiated is already true. The effect cleanup has meanwhile run onBeforeUnload, which calls resetColors() on the namespace, so on master the second dashboard keeps palette colors for the rest of the session until a full reload. That matches the reporter's After refreshing or clicking legend, the colors become correct in #40708, and src/pages/Dashboard/index.tsx:25 renders DashboardPage with no key, so in-app navigation really does swap dashboardInfo.id under a mounted container.

For the first-render half I could not build a cold-load repro: hydrate.ts:174 seeds every chart from the initial chart state with queriesResponse: null, and DashboardPage.tsx:423 only mounts the container once dashboardInfo is populated, so no chart owns a query response at that first commit on a fresh page load. Am I missing a path there? Either way, calling the navigation case out in the description would make this easier to justify.

@bito-code-review

Copy link
Copy Markdown
Contributor

The observation is correct. The core of the fix is indeed the transition from a simple boolean dashboardLabelsColorInitiated to colorInitializedDashboardId, which allows the component to re-initialize colors when the dashboardInfo.id changes. By tying dashboardLabelsColorInitiated to the current dashboardInfo.id, the component correctly triggers applyDashboardLabelsColorOnLoad again during in-app navigation, ensuring the new dashboard's palette is applied. The addition of the conditional rendering {dashboardLabelsColorInitiated && renderParentSizeChildren({ width })} further ensures that the grid (and its cached charts) only mounts once the colors are ready, preventing the race condition where charts might consume default colors before the custom palette is applied.

superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx

const [colorInitializedDashboardId, setColorInitializedDashboardId] =
    useState<number | null>(null);
  const dashboardLabelsColorInitiated =
    colorInitializedDashboardId === dashboardInfo?.id;

superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx

{/* Defer the grid until hydration and color initialization complete,
          before cached charts consume their color scales on first render. */}
      {dashboardLabelsColorInitiated && renderParentSizeChildren({ width })}

@bito-code-review

bito-code-review Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #a1451e

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 4316a97..13bf670
    • superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.test.tsx
    • superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas
rusackas merged commit e357014 into apache:master Sep 24, 2026
83 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Some dashboard charts intermittently fall back to color_scheme instead of label_colors on initial render

3 participants