Dashboard Chart Interactions#
Overview#
Dashboard chart interactions in Superset allow clicking a data point in one chart to apply a cross-filter that narrows other charts in scope. The system has three distinct responsibilities:
- Cross-filter emission — translating a click event into a
DataMaskthat updates dashboard Redux state, causing filtered charts to re-query. - Visual highlighting — immediately dimming unselected data points on the emitting chart via ECharts
highlight/downplayactions, without re-rendering. - Scope configuration — determining which charts on the dashboard are affected by a given chart's cross-filter.
These are implemented across four primary files:
utils/eventHandlers.ts— click-to-dataMask logiccomponents/Echart.tsx— highlight/downplay dispatchsrc/dashboard/util/crossFilters.ts— scope resolutiontypes.ts— shared type contracts
Cross-Filter Emission (Click → DataMask)#
The entry point for cross-filtering is allEventHandlers in eventHandlers.ts, which wires ECharts click and contextmenu event handlers onto charts that have groupby columns configured.
Click flow:
clickEventHandler is a no-op unless emitCrossFilters is true — a per-chart toggle set in the dashboard edit UI. When enabled, it calls getCrossFilterDataMask(name)?.dataMask and dispatches it via setDataMask.
getCrossFilterDataMask implements toggle semantics: clicking an already-selected value removes it from the selection; clicking an unselected value replaces the selection with just that value. It produces:
dataMask.extraFormData.filters— oneINfilter clause pergroupbycolumn, usinglabelMapto resolve multi-column groupby values by index.dataMask.filterState—{ value: groupbyValues, selectedValues: values }for downstream chart state.
Context menu flow:
contextMenuEventHandler surfaces three options to the dashboard context menu: drillToDetail, crossFilter (the same dataMask as the click path), and drillBy.
Visual Highlighting (highlight / downplay)#
Selection state is reflected on the ECharts canvas through direct action dispatch — not a setOption re-render — keeping the interaction fast.
Data flow into Echart.tsx:
selectedValues (Record<number, string>) maps a data index to its series name for each currently selected item . This prop is populated by each chart's transformProps from the dashboard Redux filterState.selectedValues, and passed down to <Echart>.
Diff and dispatch:
Inside Echart.tsx, currentSelection is memoized as Object.keys(selectedValues) and compared against previousSelection (a useRef) in a dedicated useEffect:
downplayis dispatched for indices that were inpreviousSelectionbut are not incurrentSelection— dimming items that left the selection.highlightis dispatched for all indices incurrentSelection— visually emphasizing selected items.previousSelection.currentis updated tocurrentSelection.
Both actions use dataIndex (an array of index strings) as the targeting mechanism, which maps to ECharts' native series data indices. This sidesteps a full setOption call on every filter interaction.
Filter Scope Configuration#
Which charts a cross-filter affects is computed by getCrossFiltersConfiguration in crossFilters.ts.
Only chart types with Behavior.InteractiveChart in their metadata are eligible to emit cross-filters. The function iterates the dashboard layout, checks each chart's registered behaviors, and resolves its scope:
- Global scope (
GLOBAL_SCOPE_POINTER): the chart's filter applies to all other charts on the dashboard, usingglobal_chart_configuration.scopefrom dashboard metadata. - Custom scope: a
NativeFilterScopestored per-chart inmetadata.chart_configuration[chartId], resolved to a list of chart IDs viagetChartIdsInFilterScope. - Default: if a chart hasn't had its scope saved,
DEFAULT_CROSS_FILTER_SCOPINGis used and all charts are in scope.
The resulting chartConfiguration object (keyed by chart ID) contains crossFilters.chartsInScope — the list of chart IDs that will re-query when this chart emits a filter. This is stored in dashboard metadata and re-computed on dashboard save via saveDashboardRequest.
isCrossFiltersEnabled reads metadataCrossFiltersEnabled and defaults to true when the flag is undefined, so cross-filtering is on by default.
Key Types and Data Flow#
CrossFilterTransformedProps — the contract between transformProps and eventHandlers:
groupby: columns used to build filter clauseslabelMap: maps series display name → raw column values per groupby indexsetDataMask: Redux dispatch hook for publishing the selectionselectedValues: current selection state (index → name)emitCrossFilters: per-chart toggle enabling cross-filter emission
EchartsProps — what <Echart> receives, including selectedValues?: Record<number, string> for highlight/downplay.
End-to-end flow:
User click
→ ECharts 'click' event
→ clickEventHandler (emitCrossFilters gate)
→ getCrossFilterDataMask → DataMask
→ setDataMask (Redux dispatch)
→ dashboard dataMask state updates
→ cross-scoped charts re-query
→ filterState.selectedValues flows back via transformProps
→ EchartsProps.selectedValues updates
→ highlight/downplay useEffect diffs and dispatches ECharts actions
labelMap is constructed during transformProps for each chart type, mapping the ECharts series name field to the underlying raw groupby column values. This is what enables getCrossFilterDataMask to emit correctly typed IN filter clauses for multi-column groupby.
Key Files#
| File | Purpose |
|---|---|
plugins/plugin-chart-echarts/src/utils/eventHandlers.ts | getCrossFilterDataMask, clickEventHandler, allEventHandlers — click-to-dataMask logic |
plugins/plugin-chart-echarts/src/components/Echart.tsx | Shared ECharts wrapper — highlight/downplay useEffect, selectedValues diffing |
plugins/plugin-chart-echarts/src/types.ts | CrossFilterTransformedProps, EchartsProps, BaseTransformedProps |
src/dashboard/util/crossFilters.ts | Scope resolution — getCrossFiltersConfiguration, isCrossFiltersEnabled |
src/dashboard/util/crossFilters.test.ts | Tests for scope configuration logic |