diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/EchartsGauge.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/EchartsGauge.tsx index 5f57bad421f9..731aadded839 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/EchartsGauge.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/EchartsGauge.tsx @@ -16,10 +16,76 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; -import { EchartsProps } from '../types'; +import React, { useCallback } from 'react'; +import { GaugeChartTransformedProps } from './types'; import Echart from '../components/Echart'; +import { EventHandlers } from '../types'; -export default function EchartsGauge({ height, width, echartOptions }: EchartsProps) { - return ; +export default function EchartsGauge({ + height, + width, + echartOptions, + setDataMask, + labelMap, + groupby, + selectedValues, + formData: { emitFilter }, +}: GaugeChartTransformedProps) { + const handleChange = useCallback( + (values: string[]) => { + if (!emitFilter) { + return; + } + + const groupbyValues = values.map(value => labelMap[value]); + + setDataMask({ + extraFormData: { + filters: + values.length === 0 + ? [] + : groupby.map((col, idx) => { + const val = groupbyValues.map(v => v[idx]); + if (val === null || val === undefined) + return { + col, + op: 'IS NULL', + }; + return { + col, + op: 'IN', + val: val as (string | number | boolean)[], + }; + }), + }, + filterState: { + value: groupbyValues.length ? groupbyValues : null, + selectedValues: values.length ? values : null, + }, + }); + }, + [groupby, labelMap, setDataMask, selectedValues], + ); + + const eventHandlers: EventHandlers = { + click: props => { + const { name } = props; + const values = Object.values(selectedValues); + if (values.includes(name)) { + handleChange(values.filter(v => v !== name)); + } else { + handleChange([name]); + } + }, + }; + + return ( + + ); } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx index 2866dcd36122..56e21d4b11fe 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx @@ -17,7 +17,13 @@ * under the License. */ import React from 'react'; -import { t, validateNonEmpty, validateInteger } from '@superset-ui/core'; +import { + t, + validateNonEmpty, + validateInteger, + isFeatureEnabled, + FeatureFlag, +} from '@superset-ui/core'; import { sharedControls, ControlPanelConfig, @@ -121,6 +127,20 @@ const config: ControlPanelConfig = { }, ], ['color_scheme'], + isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS) + ? [ + { + name: 'emit_filter', + config: { + type: 'CheckboxControl', + label: t('Enable emitting filters'), + default: DEFAULT_FORM_DATA.emitFilter, + renderTrigger: true, + description: t('Enable emmiting filters.'), + }, + }, + ] + : [], [ { name: 'font_size', diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/index.ts index f32d67643d23..51a8e4f74ae8 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/index.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/index.ts @@ -21,8 +21,12 @@ import controlPanel from './controlPanel'; import transformProps from './transformProps'; import thumbnail from './images/thumbnail.png'; import buildQuery from './buildQuery'; +import { EchartsGaugeChartProps, EchartsGaugeFormData } from './types'; -export default class EchartsGaugeChartPlugin extends ChartPlugin { +export default class EchartsGaugeChartPlugin extends ChartPlugin< + EchartsGaugeFormData, + EchartsGaugeChartProps +> { constructor() { super({ buildQuery, diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts index 85209054eaa3..ccd099376b05 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts @@ -18,12 +18,12 @@ */ import { QueryFormMetric, - ChartProps, CategoricalColorNamespace, CategoricalColorScale, DataRecord, getNumberFormatter, getMetricLabel, + DataRecordValue, } from '@superset-ui/core'; import { EChartsOption, GaugeSeriesOption } from 'echarts'; import { GaugeDataItemOption } from 'echarts/types/src/chart/gauge/GaugeSeries'; @@ -33,6 +33,8 @@ import { DEFAULT_FORM_DATA as DEFAULT_GAUGE_FORM_DATA, EchartsGaugeFormData, AxisTickLineStyle, + GaugeChartTransformedProps, + EchartsGaugeChartProps, } from './types'; import { DEFAULT_GAUGE_SERIES_OPTION, @@ -40,6 +42,7 @@ import { OFFSETS, FONT_SIZE_MULTIPLIERS, } from './constants'; +import { OpacityEnum } from '../constants'; const setIntervalBoundsAndColors = ( intervals: string, @@ -71,8 +74,10 @@ const setIntervalBoundsAndColors = ( const calculateAxisLineWidth = (data: DataRecord[], fontSize: number, overlap: boolean): number => overlap ? fontSize : data.length * fontSize; -export default function transformProps(chartProps: ChartProps) { - const { width, height, formData, queriesData } = chartProps; +export default function transformProps( + chartProps: EchartsGaugeChartProps, +): GaugeChartTransformedProps { + const { width, height, formData, queriesData, hooks, filterState } = chartProps; const { groupby, metric, @@ -94,6 +99,7 @@ export default function transformProps(chartProps: ChartProps) { intervals, intervalColorIndices, valueFormatter, + emitFilter, }: EchartsGaugeFormData = { ...DEFAULT_GAUGE_FORM_DATA, ...formData }; const data = (queriesData[0]?.data || []) as DataRecord[]; const numberFormatter = getNumberFormatter(numberFormat); @@ -115,24 +121,51 @@ export default function transformProps(chartProps: ChartProps) { colorFn, normalizer, ); - const transformedData: GaugeDataItemOption[] = data.map((data_point, index) => ({ - value: data_point[getMetricLabel(metric as QueryFormMetric)] as number, - name: groupby.map(column => `${column}: ${data_point[column]}`).join(', '), - itemStyle: { - color: colorFn(index), - }, - title: { - offsetCenter: ['0%', `${index * titleOffsetFromTitle + OFFSETS.titleFromCenter}%`], - fontSize, - }, - detail: { - offsetCenter: [ - '0%', - `${index * titleOffsetFromTitle + OFFSETS.titleFromCenter + detailOffsetFromTitle}%`, - ], - fontSize: FONT_SIZE_MULTIPLIERS.detailFontSize * fontSize, - }, - })); + const columnsLabelMap = new Map(); + + const transformedData: GaugeDataItemOption[] = data.map((data_point, index) => { + const name = groupby.map(column => `${column}: ${data_point[column]}`).join(', '); + columnsLabelMap.set( + name, + groupby.map(col => data_point[col]), + ); + let item: GaugeDataItemOption = { + value: data_point[getMetricLabel(metric as QueryFormMetric)] as number, + name, + itemStyle: { + color: colorFn(index), + }, + title: { + offsetCenter: ['0%', `${index * titleOffsetFromTitle + OFFSETS.titleFromCenter}%`], + fontSize, + }, + detail: { + offsetCenter: [ + '0%', + `${index * titleOffsetFromTitle + OFFSETS.titleFromCenter + detailOffsetFromTitle}%`, + ], + fontSize: FONT_SIZE_MULTIPLIERS.detailFontSize * fontSize, + }, + }; + if (filterState.selectedValues && !filterState.selectedValues.includes(name)) { + item = { + ...item, + itemStyle: { + color: colorFn(index), + opacity: OpacityEnum.SemiTransparent, + }, + detail: { + show: false, + }, + title: { + show: false, + }, + }; + } + return item; + }); + + const { setDataMask = () => {} } = hooks; const progress = { show: showProgress, @@ -223,8 +256,14 @@ export default function transformProps(chartProps: ChartProps) { }; return { + formData, width, height, echartOptions, + setDataMask, + emitFilter, + labelMap: Object.fromEntries(columnsLabelMap), + groupby, + selectedValues: filterState.selectedValues || [], }; } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/types.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/types.ts index 42b579b4fc8e..90058af167c9 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/types.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Gauge/types.ts @@ -16,14 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -import { DEFAULT_LEGEND_FORM_DATA } from '../types'; +import { ChartDataResponseResult, ChartProps, QueryFormData } from '@superset-ui/core'; +import { DEFAULT_LEGEND_FORM_DATA, EChartTransformedProps } from '../types'; export type AxisTickLineStyle = { width: number; color: string; }; -export type EchartsGaugeFormData = { +export type EchartsGaugeFormData = QueryFormData & { colorScheme?: string; groupby: string[]; metric?: object; @@ -45,9 +46,10 @@ export type EchartsGaugeFormData = { intervals: string; intervalColorIndices: string; valueFormatter: string; + emitFilter: boolean; }; -export const DEFAULT_FORM_DATA: EchartsGaugeFormData = { +export const DEFAULT_FORM_DATA: Partial = { ...DEFAULT_LEGEND_FORM_DATA, groupby: [], rowLimit: 10, @@ -68,4 +70,12 @@ export const DEFAULT_FORM_DATA: EchartsGaugeFormData = { intervals: '', intervalColorIndices: '', valueFormatter: '{value}', + emitFilter: false, }; + +export interface EchartsGaugeChartProps extends ChartProps { + formData: EchartsGaugeFormData; + queriesData: ChartDataResponseResult[]; +} + +export type GaugeChartTransformedProps = EChartTransformedProps; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/types.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/types.ts index 271b2d0436c1..029aec30354e 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/types.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/types.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { DataRecordValue, SetDataMaskHook } from '@superset-ui/core'; import { EChartsOption } from 'echarts'; import { TooltipMarker } from 'echarts/types/src/util/format'; @@ -96,3 +97,15 @@ export enum LabelPositionEnum { InsideTopRight = 'insideTopRight', InsideBottomRight = 'insideBottomRight', } + +export interface EChartTransformedProps { + formData: F; + height: number; + width: number; + echartOptions: EChartsOption; + emitFilter: boolean; + setDataMask: SetDataMaskHook; + labelMap: Record; + groupby: string[]; + selectedValues: Record; +}