Skip to content

Commit

Permalink
feat(plugin-chart-echarts): add x-filtering in gauge chart (#1203)
Browse files Browse the repository at this point in the history
* fix(plugin-chart-echarts): add gauge cross filtering

* fix(plugin-chart-echarts): fix lint

* fix(plugin-chart-echarts): use constants
  • Loading branch information
stephenLYZ authored and zhaoyongjie committed Nov 26, 2021
1 parent 5601141 commit 4333a68
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Echart height={height} width={width} echartOptions={echartOptions} />;
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 (
<Echart
height={height}
width={width}
echartOptions={echartOptions}
eventHandlers={eventHandlers}
selectedValues={selectedValues}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -33,13 +33,16 @@ import {
DEFAULT_FORM_DATA as DEFAULT_GAUGE_FORM_DATA,
EchartsGaugeFormData,
AxisTickLineStyle,
GaugeChartTransformedProps,
EchartsGaugeChartProps,
} from './types';
import {
DEFAULT_GAUGE_SERIES_OPTION,
INTERVAL_GAUGE_SERIES_OPTION,
OFFSETS,
FONT_SIZE_MULTIPLIERS,
} from './constants';
import { OpacityEnum } from '../constants';

const setIntervalBoundsAndColors = (
intervals: string,
Expand Down Expand Up @@ -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,
Expand All @@ -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);
Expand All @@ -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<string, DataRecordValue[]>();

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,
Expand Down Expand Up @@ -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 || [],
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<EchartsGaugeFormData> = {
...DEFAULT_LEGEND_FORM_DATA,
groupby: [],
rowLimit: 10,
Expand All @@ -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<EchartsGaugeFormData>;
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -96,3 +97,15 @@ export enum LabelPositionEnum {
InsideTopRight = 'insideTopRight',
InsideBottomRight = 'insideBottomRight',
}

export interface EChartTransformedProps<F> {
formData: F;
height: number;
width: number;
echartOptions: EChartsOption;
emitFilter: boolean;
setDataMask: SetDataMaskHook;
labelMap: Record<string, DataRecordValue[]>;
groupby: string[];
selectedValues: Record<number, string>;
}

0 comments on commit 4333a68

Please sign in to comment.