Skip to content

Commit

Permalink
feat(plugin-chart-echarts): implement cross filter in mixd-timeseries…
Browse files Browse the repository at this point in the history
… chart (#1256)

* feat(plugin-chart-echarts): add x-filter for MixedTimeseries

* fix(plugin-chart-echarts): delete debugger

* fix(plugin-chart-echarts): mix-timeseries supports x-filtering

* fix(plugin-chart-echarts): mix-timeseries query

Co-authored-by: xiezhongfu <458957068@qq.com>
  • Loading branch information
2 people authored and zhaoyongjie committed Nov 26, 2021
1 parent 8dd9104 commit e54ae04
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Echart height={height} width={width} echartOptions={echartOptions} />;
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 (
<Echart
height={height}
width={width}
echartOptions={echartOptions}
eventHandlers={eventHandlers}
selectedValues={selectedValues}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
ControlPanelConfig,
ControlPanelSectionConfig,
ControlSetRow,
emitFilterControl,
sections,
sharedControls,
} from '@superset-ui/chart-controls';
Expand Down Expand Up @@ -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}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -64,6 +69,7 @@ export default class EchartsTimeseriesChartPlugin extends ChartPlugin {
t('Transformable'),
],
}),
// @ts-ignore
transformProps,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,21 @@
import {
AnnotationLayer,
CategoricalColorNamespace,
ChartProps,
DataRecordValue,
TimeseriesDataRecord,
getNumberFormatter,
isEventAnnotationLayer,
isFormulaAnnotationLayer,
isIntervalAnnotationLayer,
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';
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -130,6 +142,7 @@ export default function transformProps(chartProps: ChartProps): EchartsProps {
seriesType,
stack,
yAxisIndex,
filterState,
});
if (transformedSeries) series.push(transformedSeries);
});
Expand All @@ -142,6 +155,7 @@ export default function transformProps(chartProps: ChartProps): EchartsProps {
seriesType: seriesTypeB,
stack: stackB,
yAxisIndex: yAxisIndexB,
filterState,
});
if (transformedSeries) series.push(transformedSeries);
});
Expand Down Expand Up @@ -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<string, DataRecordValue[]>;

const labelMapB = rawSeriesB.reduce((acc, datum) => {
const label = datum.name as string;
return {
...acc,
[label]: label.split(', '),
};
}, {}) as Record<string, DataRecordValue[]>;

const { setDataMask = () => {} } = hooks;

const echartOptions: EChartsOption = {
useUTC: true,
grid: {
Expand Down Expand Up @@ -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 || [],
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,24 @@
* 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,
EchartsTimeseriesContributionType,
EchartsTimeseriesSeriesType,
} from '../Timeseries/types';

export type EchartsMixedTimeseriesFormData = {
export type EchartsMixedTimeseriesFormData = QueryFormData & {
annotationLayers: AnnotationLayer[];
// shared properties
minorSplitLine: boolean;
Expand Down Expand Up @@ -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: [],
Expand Down Expand Up @@ -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<string, DataRecordValue[]>;
labelMapB: Record<string, DataRecordValue[]>;
selectedValues: Record<number, string>;
seriesBreakdown: number;
};

0 comments on commit e54ae04

Please sign in to comment.