Skip to content

fix(dashboard): apply dynamic groupby display controls to scoped charts#39356

Merged
richardfogaca merged 5 commits intoapache:masterfrom
richardfogaca:fix/dashboard-dynamic-groupby-core
Apr 15, 2026
Merged

fix(dashboard): apply dynamic groupby display controls to scoped charts#39356
richardfogaca merged 5 commits intoapache:masterfrom
richardfogaca:fix/dashboard-dynamic-groupby-core

Conversation

@richardfogaca
Copy link
Copy Markdown
Contributor

@richardfogaca richardfogaca commented Apr 14, 2026

SUMMARY

This PR fixes Dynamic Group By display controls not updating scoped dashboard charts correctly.

The bug had two main causes:

  1. The selected display-control value was being treated as a row-filter value during query generation, instead of being applied as a chart dimension.
    Example: selecting status could produce a bogus status IN ("status") filter instead of updating the chart groupby.

  2. Legacy chart customization metadata was not normalized before scope calculation, so some charts that should have been in scope were skipped and never updated.

This PR fixes that by:

  • applying Dynamic Group By selections to chart form data as dimensions instead of injecting them as spurious filters
  • preserving valid selections even when the selected column already exists in the chart's base groupby
  • normalizing single-select values so they flow through the same groupby-handling path as multi-select values
  • normalizing legacy chart customization items before scope calculation so the intended charts are actually updated
  • adding focused regression coverage for both query generation and dashboard scope handling

Why this works:

  • the selected column is now carried through the query path as groupby, which is what affected charts expect
  • the old invalid filters clause is no longer generated
  • legacy customizations are scoped before charts are evaluated, so the right charts receive the update

This keeps the fix focused on the reported dashboard Dynamic Group By issue.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

BEFORE

Selecting a Dynamic Group By value could fail to change the chart grouping because the selected column was incorrectly turned into a row filter, or because the chart was skipped during scope resolution.
before

AFTER

The same interaction now updates the chart query using the selected column in groupby, without generating a bogus row filter, and scoped charts update correctly.
after

TESTING INSTRUCTIONS

  1. Create an aggregate table chart with a base groupby and add it to a dashboard.
  2. Add a Dynamic Group By display control scoped to that chart.
  3. Select a column and click Apply filters.
  4. Expected:
    • the chart re-queries with the selected column applied in groupby
    • no spurious filters clause is generated using the selected column name as a value
  5. Also verify a legacy dashboard metadata case still applies the customization to the correct scoped chart.

Automated:

  • npm run test -- src/dashboard/util/getFormDataWithExtraFilters.test.ts
  • npm run test -- src/dashboard/components/DashboardBuilder/DashboardContainer.test.tsx

ADDITIONAL INFORMATION

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

Copilot AI review requested due to automatic review settings April 14, 2026 22:18
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review bot commented Apr 14, 2026

Code Review Agent Run #3efc58

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts - 1
    • Missing return type hints · Line 25-66
      Helper functions should include explicit return type hints for consistency and type safety, as per project guidelines.
      Code suggestion
       @@ -25,5 +25,5 @@
        const expectGroupBy = (
          result: CachedFormDataWithExtraControls,
          expected: unknown,
      - ) => {
      + ): void => {
          expect('groupby' in result).toBe(true);
          if (!('groupby' in result)) {
            throw new Error('Expected groupby to be present in form data');
          }
          expect(result.groupby).toEqual(expected);
        };
       @@ -36,5 +36,5 @@
        const expectGroupByLength = (
          result: CachedFormDataWithExtraControls,
          length: number,
      - ) => {
      + ): void => {
          expect('groupby' in result).toBe(true);
          if (!('groupby' in result)) {
            throw new Error('Expected groupby to be present in form data');
          }
          expect(result.groupby).toHaveLength(length);
        };
       @@ -47,2 +47,2 @@
        const getResultFilters = (result: CachedFormDataWithExtraControls) => {
      + const getResultFilters = (result: CachedFormDataWithExtraControls): {col: string; val: unknown[]}[] => {
          if (!('filters' in result) || !Array.isArray(result.filters)) {
            return [];
          }
          return result.filters.filter(
            (
              filter,
            ): filter is {
              col: string;
              val: unknown[];
            } =>
              typeof filter === 'object' &&
              filter !== null &&
              'col' in filter &&
              'val' in filter &&
              typeof filter.col === 'string' &&
              Array.isArray(filter.val),
          );
        };
Review Details
  • Files reviewed - 4 · Commit Range: aaffada..aaffada
    • superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.test.tsx
    • superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx
    • superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts
    • superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

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

  • /review - 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

@dosubot dosubot bot added change:frontend Requires changing the frontend dashboard Namespace | Anything related to the Dashboard labels Apr 14, 2026
Comment thread superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts Outdated
Comment thread superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts Outdated
Comment thread superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts Outdated
Comment thread superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes dashboard Dynamic Group By display controls so scoped charts receive correct groupby updates (and no longer get incorrect filter injections), including improved handling of legacy chart customization metadata.

Changes:

  • Remove the spurious “selected column as filter value” injection path and ensure dynamic group-by selections are applied as chart dimensions.
  • Normalize single-select dynamic group-by values (string → one-item array) and deduplicate chord groupby.
  • Normalize/migrate legacy chart customization config before scope calculation, and add regression tests for getFormDataWithExtraFilters and DashboardContainer.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts Adds focused regression coverage for dynamic group-by behavior and scoping edge cases.
superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts Fixes dynamic group-by form-data generation (remove filter injection, normalize single-select, chord dedupe).
superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx Migrates legacy chart customization items before scope calculation.
superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.test.tsx Adds tests validating charts-in-scope calculation for chart customizations, including legacy format migration.
Comments suppressed due to low confidence (1)

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

  • The legacy normalization + calculateScopes path can incorrectly expand a legacy chart customization that was previously scoped via chartId. migrateChartCustomization() sets chartsInScope: [legacy.chartId], but calculateScopes() ignores chartsInScope and recomputes it solely from item.scope; the migrated legacy scope defaults to { rootPath: [DASHBOARD_ROOT_ID], excluded: [] }, which will put all charts in scope and then setInScopeStatusOfCustomizations persists that broadened scope back into metadata.

To preserve legacy per-chart scoping, consider special-casing legacy items that include chartId when building normalizedCustomizations (e.g., after migration, set scope.excluded to chartIds.filter(id => id !== legacy.chartId) or otherwise derive a scope that yields only the intended chart(s) in calculateScopes).

    // Normalize legacy chart customizations before scope calculation.
    const hasLegacy = chartCustomizations.some(
      isLegacyChartCustomizationFormat,
    );
    const normalizedCustomizations = hasLegacy
      ? migrateChartCustomizationArray(chartCustomizations)
      : chartCustomizations;

    const scopes = calculateScopes(
      normalizedCustomizations,
      chartIds,
      chartLayoutItems,
      item => item.type === ChartCustomizationType.Divider,
    ).map(scope => ({

Comment thread superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts Outdated
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 14, 2026

Codecov Report

❌ Patch coverage is 80.95238% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.54%. Comparing base (c2d96e0) to head (2e6d68f).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...shboard/util/charts/getFormDataWithExtraFilters.ts 66.66% 3 Missing ⚠️
...components/DashboardBuilder/DashboardContainer.tsx 91.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #39356      +/-   ##
==========================================
+ Coverage   64.45%   64.54%   +0.09%     
==========================================
  Files        2557     2557              
  Lines      132976   132985       +9     
  Branches    30885    30891       +6     
==========================================
+ Hits        85713    85841     +128     
+ Misses      45772    45653     -119     
  Partials     1491     1491              
Flag Coverage Δ
javascript 66.33% <80.95%> (+0.16%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 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.

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review bot commented Apr 14, 2026

Code Review Agent Run #4360c0

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts - 1
    • Misleading test description · Line 332-337
      The test description claims it 'keeps the base groupby', but the expectation sets groupby to ['status'], replacing the base ['original_column']. This is misleading and should accurately describe that empty strings are ignored while valid selections are applied.
Review Details
  • Files reviewed - 4 · Commit Range: aaffada..067d7ad
    • superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.test.tsx
    • superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx
    • superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts
    • superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

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

  • /review - 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

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review bot commented Apr 15, 2026

Code Review Agent Run #940d46

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 067d7ad..e2cb057
    • superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts
    • superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

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

  • /review - 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

Copy link
Copy Markdown
Contributor

@aminghadersohi aminghadersohi left a comment

Choose a reason for hiding this comment

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

Review feedback

Nice fix — the core issue (groupby values being injected as row filters) is clearly identified and the removal of the allFilters logic is the right call. A few things I'd like to understand or see addressed:

Medium: Chord chart — contradictory code paths

processGroupByCustomizations now returns {} early for chord charts (line 331), which means the updated chord branch in applyChartSpecificGroupBy (lines 413-414) is unreachable through the dynamic groupby flow. Could you either:

  • Remove the early return and rely on the applyChartSpecificGroupBy change, or
  • Revert the applyChartSpecificGroupBy chord change since it's dead code?

Having both is confusing to future readers.

Low: Removing existingGroupBy from buildExistingColumnsSet

Previously, columns already in the chart's base groupby were treated as conflicts and filtered out by nonConflictingColumns. Now they pass through. The "still applies when the selected column is already in the base groupby" test covers the happy path, but could this cause duplicate columns for chart types that merge (append) rather than replace groupby? Worth a quick check.

Nit: Test cleanup pattern

The try/finally spy restore in DashboardContainer.test.tsx is thorough but verbose — an afterEach(() => jest.restoreAllMocks()) at the top of the file would simplify all five test cases.

Otherwise the test coverage is solid and the legacy normalization in normalizeChartCustomizationsForScopeCalculation looks clean.

@richardfogaca
Copy link
Copy Markdown
Contributor Author

richardfogaca commented Apr 15, 2026

hey @aminghadersohi , thanks for reviewing this PR :)

The chord-specific branch is leftover from an earlier attempt to support chord in the generic dynamic groupby flow, but chord was later excluded after local testing exposed regressions. So in the current PR state that branch is dead code.

I don’t think we should re-enable chord in this PR just to make that path reachable. Chord is a legacy special case: it still models source/target through groupby + columns, and the backend builds its query from those fields directly, so it’s riskier than the other charts covered here. The safer approach is to keep chord excluded in this PR and remove the unreachable branch so the intent is explicit.

I also checked the existingGroupBy concern. For the chart types still participating in this flow, I don’t see an obvious duplication issue from that change, but I agree it was worth validating.

The jest.restoreAllMocks() cleanup suggestion is reasonable too, and I can fold that in.

What do you think?

@richardfogaca richardfogaca merged commit c3a0f27 into apache:master Apr 15, 2026
68 checks passed
@richardfogaca richardfogaca deleted the fix/dashboard-dynamic-groupby-core branch April 15, 2026 17:57
@bito-code-review
Copy link
Copy Markdown
Contributor

Bito Automatic Review Skipped – PR Already Merged

Bito scheduled an automatic review for this pull request, but the review was skipped because this PR was merged before the review could be run.
No action is needed if you didn't intend to review it. To get a review, you can type /review in a comment and save it

michael-s-molina pushed a commit that referenced this pull request Apr 15, 2026
sadpandajoe added a commit that referenced this pull request Apr 16, 2026
…d add null guards

PR #39356 removed existing groupby columns from buildExistingColumnsSet(),
causing processGroupByCustomizations() to replace charts' base groupby
instead of merging. This crashed dashboards with chart customizations
(dynamic groupby). Also adds null guards in isLegacyChartCustomizationFormat()
and normalizeChartCustomizationsForScopeCalculation() for legacy items with
customization: null.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:frontend Requires changing the frontend dashboard Namespace | Anything related to the Dashboard size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants