Skip to content

fix(echarts): enable cross-filtering for pie chart "Other" slice - #43088

Open
omsn2 wants to merge 13 commits into
apache:masterfrom
omsn2:fix-echarts-pie-cross-filter
Open

fix(echarts): enable cross-filtering for pie chart "Other" slice#43088
omsn2 wants to merge 13 commits into
apache:masterfrom
omsn2:fix-echarts-pie-cross-filter

Conversation

@omsn2

@omsn2 omsn2 commented Aug 12, 2026

Copy link
Copy Markdown

SUMMARY

The "Other" slice in a pie chart aggregates multiple rows into one visual
segment. In transformProps.ts, its labelMap entry is a 2D array
(string[][] — one row per aggregated data point), whereas all other
slices use a 1D array (string[]).

The previous getCrossFilterDataMask guard required
groupbyValues.length === values.length. Because flatMap on a 2D
entry expands into multiple rows, this check always failed silently for
"Other", suppressing cross-filter emission entirely (no API calls fired).

Root cause: The guard groupbyValues.length !== values.length was
too strict and did not account for the multi-row "Other" aggregation.

Fix:

  • transformProps.ts: Populate labelMap['Other'] with a 2D array
    (one string[] per aggregated row).
  • eventHandlers.ts: Replace the strict equality guard with
    length === 0 && values.length > 0, and use flatMap to normalise
    both 1D and 2D labelMap entries into a uniform string[][] before
    building the IN-filter payload.
  • types.ts: Make CrossFilterTransformedProps generic on its
    labelMap value type (default string[]) so the pie chart can
    declare string[] | string[][] without breaking other chart types.
  • Pie/types.ts: Instantiate the generic for PieChartTransformedProps.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Before Fix: When Clicked on the Other we get Null Data in Bar Chart

Screenshot from 2026-08-12 16-00-23 After Fix:When Clicked on the Other we get other data in Bar Chart Screenshot from 2026-08-12 16-04-22

TESTING INSTRUCTIONS

  1. Create a pie chart with an "Other" threshold (e.g. show top 5 categories, rest grouped as Other).
  2. Add the pie chart to a dashboard alongside a bar chart of the same dataset.
  3. Enable cross-filtering on the dashboard.
  4. Click on the "Other" slice — cross-filtering now fires correctly and filters the bar chart.

ADDITIONAL INFORMATION

  • Changes UI
  • Has associated issue:
  • Required feature flags:
  • Includes DB Migration
  • Introduces new feature or API
  • Removes existing feature or API

###Dataset Used
salesdata.xlsx

omsn2 added 2 commits August 6, 2026 14:21
…ements

Fixes invalid cross-filters being emitted when non-category pie chart
elements (Total graphic text, Other slice, empty name events) are clicked.
getCrossFilterDataMask now returns undefined when any selected value has
no labelMap entry, and clickEventHandler guards against empty name events.
Fixes apache#42340
The 'Other' slice in a pie chart aggregates multiple rows into a single
segment. Its labelMap entry is a 2D array (string[][]) — one row per
aggregated data point — whereas all other slices use a 1D array (string[]).

The previous getCrossFilterDataMask guard required groupbyValues.length
to equal values.length. Because flatMap on a 2D entry expands it into
multiple rows, this check always failed for 'Other', silently suppressing
the cross-filter emission.

Changes:
- transformProps.ts: populate labelMap['Other'] with a 2D array
  (one string[] per aggregated row) so the event handler has the
  raw dimension values available.
- eventHandlers.ts: replace the strict equality guard with a looser
  check (length === 0 && values.length > 0) and use flatMap to normalise
  both 1D and 2D labelMap entries into a uniform string[][] before
  building the IN-filter payload.
- types.ts: make CrossFilterTransformedProps generic on its labelMap
  value type (default string[]) so the pie chart can declare
  string[] | string[][] without breaking other chart types.
- Pie/types.ts: instantiate the generic for PieChartTransformedProps.
@dosubot dosubot Bot added change:frontend Requires changing the frontend dashboard:cross-filters Related to the Dashboard cross filters viz:charts:pie Related to the Pie chart labels Aug 12, 2026
@bito-code-review

bito-code-review Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #3b3edb

Actionable Suggestions - 0
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset-frontend/plugins/plugin-chart-echarts/src/utils/eventHandlers.ts - 1
Review Details
  • Files reviewed - 5 · Commit Range: f0a91dc..c12dddd
    • superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Pie/types.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/types.ts
    • 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

@netlify

netlify Bot commented Aug 12, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 4bda379
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a7f450a5ba97700088d71bd
😎 Deploy Preview https://deploy-preview-43088--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

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

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

Comment on lines +366 to +370
if (otherDatum && otherRows.length > 0) {
labelMap[otherDatum.name] = otherRows.map(row =>
groupbyLabels.map(col => row[col] as string),
);
}

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.

Suggestion: The aggregated entry is stored under the rendered name Other, so it overwrites any real data row whose formatted groupby label is also Other. Since both slices are rendered with the same ECharts name, clicking the real row or the aggregate can then resolve to the aggregated rows and emit an incorrect cross-filter. Use a collision-safe key or otherwise disambiguate the aggregate from ordinary data labels. [logic error]

Severity Level: Major ⚠️
- ❌ Real `Other` category clicks filter aggregated rows.
- ❌ Aggregate and real slices share cross-filter selection state.
- ⚠️ Pie-to-chart cross-filter results become incorrect for colliding labels.

Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts
**Line:** 366:370
**Comment:**
	*Logic Error: The aggregated entry is stored under the rendered name `Other`, so it overwrites any real data row whose formatted groupby label is also `Other`. Since both slices are rendered with the same ECharts name, clicking the real row or the aggregate can then resolve to the aggregated rows and emit an incorrect cross-filter. Use a collision-safe key or otherwise disambiguate the aggregate from ordinary data labels.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch! I have resolved this collision issue in the latest commit.

How it was fixed:

The labelMap now stores the aggregated slice using a disambiguated prefix (other${name}) to guarantee it never overwrites an actual data row named "Other".
The click and contextmenu event handlers in eventHandlers.ts have been updated to evaluate the data.isOther property injected by ECharts.
If it is the aggregated slice, the handler safely reconstructs the other key to emit the cross-filter. Real data rows named "Other" will map securely to their own native keys.

@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is valid. The current implementation uses the rendered label (e.g., 'Other') as the key in labelMap, which causes collisions when both an aggregated 'Other' slice and a real data row share the same label. This leads to incorrect cross-filtering behavior.

To resolve this, you should disambiguate the keys in labelMap. A common approach is to use a unique identifier or a composite key that includes the type of data (e.g., 'aggregate:Other' vs 'data:Other').

Since the issue is identified in superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts, you can modify the labelMap construction to include a prefix or unique identifier for aggregated rows.

Would you like me to fetch all other comments on this PR to validate and implement fixes for them as well?

superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts

if (otherDatum && otherRows.length > 0) {
    labelMap[`aggregate:${otherDatum.name}`] = otherRows.map(row =>
      groupbyLabels.map(col => row[col] as string),
    );
  }

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 67.74194% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.69%. Comparing base (9272816) to head (4bda379).

Files with missing lines Patch % Lines
...ns/plugin-chart-echarts/src/utils/eventHandlers.ts 60.00% 8 Missing ⚠️
...ins/plugin-chart-echarts/src/Pie/transformProps.ts 81.81% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #43088      +/-   ##
==========================================
- Coverage   66.69%   66.69%   -0.01%     
==========================================
  Files        2872     2872              
  Lines      163452   163468      +16     
  Branches    37725    37734       +9     
==========================================
+ Hits       109022   109028       +6     
- Misses      52302    52312      +10     
  Partials     2128     2128              
Flag Coverage Δ
javascript 73.75% <67.74%> (-0.01%) ⬇️

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.

omsn2 and others added 5 commits August 12, 2026 17:25
…ng collisions

- Applies a unique prefix '__other__' to the aggregated slice in labelMap
- Updates Pie chart cross-filtering event handlers to reconstruct the key using data.isOther
- Preserves accurate filtering behavior for real data rows named 'Other' without breaking UI highlighting
…slice key

- Aborts cross-filter emission if clicked elements lack a valid name (e.g. empty labels)
- Safely no-ops if any selected values cannot be strictly resolved in the labelMap (e.g. 'Total' text)
- Applies a unique prefix '__other__' to the aggregated slice in labelMap to prevent cross-filtering collisions
- Updates Pie chart event handlers to reconstruct the key using data.isOther, preserving accurate filtering behavior for real data rows named 'Other'
@bito-code-review

bito-code-review Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #60522c

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: c12dddd..3a9e82e
    • superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/eventHandlers.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

omsn2 and others added 4 commits August 13, 2026 07:38
Add three new unit tests to cover the __other__ key path in the
pie chart event handlers which was missing from the Codecov patch
coverage report:
1. Clicking the aggregated Other slice (isOther=true) correctly emits
   filters for all aggregated rows via the __other__ key
2. Clicking a real data row named 'Other' (no isOther flag) correctly
   emits only that single row's filter
3. Clicking the Other slice when __other__ key is missing from labelMap
   safely no-ops without emitting any cross-filter

Copilot AI 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.

Pull request overview

Enables cross-filtering for ECharts pie charts when clicking the aggregated “Other” slice by supporting multi-row label mappings and routing “Other” clicks through a dedicated __other__ key so they can be expanded into an IN filter payload.

Changes:

  • Extends cross-filter label mapping to support string[] | string[][] and normalizes it in event handlers.
  • Updates pie transform to store aggregated “Other” rows under a __other__{label} key and to persist selection/opacity using that key.
  • Adds unit tests covering clicks on pseudo-elements (“Total”), “Other” aggregation behavior, and real data rows named “Other”.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
superset-frontend/plugins/plugin-chart-echarts/test/utils/eventHandlers.test.ts Adds coverage for “Other”/pseudo-element click behavior and aggregated-row cross-filter emission.
superset-frontend/plugins/plugin-chart-echarts/src/utils/eventHandlers.ts Normalizes labelMap entries (1D/2D) and routes pie “Other” clicks via __other__ keys; updates context menu handling.
superset-frontend/plugins/plugin-chart-echarts/src/types.ts Makes CrossFilterTransformedProps generic over labelMap value type.
superset-frontend/plugins/plugin-chart-echarts/src/Pie/types.ts Instantiates the cross-filter generic for pie charts to allow `string[]
superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts Stores aggregated “Other” mappings as 2D arrays under __other__ keys and updates selection/opacity logic accordingly.
Suppressed comments (3)

superset-frontend/plugins/plugin-chart-echarts/test/utils/eventHandlers.test.ts:46

  • The test helper is still typed as CrossFilterTransformedProps (defaulting labelMap to string[]), then relies on casts to allow string[][]. Since CrossFilterTransformedProps is now generic, the helper can use CrossFilterTransformedProps<string[] | string[][]> directly and drop the labelMap cast in the returned object.

This issue also appears in the following locations of the same file:

  • line 245
  • line 281
function buildProps(
  overrides: Partial<
    BaseTransformedProps<QueryFormData> & CrossFilterTransformedProps
  >,
): BaseTransformedProps<QueryFormData> & CrossFilterTransformedProps {
  return {
    formData: {} as QueryFormData,
    height: 400,
    width: 800,
    queriesData: [],
    filterState: {},
    onContextMenu: jest.fn(),
    setDataMask: jest.fn(),
    emitCrossFilters: true,
    groupby: [],
    labelMap: {} as Record<string, string[] | string[][]>,
    selectedValues: {},
    coltypeMapping: {},
    ...overrides,
  } as BaseTransformedProps<QueryFormData> & CrossFilterTransformedProps;
}

superset-frontend/plugins/plugin-chart-echarts/test/utils/eventHandlers.test.ts:253

  • These new tests disable no-explicit-any and cast labelMap to any, even though the updated labelMap type already supports string[][]. Once the helper is typed with CrossFilterTransformedProps<string[] | string[][]>, the as any casts can be removed.
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    labelMap: {
      Electronics: ['Electronics'],
      Clothing: ['Clothing'],
      // The aggregated "Other" slice is stored under the __other__ prefix
      // to avoid colliding with any real data row named "Other".
      '__other__Other': [['SmallA'], ['SmallB']],
    } as any,
    selectedValues: {},

superset-frontend/plugins/plugin-chart-echarts/test/utils/eventHandlers.test.ts:288

  • This test also disables no-explicit-any and casts labelMap to any. With CrossFilterTransformedProps<string[] | string[][]> in the helper, the labelMap literal is already valid and can be kept strongly typed.
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    labelMap: {
      // A real data row whose category value is literally "Other"
      Other: ['Other'],
      // The aggregated slice is stored separately under the __other__ prefix
      '__other__Other': [['SmallA'], ['SmallB']],
    } as any,
    selectedValues: {},

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.


return {
value,
value: value as number,

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in the latest commit. Replaced value: value as number with value: Number(value). This forces a real runtime coercion, ensuring ECharts always receives a number even if datum[metricLabel] arrives as a string (which happens when the backend serializes numeric columns as text).

Comment on lines 143 to 171
const drillFilters: any[] = [];
const key = e.data?.isOther ? `__other__${e.name}` : e.name;
if (groupby.length > 0) {
const values = labelMap[e.name];
const values = labelMap[key];
if (!values) {
return;
}
const isMulti = Array.isArray(values[0]);
groupby.forEach((dimension, i) => {
const val = isMulti
? (values as string[][]).map(v => {
const metricsCount = v.length - groupby.length;
return v[metricsCount + i];
})
: (values as string[])[
(values as string[]).length - groupby.length + i
];
drillFilters.push({
col: dimension,
op: '==',
val: values[i],
formattedVal: formatSeriesName(values[i], {
timeFormatter: getTimeFormatter(formData.dateFormat),
numberFormatter: getNumberFormatter(formData.numberFormat),
coltype: coltypeMapping?.[getColumnLabel(dimension)],
}),
op: isMulti ? 'IN' : '==',
val,
formattedVal: isMulti
? e.name
: formatSeriesName(val as string, {
timeFormatter: getTimeFormatter(formData.dateFormat),
numberFormatter: getNumberFormatter(formData.numberFormat),
coltype: coltypeMapping?.[getColumnLabel(dimension)],
}),
});

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in the latest commit. Restored the proper drillFilters: BinaryQueryObjectFilterClause[] typing by importing it from @superset-ui/core. Also added a check: for multi-row aggregated "Other" slices, we now emit an empty drillFilters array since drill-to-detail is ambiguous for aggregations. The crossFilter object remains fully supported via the other key.

Comment on lines 183 to 186
export const allEventHandlers = (
transformedProps: BaseTransformedProps<any> & CrossFilterTransformedProps,
transformedProps: BaseTransformedProps<any> &
CrossFilterTransformedProps<any>,
) => {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in the latest commit. Removed the any widenings. The function signature now properly uses BaseTransformedProps & CrossFilterTransformedProps<string[] | string[][]>, aligning perfectly with the type of labelMap in this plugin.

@bito-code-review

bito-code-review Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #00a5b8

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: 3a9e82e..e692442
    • superset-frontend/plugins/plugin-chart-echarts/test/utils/eventHandlers.test.ts
  • Files skipped - 0
  • Tools
    • Eslint (Linter) - ✔︎ Successful
    • 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 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

- Convert pie metric value with Number() at runtime instead of 'as number'
  type cast — datum[metricLabel] can be a string when the backend
  serialises numeric columns as text
- For aggregated Other slice in contextMenuEventHandler, emit empty drill
  filters since drill-to-detail is ambiguous for multi-row aggregates;
  crossFilter is still emitted correctly via the __other__ key
- Remove 'any' from allEventHandlers: use QueryFormData and
  CrossFilterTransformedProps<string[] | string[][]> for proper typing
- Import BinaryQueryObjectFilterClause from @superset-ui/core to restore
  the correct type for drillFilters array
@bito-code-review

bito-code-review Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #18969c

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: e692442..ac915e4
    • superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/eventHandlers.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

change:frontend Requires changing the frontend dashboard:cross-filters Related to the Dashboard cross filters plugins size/L viz:charts:pie Related to the Pie chart

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants