Skip to content

fix(chart): cross-filter emits dimension value instead of metric label for stacked bars#38120

Open
nitishagar wants to merge 1 commit into
apache:masterfrom
nitishagar:fix-37882
Open

fix(chart): cross-filter emits dimension value instead of metric label for stacked bars#38120
nitishagar wants to merge 1 commit into
apache:masterfrom
nitishagar:fix-37882

Conversation

@nitishagar
Copy link
Copy Markdown
Contributor

@nitishagar nitishagar commented Feb 20, 2026

User description

SUMMARY

Fixes the cross-filter behavior for stacked bar charts with multiple metrics. When clicking a stacked bar segment, the cross-filter incorrectly emitted the metric label (e.g., "Intent") as the filter value instead of the actual groupby dimension value (e.g., "cancellations"). This caused downstream charts to receive invalid filters and return no rows.

Root cause: The labelMap arrays for multi-metric series contain both metric labels and dimension values (e.g., ["Intent", "cancellations"]), but getCrossFilterDataMask indexed from position 0 (the metric label) instead of skipping the metric entries to reach the dimension values.

Fix: Calculate a metricsCount offset (labelMap entry length - groupby column count) and index from there. This is the same approach already used by the drillBy handler in EchartsTimeseries.tsx (lines 240-241), which correctly skips metric values.

Applied consistently across all three getCrossFilterDataMask implementations:

  • EchartsTimeseries.tsx — Timeseries/Bar charts
  • EchartsMixedTimeseries.tsx — Mixed Timeseries charts
  • eventHandlers.ts — shared handler used by Pie, Funnel, Gauge, Radar, BoxPlot

For single-metric charts, the offset is 0, so behavior is unchanged.

TESTING INSTRUCTIONS

  1. Create a dataset with a dimension column (e.g., topics) and two metrics (e.g., Intent, Volume)
  2. Create a stacked Bar Chart with both metrics and topics as the groupby dimension
  3. Add a Table chart to the same dashboard
  4. Enable cross-filtering on the dashboard
  5. Click on a stacked bar segment (e.g., the "cancellations" segment)
  6. Verify the Table chart filters correctly by topics = 'cancellations' instead of using the metric label

ADDITIONAL INFORMATION


CodeAnt-AI Description

Use the clicked dimension value when cross-filtering stacked and multi-metric charts

What Changed

  • Clicking a stacked bar or other multi-metric chart segment now sends the actual group value to filters, not the metric name
  • Cross-filtering now works correctly when a chart has more than one grouping column
  • Added tests that cover single-metric charts, multi-metric charts, multiple group-by columns, deselection, and disabled cross-filtering

Impact

✅ Correct filters from stacked chart clicks
✅ Fewer empty or wrong dashboard results
✅ Reliable cross-filtering across chart types

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Feb 20, 2026

Code Review Agent Run #0bb261

Actionable Suggestions - 0
Review Details
  • Files reviewed - 4 · Commit Range: d2de3bb..d2de3bb
    • superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/EchartsMixedTimeseries.tsx
    • superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/eventHandlers.ts
    • superset-frontend/plugins/plugin-chart-echarts/test/utils/eventHandlers.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

@sfirke
Copy link
Copy Markdown
Member

sfirke commented Feb 20, 2026

@nitishagar sorry if this is a half-baked question but how does this interact with #37407 which was merged recently and is cherry-picked into 6.0.1rc1? Does your PR interact with that one or are they touching different aspects of Superset? Just on my mind since I tested that other PR out this week in 6.0.1rc1.

@sadpandajoe
Copy link
Copy Markdown
Member

Superset uses Git pre-commit hooks courtesy of pre-commit. To install run the following:

pip3 install -r requirements/development.txt
pre-commit install

A series of checks will now run when you make a git commit.

Alternatively it is possible to run pre-commit by running pre-commit manually:

pre-commit run --all-files

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 cross-filtering for stacked/multi-metric ECharts bar/timeseries (and related charts using shared handlers) so that emitted filters use the actual groupby dimension value(s) rather than the metric label.

Changes:

  • Update getCrossFilterDataMask implementations to skip metric label entries in labelMap and extract the correct groupby dimension values.
  • Apply the same offset logic across Timeseries, MixedTimeseries, and shared eventHandlers.
  • Add unit tests for the shared eventHandlers cross-filter behavior (single- and multi-metric cases).

Reviewed changes

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

File Description
superset-frontend/plugins/plugin-chart-echarts/test/utils/eventHandlers.test.ts Adds unit tests verifying cross-filter emits dimension values (including multi-metric + multi-groupby scenarios).
superset-frontend/plugins/plugin-chart-echarts/src/utils/eventHandlers.ts Adjusts cross-filter value extraction to skip metric label(s) in labelMap entries.
superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx Adjusts cross-filter value extraction for timeseries/bar charts to skip metric label(s).
superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/EchartsMixedTimeseries.tsx Adjusts cross-filter value extraction for mixed timeseries charts to skip metric label(s).
Comments suppressed due to low confidence (3)

superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx:141

  • val is computed as an array via groupbyValues.map(...), so val === null || val === undefined can’t be true. If a dimension value can be null/undefined, this will incorrectly emit an IN filter containing null instead of IS NULL. Consider checking the selected element(s) inside val instead of the array itself.
                    const val = groupbyValues.map(v => {
                      const metricsCount = v.length - groupby.length;
                      return v[metricsCount + idx];
                    });
                    if (val === null || val === undefined)
                      return {
                        col,
                        op: 'IS NULL' as const,
                      };
                    return {

superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/EchartsMixedTimeseries.tsx:94

  • val is an array (from groupbyValues.map(...)), so val === null || val === undefined will never be true. This makes the IS NULL branch unreachable and can produce IN [null] when a groupby value is null/undefined. Consider checking the extracted value(s) inside val instead.
                    const val: DataRecordValue[] = groupbyValues.map(
                      v => {
                        const metricsCount =
                          v.length - currentGroupBy.length;
                        return v[metricsCount + idx];
                      },
                    );
                    if (val === null || val === undefined)
                      return {
                        col,
                        op: 'IS NULL' as const,
                      };
                    return {

superset-frontend/plugins/plugin-chart-echarts/src/utils/eventHandlers.ts:76

  • val is always an array (from groupbyValues.map(...)), so the val === null || val === undefined check will never be true. This prevents emitting an IS NULL filter when the selected dimension value is actually null/undefined (you’ll end up with IN [null], which won’t match in SQL). Consider checking the extracted element(s) instead (e.g., val[0] for single-select behavior, or val.every(...)/split clauses if multi-select is supported).
                  const val = groupbyValues.map(v => {
                    const metricsCount = v.length - groupby.length;
                    return v[metricsCount + idx];
                  });
                  if (val === null || val === undefined)
                    return {
                      col,
                      op: 'IS NULL' as const,
                    };

Comment on lines +132 to +135
const val = groupbyValues.map(v => {
const metricsCount = v.length - groupby.length;
return v[metricsCount + idx];
});
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

This change fixes stacked/multi-metric cross-filtering in getCrossFilterDataMask, but there isn’t a unit test covering the multi-metric + groupby click path in EchartsTimeseries (the reported regression case). Adding a test that simulates a click on a stacked-bar seriesName like 'Intent, cancellations' and asserts the emitted filter uses the dimension value would prevent regressions.

Copilot generated this review using guidance from repository custom instructions.
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

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (3)

superset-frontend/plugins/plugin-chart-echarts/src/utils/eventHandlers.ts:76

  • val is always an array here (result of groupbyValues.map(...)), so the val === null || val === undefined check will never be true. This makes the IS NULL branch unreachable and will emit an IN filter even when the dimension value is null/undefined. Consider checking the mapped values (e.g., val[0] when single-select, or val.some(v => v == null)/splitting into IN + IS NULL logic) instead of checking the array itself.
                  const val = groupbyValues.map(v => {
                    const metricsCount = v.length - groupby.length;
                    return v[metricsCount + idx];
                  });
                  if (val === null || val === undefined)
                    return {
                      col,
                      op: 'IS NULL' as const,
                    };

superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx:141

  • val is an array (from groupbyValues.map(...)), so val === null || val === undefined will never be true and the IS NULL filter path is unreachable. If the intention is to support null dimension values, check the contents of val (typically val[0] since cross-filtering is single-select) and emit IS NULL when that value is nullish.
                    const val = groupbyValues.map(v => {
                      const metricsCount = v.length - groupby.length;
                      return v[metricsCount + idx];
                    });
                    if (val === null || val === undefined)
                      return {
                        col,
                        op: 'IS NULL' as const,
                      };
                    return {

superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/EchartsMixedTimeseries.tsx:93

  • val is declared as DataRecordValue[] and assigned via groupbyValues.map(...), so the subsequent val === null || val === undefined check can never succeed. If null groupby values are expected, check the mapped entries (e.g., val[0] for single-select) and emit an IS NULL clause based on the element value(s), not the array reference.
                    const val: DataRecordValue[] = groupbyValues.map(
                      v => {
                        const metricsCount =
                          v.length - currentGroupBy.length;
                        return v[metricsCount + idx];
                      },
                    );
                    if (val === null || val === undefined)
                      return {
                        col,
                        op: 'IS NULL' as const,
                      };

Comment on lines 83 to 88
v => {
const metricsCount =
v.length - currentGroupBy.length;
return v[metricsCount + idx];
},
);
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

The cross-filter labelMap indexing logic changed here, but there are no tests covering EchartsMixedTimeseries click-to-cross-filter behavior for multi-metric stacked series (including multiple groupby columns). Adding a small unit test (or factoring the indexing into a shared, tested helper) would help ensure this fix doesn’t regress.

Copilot uses AI. Check for mistakes.
const metricsCount = v.length - groupby.length;
return v[metricsCount + idx];
});
if (val === null || val === undefined)
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

The offset logic for multi-metric labelMap entries is updated here, but there doesn’t appear to be any unit test coverage exercising EchartsTimeseries cross-filter click behavior (especially for stacked multi-metric series). Consider adding a focused test (or extracting this indexing into a shared helper that can be unit-tested) to prevent regressions for the original bug scenario.

Suggested change
if (val === null || val === undefined)
if (val.every(vv => vv == null))

Copilot uses AI. Check for mistakes.
@codeant-ai-for-open-source codeant-ai-for-open-source Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Mar 11, 2026
@codeant-ai-for-open-source
Copy link
Copy Markdown
Contributor

Sequence Diagram

This PR fixes cross-filtering for multi-metric stacked charts so that clicking a segment emits filters based on groupby dimension values (with metric fields skipped) and updates dependent charts correctly.

sequenceDiagram
    participant User
    participant EchartsChart
    participant CrossFilterHandler
    participant DashboardFilters
    participant DependentChart

    User->>EchartsChart: Click stacked bar segment
    EchartsChart->>CrossFilterHandler: Handle click with labelMap and groupby
    CrossFilterHandler->>CrossFilterHandler: Skip metric labels and derive dimension values
    CrossFilterHandler->>DashboardFilters: setDataMask with dimension filters
    DashboardFilters->>DependentChart: Apply filters and refresh
    DependentChart-->>User: Updated data for selected dimension
Loading

Generated by CodeAnt AI

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Mar 11, 2026

Code Review Agent Run #8f3a5a

Actionable Suggestions - 0
Review Details
  • Files reviewed - 4 · Commit Range: 51ecf8f..51ecf8f
    • superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/EchartsMixedTimeseries.tsx
    • superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/eventHandlers.ts
    • superset-frontend/plugins/plugin-chart-echarts/test/utils/eventHandlers.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

…l for stacked bars

When clicking a stacked bar segment in a multi-metric chart, the cross-filter
incorrectly used the metric label as the filter value instead of the groupby
dimension value. This happened because labelMap arrays contain both metric
labels and dimension values (e.g., ["Intent", "cancellations"]), but the
getCrossFilterDataMask function indexed from position 0 (the metric label)
instead of skipping to the dimension values.

The fix calculates a metricsCount offset (labelMap entry length minus groupby
count) and indexes from there, consistent with how the drillBy handler already
works in EchartsTimeseries.tsx.

Applied to all three getCrossFilterDataMask implementations:
- EchartsTimeseries.tsx (Timeseries/Bar charts)
- EchartsMixedTimeseries.tsx (Mixed Timeseries charts)
- eventHandlers.ts (shared handler for Pie, Funnel, Gauge, Radar, BoxPlot)

Closes apache#37882
@codeant-ai-for-open-source codeant-ai-for-open-source Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels Mar 24, 2026
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Mar 24, 2026

Code Review Agent Run #928805

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx - 1
    • Incorrect null handling in cross-filter logic · Line 132-139
      The updated cross-filter logic improves null handling by checking if all dimension values are null to use 'IS NULL', but when dimension values are mixed (some null, some not), the 'IN' filter including nulls won't match null rows in SQL. This could lead to incomplete cross-filtering. Filtering nulls from the 'IN' values would ensure correct behavior.
      Code suggestion
       @@ -144,1 +144,1 @@
      -                      val: val as (string | number | boolean)[],
      +                      val: val.filter(vv => vv != null) as (string | number | boolean)[],
Review Details
  • Files reviewed - 4 · Commit Range: 24e965c..24e965c
    • superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/EchartsMixedTimeseries.tsx
    • superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/eventHandlers.ts
    • superset-frontend/plugins/plugin-chart-echarts/test/utils/eventHandlers.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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugins size/L size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bar Chart cross-filter emits metric label instead of groupby dimension value (Superset v6)

4 participants