Skip to content

[fix] Add selection support for treemap and sunburst in st.plotly_chart#13935

Merged
lukasmasuch merged 3 commits intodevelopfrom
lukasmasuch/plotly-selection-fix
Feb 17, 2026
Merged

[fix] Add selection support for treemap and sunburst in st.plotly_chart#13935
lukasmasuch merged 3 commits intodevelopfrom
lukasmasuch/plotly-selection-fix

Conversation

@lukasmasuch
Copy link
Copy Markdown
Collaborator

Describe your changes

  • Adds onClick handler to capture click events from treemap and sunburst charts
  • Hierarchical charts don't emit plotly_selected events, but do emit plotly_click events
  • Selection data sent with consistent format including id, parent, label, value, and hierarchy-specific fields

GitHub Issue Link (if applicable)

Closes #9001

Testing Plan

  • Unit Tests (JS)
  • E2E Tests (treemap and sunburst selection)

Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

Hierarchical charts (treemap, sunburst) don't emit plotly_selected events
but do emit plotly_click events. Add onClick handler that captures these
clicks and sends them as selection state for API consistency.
Copilot AI review requested due to automatic review settings February 12, 2026 21:56
@lukasmasuch lukasmasuch added change:bugfix PR contains bug fix implementation impact:users PR changes affect end users labels Feb 12, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 12, 2026

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-13935/streamlit-1.54.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-13935.streamlit.app (☁️ Deploy here if not accessible)

@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Feb 12, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@lukasmasuch lukasmasuch changed the title [fix] add selection support for treemap and sunburst charts [fix] Add selection support for treemap and sunburst charts Feb 12, 2026
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

This pull request adds click event selection support for hierarchical Plotly charts (treemap and sunburst) to fix issue #9001. These chart types don't emit plotly_selected events like other charts, but they do emit plotly_click events that can be leveraged to provide the same selection functionality.

Changes:

  • Added handleClickEvent function to process click events from hierarchical charts and convert them to selection state
  • Integrated the click handler into the PlotlyChart component with proper activation guards
  • Added comprehensive unit tests and E2E tests for both treemap and sunburst chart selection

Reviewed changes

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

Show a summary per file
File Description
frontend/lib/src/components/elements/PlotlyChart/utils.ts Added handleClickEvent function to handle click events from hierarchical charts and emit selection data in a format consistent with other chart types
frontend/lib/src/components/elements/PlotlyChart/PlotlyChart.tsx Integrated the click handler with proper conditional activation based on isSelectionActivated
frontend/lib/src/components/elements/PlotlyChart/utils.test.ts Added comprehensive unit tests covering early returns, treemap/sunburst clicks, undefined pointNumber handling, and deduplication
e2e_playwright/st_plotly_chart_select.py Added test app sections for treemap and sunburst charts with selection enabled
e2e_playwright/st_plotly_chart_select_test.py Added parametrized E2E test verifying click interaction and selection data emission for both chart types

@github-actions
Copy link
Copy Markdown
Contributor

Consolidated Code Review

Summary

This PR adds on_select support for hierarchical Plotly chart types (treemap and sunburst) that don't emit native plotly_selected events but do emit plotly_click events. The implementation intercepts click events, detects hierarchical chart clicks by checking for id and parent properties on the clicked point, and routes them through the existing widget state mechanism as selection data. This is a targeted bugfix (closes #9001) with no changes to protobuf definitions or backend Python code.

Key changes:

  • utils.ts: New handleClickEvent function that processes click events from hierarchical charts, formats the data consistently with the existing selection state schema, and avoids redundant state updates via deduplication.
  • PlotlyChart.tsx: Adds a handleClickCallback via useCallback and wires it as onClick on the Plotly component, gated behind isSelectionActivated.
  • utils.test.ts: Unit tests covering early returns, successful processing, undefined pointNumber, and deduplication.
  • E2E tests: App script with treemap and sunburst charts, and a parameterized test that clicks on chart segments and verifies selection data appears.

Code Quality

Both reviewers agree the implementation is clean and follows existing Plotly selection patterns well. The handleClickEvent function mirrors the structure of handleSelection (guard clause, state construction, deduplication check before committing). The useCallback in the component follows the same eslint-disable pattern used by the neighboring handleSelectionCallback.

Selection mode gating (reviewers disagreed)

gpt-5.3-codex-high flagged that onClick is gated on isSelectionActivated (any non-empty selection_mode) rather than isPointsSelectionActivated, meaning a chart configured with selection_mode=["box"] could still emit click-based selections. opus-4.6-thinking acknowledged this but considered the overhead negligible and the guard clause in handleClickEvent sufficient.

Consolidated assessment: The concern is technically valid -- if a hierarchical chart were configured with only box/lasso modes (no "points"), click-based selections would still fire. However, this is a minor issue in practice because:

  1. Box and lasso modes don't meaningfully apply to treemap/sunburst charts, so configuring them without "points" is an unlikely edge case.
  2. The E2E test app correctly uses selection_mode="points", which is the expected real-world configuration.
  3. The fix is trivial (change isSelectionActivated to isPointsSelectionActivated for the onClick prop) and would be a good defensive improvement, but is not blocking.

Recommendation (non-blocking): Gate the onClick handler on isPointsSelectionActivated instead of isSelectionActivated for consistency with how clickmode is already handled at line 434: clickmode = isPointsSelectionActivated ? "event+select" : "none".

Other code observations (both reviewers agree):

  • The any cast on event.points[0] is consistent with existing codebase style and annotated appropriately.
  • The id/parent guard heuristic is a reasonable approach for detecting hierarchical chart types.
  • Missing dependency array comment on handleClickCallback (the neighboring handleSelectionCallback has an explanatory comment for why element.id is used instead of element). Minor consistency issue.

Test Coverage

Both reviewers agree test coverage is strong for the primary scenarios:

  • Unit tests cover early returns (undefined event, empty points, non-hierarchical clicks), successful treemap/sunburst processing, undefined pointNumber edge case, and deduplication.
  • E2E tests verify initial "no selection" state and post-click selection state for both chart types.

Areas for improvement (both reviewers broadly agree):

  1. E2E locators are fragile (opus-4.6-thinking): The test uses index-based locators (app.get_by_test_id("stPlotlyChart").nth(7)) instead of the available key-based locators. Since the charts have keys (treemap_chart, sunburst_chart) and get_element_by_key is already imported in the test file, using get_element_by_key(app, "treemap_chart") would be significantly more robust and aligned with the project's E2E testing best practices.

  2. Missing negative test for selection_mode exclusion (gpt-5.3-codex-high): No test verifies that click-based hierarchical selection is ignored when POINTS mode is not in selectionMode. This would be relevant if the gating recommendation above is implemented.

  3. No double-click reset test (opus-4.6-thinking): The onDoubleClick handler is already wired up but not tested for hierarchical chart types.

  4. Generic text assertions (opus-4.6-thinking): app.get_by_text("Selected:") and app.get_by_text("ID:") could match other elements; more specific or scoped assertions would be more robust.

Backwards Compatibility

Both reviewers agree: This change is fully backwards compatible.

  • No protobuf or backend Python changes.
  • The onClick handler is only active when isSelectionActivated is true.
  • For non-hierarchical charts, the guard clause returns early without side effects.
  • The selection data format follows the existing PlotlyWidgetState schema.

Security & Risk

Both reviewers agree: Low risk. All changes are frontend-only, processing client-side Plotly click events through the existing widget state mechanism. No new network calls, no new data sources, and no server-side changes.

Accessibility

Both reviewers agree: No accessibility concerns. No new interactive UI elements are introduced; the change leverages existing Plotly chart interaction patterns.

Recommendations

Ordered by priority:

  1. (Recommended) Use key-based locators in E2E tests: Replace app.get_by_test_id("stPlotlyChart").nth(7) with get_element_by_key(app, "treemap_chart") (and similarly for sunburst). This follows documented best practices and is resilient to future changes.

  2. (Recommended) Gate onClick on isPointsSelectionActivated instead of isSelectionActivated for semantic consistency with how clickmode is already handled.

  3. (Nice to have) Add the dependency array comment to handleClickCallback for consistency with handleSelectionCallback.

  4. (Nice to have) Add a double-click reset test for hierarchical charts.

  5. (Nice to have) Consider documenting/testing icicle chart support, which would also work via the same mechanism.

Verdict

APPROVED -- A clean, well-structured bugfix that correctly adds selection support for hierarchical Plotly charts. The implementation follows existing patterns, includes good test coverage, and poses minimal regression risk. The E2E locator fragility and selection_mode gating are real but non-blocking issues that can be addressed in this PR or as a follow-up. Both reviewers agree on the overall quality of the implementation.


Consolidated review by opus-4.6-thinking.


📋 Review by `gpt-5.3-codex-high`

Summary

This PR adds on_select support for hierarchical Plotly charts (treemap and sunburst) by wiring plotly_click into Streamlit's selection state. It introduces a new frontend utility (handleClickEvent), updates the Plotly component to register click handlers, and adds both unit and e2e coverage for treemap/sunburst selection behavior.

Code Quality

The implementation is generally clean and follows existing Plotly selection patterns, but there is one behavioral inconsistency that should be fixed before merge:

  • Selection mode gating bug: In frontend/lib/src/components/elements/PlotlyChart/PlotlyChart.tsx (around lines 488-491), onClick is enabled whenever isSelectionActivated is true (any non-empty selection_mode). In frontend/lib/src/components/elements/PlotlyChart/utils.ts (around lines 347-352), handleClickEvent only checks for hierarchical point shape (id + parent) and does not verify that POINTS mode is enabled.
    This means charts configured with selection_mode=["box"] or selection_mode=["lasso"] can still emit click-based point selections on treemap/sunburst, which violates the expected semantics of selection_mode.

Test Coverage

Coverage is strong for the primary happy path:

  • Added unit tests for handleClickEvent in frontend/lib/src/components/elements/PlotlyChart/utils.test.ts.
  • Added e2e tests for treemap/sunburst click selection in e2e_playwright/st_plotly_chart_select_test.py.

However, an important negative case is missing:

  • No test verifies that click-based hierarchical selection is ignored when POINTS mode is not selected.

Backwards Compatibility

Most changes are additive and preserve existing behavior for default on_select usage.
The exception is the selection-mode inconsistency above: users who intentionally disable point selection may now still get point-like reruns on hierarchical charts.

Security & Risk

No direct security concerns identified.
Main risk is behavioral regression in event semantics (selection_mode) that can cause unexpected reruns and callback triggers.

Accessibility

No accessibility regressions identified in this PR. The change is event wiring in an existing Plotly component and does not introduce new custom interactive DOM controls.

Recommendations

  1. Gate hierarchical click handling on POINTS mode (e.g., attach onClick only when isPointsSelectionActivated, or early-return in handleClickEvent when element.selectionMode does not include POINTS).
  2. Add a unit test in frontend/lib/src/components/elements/PlotlyChart/utils.test.ts asserting handleClickEvent does not call setStringValue when selectionMode excludes POINTS.
  3. Add an e2e anti-regression case in e2e_playwright/st_plotly_chart_select_test.py for a hierarchical chart configured without "points" mode, asserting no selection output appears.

Verdict

CHANGES REQUESTED: The hierarchical click fix is directionally correct, but selection-mode semantics are currently violated for non-points configurations and should be corrected before merge.


This is an automated AI review by gpt-5.3-codex-high. Please verify the feedback and use your judgment.

📋 Review by `opus-4.6-thinking`

Summary

This PR adds selection support for hierarchical Plotly chart types (treemap and sunburst) that don't emit native plotly_selected events but do emit plotly_click events. The implementation intercepts click events, detects hierarchical chart clicks by checking for id and parent properties on the clicked point, and routes them through the existing widget state mechanism as selection data. This is a targeted bugfix (closes #9001) with no changes to protobuf definitions or backend Python code.

Key changes:

  • utils.ts: New handleClickEvent function that processes click events from hierarchical charts, formats the data consistently with the existing selection state schema, and avoids redundant state updates.
  • PlotlyChart.tsx: Adds an onClick handler wired via useCallback, gated behind isSelectionActivated.
  • utils.test.ts: Unit tests covering early returns, successful processing, undefined pointNumber, and deduplication.
  • E2E tests: App script with treemap and sunburst charts, and a parameterized test that clicks on chart segments and verifies selection data appears.

Code Quality

The implementation follows existing patterns well. The handleClickEvent function mirrors the structure of handleSelection (guard clause, state construction, deduplication check before committing). The useCallback in the component follows the same eslint-disable pattern used by the neighboring handleSelectionCallback.

Minor observations:

  1. utils.ts line 345 (const point = event.points[0] as any): The any cast is annotated with an eslint-disable comment, consistent with the existing codebase style. The PR only takes the first point from the click event. For treemap/sunburst, only one segment can be clicked at a time, so this is correct behavior.

  2. utils.ts lines 349-352 — The guard clause point.id === undefined || point.parent === undefined is a heuristic to distinguish hierarchical chart clicks from regular chart clicks. This is a reasonable approach since id and parent are specific to hierarchical chart types in Plotly's event model. It would also correctly handle Plotly's icicle chart type (another hierarchical chart) — though this isn't documented or tested.

  3. PlotlyChart.tsx line 491 — The onClick handler is attached to ALL charts when selection is activated, not just hierarchical ones. While the guard clause in handleClickEvent prevents side effects for non-hierarchical charts, this adds a minor overhead (function call + guard check) for every click on any Plotly chart with selection enabled. This is negligible and the alternative (detecting chart type at the component level) would be more complex.

  4. PlotlyChart.tsx line 360 — The eslint-disable comment for react-hooks/exhaustive-deps uses element.id instead of element in the dependency array, matching the pattern of the neighboring handleSelectionCallback. The comment explaining why is missing for this callback (it exists on line 348-349 for handleSelectionCallback).

Test Coverage

Unit tests (utils.test.ts):

  • Good use of it.each for parameterized early-return cases (undefined event, empty points, non-hierarchical click).
  • Tests cover the core happy path (treemap/sunburst click with full data), edge case (undefined pointNumber), and deduplication (unchanged state).
  • Tests follow the positive + negative assertion pattern: verifying setStringValue is NOT called for early returns and IS called for valid events.

E2E tests (st_plotly_chart_select_test.py):

  • Tests use parameterized approach for treemap (index 7) and sunburst (index 8).
  • The test verifies both the initial "no selection" state and the post-click selection state, which is good.
  • Includes a negative assertion (not_to_be_attached for the "No selection" text).

Suggestions for improved coverage:

  1. E2E locators: The test uses app.get_by_test_id("stPlotlyChart").nth(7) and .nth(8) — index-based locators are fragile. Since the charts have keys (treemap_chart, sunburst_chart), using get_element_by_key(app, "treemap_chart").get_by_test_id("stPlotlyChart") would be more robust and aligned with the E2E testing best practices documented in e2e_playwright/AGENTS.md.
  2. Double-click reset: There's no E2E or unit test verifying that double-clicking a hierarchical chart properly resets the selection (via the existing onDoubleClick handler). This is a common user interaction that should be covered.
  3. E2E text assertions: app.get_by_text("Selected:") and app.get_by_text("ID:") are generic and could match other elements on the page. More specific assertions like app.get_by_text(re.compile(r"Selected: .+")) or checking within a scoped container would be more robust.

Backwards Compatibility

This change is fully backwards compatible:

  • No protobuf changes.
  • No Python backend changes.
  • The onClick handler is only active when isSelectionActivated is true (same gate as existing selection handlers).
  • For non-hierarchical charts, the guard clause in handleClickEvent returns early without any side effects.
  • The selection data format follows the existing PlotlyWidgetState schema.

Security & Risk

  • Low risk: The change only processes client-side Plotly click events and routes them through the existing widget state mechanism. No new network calls, no new data sources, no user input parsing beyond what Plotly provides.
  • Regression risk: The onClick handler is now attached to all Plotly charts with selection enabled. If a future Plotly version adds id/parent to non-hierarchical chart click events, it could cause unexpected selection behavior. However, this is unlikely and the guard clause provides a reasonable defense.
  • No server-side changes: All changes are frontend-only, limiting the blast radius.

Accessibility

  • No new interactive UI elements are added; the change leverages existing Plotly chart interaction patterns (clicking on chart segments).
  • The selection behavior is consistent with the existing selection model, so assistive technology behavior should be unchanged.
  • No accessibility concerns identified.

Recommendations

  1. Use key-based locators in E2E tests: Replace app.get_by_test_id("stPlotlyChart").nth(7) with get_element_by_key(app, "treemap_chart").get_by_test_id("stPlotlyChart") (and similarly for sunburst). This follows the documented best practices and is more resilient to future changes in the test app.

  2. Add a double-click reset test: Add an E2E test (or extend the parameterized test) to verify that double-clicking a hierarchical chart resets the selection, since the onDoubleClick handler is already wired up but not tested for this chart type.

  3. Add a comment on the handleClickCallback dependency array: The neighboring handleSelectionCallback has a comment explaining why element.id is used instead of element. Add the same comment to handleClickCallback for consistency:

    // We are using element.id here instead of element since
    // shallow reference equality will not work correctly for element.
  4. Consider documenting icicle chart support: The handleClickEvent function will also work for Plotly's icicle charts (another hierarchical type with id/parent properties). Consider mentioning this in the comments or adding an icicle test case to validate.

Verdict

APPROVED: A clean, well-structured bugfix that correctly adds selection support for hierarchical Plotly charts. The implementation follows existing patterns, includes good test coverage, and poses minimal regression risk. The recommendations above are minor improvements that could be addressed in a follow-up.


This is an automated AI review by opus-4.6-thinking.

@lukasmasuch lukasmasuch marked this pull request as draft February 12, 2026 22:14
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

@lukasmasuch lukasmasuch changed the title [fix] Add selection support for treemap and sunburst charts [fix] Add selection support for treemap and sunburst in st.plotly_chart Feb 12, 2026
Split parameterized treemap/sunburst test into separate functions for
clarity. Use isPointsSelectionActivated instead of isSelectionActivated
for onClick handler to properly scope click events.
Reword comments to clearly explain why element.id is used instead of
element in useCallback dependency arrays: the proto object gets a new
reference on each render, but element.id only changes when the element
actually changes.
@sfc-gh-lmasuch sfc-gh-lmasuch marked this pull request as ready for review February 13, 2026 20:20
@lukasmasuch lukasmasuch merged commit 0f026e6 into develop Feb 17, 2026
43 of 44 checks passed
@lukasmasuch lukasmasuch deleted the lukasmasuch/plotly-selection-fix branch February 17, 2026 21:25
lukasmasuch added a commit that referenced this pull request Feb 20, 2026
…art` (#13935)

## Describe your changes

- Adds `onClick` handler to capture click events from treemap and
sunburst charts
- Hierarchical charts don't emit `plotly_selected` events, but do emit
`plotly_click` events
- Selection data sent with consistent format including `id`, `parent`,
`label`, `value`, and hierarchy-specific fields

## GitHub Issue Link (if applicable)

Closes #9001

## Testing Plan

- [x] Unit Tests (JS)
- [x] E2E Tests (treemap and sunburst selection)

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:bugfix PR contains bug fix implementation impact:users PR changes affect end users

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Returning data from plot interactions doesn't work with Plotly Treemaps

3 participants