diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/EchartsMixedTimeseries.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/EchartsMixedTimeseries.tsx index 71c66d2e5853b..ae1a1f3c70e86 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/EchartsMixedTimeseries.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/EchartsMixedTimeseries.tsx @@ -16,10 +16,92 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; -import { EchartsProps } from '../types'; +import React, { useCallback } from 'react'; +import { EchartsMixedTimeseriesChartTransformedProps } from './types'; import Echart from '../components/Echart'; +import { EventHandlers } from '../types'; -export default function EchartsMixedTimeseries({ height, width, echartOptions }: EchartsProps) { - return ; +export default function EchartsMixedTimeseries({ + height, + width, + echartOptions, + setDataMask, + labelMap, + labelMapB, + groupby, + groupbyB, + selectedValues, + formData, + seriesBreakdown, +}: EchartsMixedTimeseriesChartTransformedProps) { + const isFirstQuery = useCallback((seriesIndex: number) => seriesIndex < seriesBreakdown, [ + seriesBreakdown, + ]); + + const handleChange = useCallback( + (values: string[], seriesIndex: number) => { + const emitFilter = isFirstQuery(seriesIndex) ? formData.emitFilter : formData.emitFilterB; + if (!emitFilter) { + return; + } + + const currentGroupBy = isFirstQuery(seriesIndex) ? groupby : groupbyB; + const currentLabelMap = isFirstQuery(seriesIndex) ? labelMap : labelMapB; + const groupbyValues = values.map(value => currentLabelMap[value]).filter(value => !!value); + + setDataMask({ + extraFormData: { + // @ts-ignore + filters: + values.length === 0 + ? [] + : [ + ...currentGroupBy.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 ? null : groupbyValues, + selectedValues: values.length ? values : null, + }, + }); + }, + [groupby, groupbyB, labelMap, labelMapB, setDataMask, selectedValues], + ); + + const eventHandlers: EventHandlers = { + click: props => { + const { seriesName, seriesIndex } = props; + const values: string[] = Object.values(selectedValues); + if (values.includes(seriesName)) { + handleChange( + values.filter(v => v !== seriesName), + seriesIndex, + ); + } else { + handleChange([seriesName], seriesIndex); + } + }, + }; + + return ( + + ); } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx index 11d91f862a6ca..7f3f03d88bb61 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx @@ -22,6 +22,7 @@ import { ControlPanelConfig, ControlPanelSectionConfig, ControlSetRow, + emitFilterControl, sections, sharedControls, } from '@superset-ui/chart-controls'; @@ -72,6 +73,14 @@ function createQuerySection(label: string, controlSuffix: string): ControlPanelS config: sharedControls.adhoc_filters, }, ], + emitFilterControl.length > 0 + ? [ + { + ...emitFilterControl[0], + name: `emit_filter${controlSuffix}`, + }, + ] + : [], [ { name: `limit${controlSuffix}`, diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/index.ts index a4158966b113f..a96487c8eec1b 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/index.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/index.ts @@ -16,13 +16,17 @@ * specific language governing permissions and limitations * under the License. */ -import { t, ChartMetadata, ChartPlugin, AnnotationType } from '@superset-ui/core'; +import { t, ChartMetadata, ChartPlugin, AnnotationType, Behavior } from '@superset-ui/core'; import buildQuery from './buildQuery'; import controlPanel from './controlPanel'; import transformProps from './transformProps'; import thumbnail from './images/thumbnail.png'; +import { EchartsMixedTimeseriesProps, EchartsMixedTimeseriesFormData } from './types'; -export default class EchartsTimeseriesChartPlugin extends ChartPlugin { +export default class EchartsTimeseriesChartPlugin extends ChartPlugin< + EchartsMixedTimeseriesFormData, + EchartsMixedTimeseriesProps +> { /** * The constructor is used to pass relevant metadata and callbacks that get * registered in respective registries that are used throughout the library @@ -39,6 +43,7 @@ export default class EchartsTimeseriesChartPlugin extends ChartPlugin { controlPanel, loadChart: () => import('./EchartsMixedTimeseries'), metadata: new ChartMetadata({ + behaviors: [Behavior.INTERACTIVE_CHART], category: t('Evolution'), credits: ['https://echarts.apache.org'], description: t( @@ -64,6 +69,7 @@ export default class EchartsTimeseriesChartPlugin extends ChartPlugin { t('Transformable'), ], }), + // @ts-ignore transformProps, }); } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts index 8079b0ddfa2c6..636921ee43571 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts @@ -20,7 +20,8 @@ import { AnnotationLayer, CategoricalColorNamespace, - ChartProps, + DataRecordValue, + TimeseriesDataRecord, getNumberFormatter, isEventAnnotationLayer, isFormulaAnnotationLayer, @@ -28,8 +29,12 @@ import { isTimeseriesAnnotationLayer, } from '@superset-ui/core'; import { EChartsOption, SeriesOption } from 'echarts'; -import { DEFAULT_FORM_DATA, EchartsMixedTimeseriesFormData } from './types'; -import { EchartsProps, ForecastSeriesEnum, ProphetValue } from '../types'; +import { + DEFAULT_FORM_DATA, + EchartsMixedTimeseriesFormData, + EchartsMixedTimeseriesChartTransformedProps, +} from './types'; +import { ForecastSeriesEnum, ProphetValue } from '../types'; import { parseYAxisBound } from '../utils/controls'; import { dedupSeries, extractTimeseriesSeries, getLegendProps } from '../utils/series'; import { extractAnnotationLabels } from '../utils/annotation'; @@ -52,11 +57,14 @@ import { } from '../Timeseries/transformers'; import { TIMESERIES_CONSTANTS } from '../constants'; -export default function transformProps(chartProps: ChartProps): EchartsProps { - const { width, height, formData, queriesData } = chartProps; - const { annotation_data: annotationData_, data: data1 = [] } = queriesData[0]; - const { data: data2 = [] } = queriesData[1]; +export default function transformProps( + chartProps: EchartsMixedTimeseriesFormData, +): EchartsMixedTimeseriesChartTransformedProps { + const { width, height, formData, queriesData, hooks, filterState } = chartProps; + const { annotation_data: annotationData_ } = queriesData[0]; const annotationData = annotationData_ || {}; + const data1: TimeseriesDataRecord[] = queriesData[0].data || []; + const data2: TimeseriesDataRecord[] = queriesData[1].data || []; const { area, @@ -95,6 +103,10 @@ export default function transformProps(chartProps: ChartProps): EchartsProps { zoomable, richTooltip, xAxisLabelRotation, + groupby, + groupbyB, + emitFilter, + emitFilterB, }: EchartsMixedTimeseriesFormData = { ...DEFAULT_FORM_DATA, ...formData }; const colorScale = CategoricalColorNamespace.getScale(colorScheme as string); @@ -130,6 +142,7 @@ export default function transformProps(chartProps: ChartProps): EchartsProps { seriesType, stack, yAxisIndex, + filterState, }); if (transformedSeries) series.push(transformedSeries); }); @@ -142,6 +155,7 @@ export default function transformProps(chartProps: ChartProps): EchartsProps { seriesType: seriesTypeB, stack: stackB, yAxisIndex: yAxisIndexB, + filterState, }); if (transformedSeries) series.push(transformedSeries); }); @@ -174,6 +188,25 @@ export default function transformProps(chartProps: ChartProps): EchartsProps { const addYAxisLabelOffset = !!(yAxisTitle || yAxisTitleSecondary); const chartPadding = getPadding(showLegend, legendOrientation, addYAxisLabelOffset, zoomable); + + const labelMap = rawSeriesA.reduce((acc, datum) => { + const label = datum.name as string; + return { + ...acc, + [label]: label.split(', '), + }; + }, {}) as Record; + + const labelMapB = rawSeriesB.reduce((acc, datum) => { + const label = datum.name as string; + return { + ...acc, + [label]: label.split(', '), + }; + }, {}) as Record; + + const { setDataMask = () => {} } = hooks; + const echartOptions: EChartsOption = { useUTC: true, grid: { @@ -280,8 +313,18 @@ export default function transformProps(chartProps: ChartProps): EchartsProps { }; return { - echartOptions, + formData, width, height, + echartOptions, + setDataMask, + emitFilter, + emitFilterB, + labelMap, + labelMapB, + groupby, + groupbyB, + seriesBreakdown: rawSeriesA.length, + selectedValues: filterState.selectedValues || [], }; } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/types.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/types.ts index 1b30ba185619b..16908835f588c 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/types.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/MixedTimeseries/types.ts @@ -16,7 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -import { AnnotationLayer, TimeGranularity } from '@superset-ui/core'; +import { EChartsOption } from 'echarts'; +import { + AnnotationLayer, + TimeGranularity, + DataRecordValue, + SetDataMaskHook, + QueryFormData, + ChartProps, + ChartDataResponseResult, +} from '@superset-ui/core'; import { DEFAULT_LEGEND_FORM_DATA, EchartsLegendFormData } from '../types'; import { DEFAULT_FORM_DATA as TIMESERIES_DEFAULTS, @@ -24,7 +33,7 @@ import { EchartsTimeseriesSeriesType, } from '../Timeseries/types'; -export type EchartsMixedTimeseriesFormData = { +export type EchartsMixedTimeseriesFormData = QueryFormData & { annotationLayers: AnnotationLayer[]; // shared properties minorSplitLine: boolean; @@ -68,8 +77,12 @@ export type EchartsMixedTimeseriesFormData = { stackB: boolean; yAxisIndex?: number; yAxisIndexB?: number; + groupby: string[]; + groupbyB: string[]; + emitFilter: boolean; } & EchartsLegendFormData; +// @ts-ignore export const DEFAULT_FORM_DATA: EchartsMixedTimeseriesFormData = { ...DEFAULT_LEGEND_FORM_DATA, annotationLayers: [], @@ -104,9 +117,32 @@ export const DEFAULT_FORM_DATA: EchartsMixedTimeseriesFormData = { stackB: TIMESERIES_DEFAULTS.stack, yAxisIndex: 0, yAxisIndexB: 0, + groupby: [], + groupbyB: [], xAxisShowMinLabel: TIMESERIES_DEFAULTS.xAxisShowMinLabel, xAxisShowMaxLabel: TIMESERIES_DEFAULTS.xAxisShowMaxLabel, zoomable: TIMESERIES_DEFAULTS.zoomable, richTooltip: TIMESERIES_DEFAULTS.richTooltip, xAxisLabelRotation: TIMESERIES_DEFAULTS.xAxisLabelRotation, }; + +export interface EchartsMixedTimeseriesProps extends ChartProps { + formData: EchartsMixedTimeseriesFormData; + queriesData: ChartDataResponseResult[]; +} + +export type EchartsMixedTimeseriesChartTransformedProps = { + formData: EchartsMixedTimeseriesFormData; + height: number; + width: number; + echartOptions: EChartsOption; + emitFilter: boolean; + emitFilterB: boolean; + setDataMask: SetDataMaskHook; + groupby: string[]; + groupbyB: string[]; + labelMap: Record; + labelMapB: Record; + selectedValues: Record; + seriesBreakdown: number; +};