Skip to content

Commit

Permalink
fix: Total calculation in stacked Timeseries charts
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-s-molina committed Jun 22, 2023
1 parent d5f88c1 commit 07cca12
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ import {
FilterState,
JsonObject,
} from '../..';
import { HandlerFunction, PlainObject, SetDataMaskHook } from '../types/Base';
import {
HandlerFunction,
LegendState,
PlainObject,
SetDataMaskHook,
} from '../types/Base';
import { QueryData, DataRecordFilters } from '..';
import { SupersetTheme } from '../../style';

Expand All @@ -54,6 +59,8 @@ type Hooks = {
onContextMenu?: HandlerFunction;
/** handle errors */
onError?: HandlerFunction;
/** handle legend state changes */
onLegendStateChanged?: HandlerFunction;
/** use the vis as control to update state */
setControlValue?: HandlerFunction;
/** handle external filters */
Expand Down Expand Up @@ -88,6 +95,8 @@ export interface ChartPropsConfig {
ownState?: JsonObject;
/** Filter state that saved in dashboard */
filterState?: FilterState;
/** Legend state */
legendState?: LegendState;
/** Set of actual behaviors that this instance of chart should use */
behaviors?: Behavior[];
/** Chart display settings related to current view context */
Expand Down Expand Up @@ -128,6 +137,8 @@ export default class ChartProps<FormData extends RawFormData = RawFormData> {

filterState: FilterState;

legendState: LegendState;

queriesData: QueryData[];

width: number;
Expand Down Expand Up @@ -156,6 +167,7 @@ export default class ChartProps<FormData extends RawFormData = RawFormData> {
hooks = {},
ownState = {},
filterState = {},
legendState,
initialValues = {},
queriesData = [],
behaviors = [],
Expand All @@ -181,6 +193,7 @@ export default class ChartProps<FormData extends RawFormData = RawFormData> {
this.queriesData = queriesData;
this.ownState = ownState;
this.filterState = filterState;
this.legendState = legendState;
this.behaviors = behaviors;
this.displaySettings = displaySettings;
this.appSection = appSection;
Expand All @@ -205,6 +218,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector {
input => input.width,
input => input.ownState,
input => input.filterState,
input => input.legendState,
input => input.behaviors,
input => input.displaySettings,
input => input.appSection,
Expand All @@ -224,6 +238,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector {
width,
ownState,
filterState,
legendState,
behaviors,
displaySettings,
appSection,
Expand All @@ -243,6 +258,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector {
queriesData,
ownState,
filterState,
legendState,
width,
behaviors,
displaySettings,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,8 @@ export enum AxisType {
log = 'log',
}

export interface LegendState {
[key: string]: boolean;
}

export default {};
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
import { EchartsMixedTimeseriesChartTransformedProps } from './types';
import Echart from '../components/Echart';
import { EventHandlers } from '../types';
import { currentSeries, formatSeriesName } from '../utils/series';
import { formatSeriesName } from '../utils/series';

export default function EchartsMixedTimeseries({
height,
Expand Down Expand Up @@ -123,12 +123,6 @@ export default function EchartsMixedTimeseries({
const { seriesName, seriesIndex } = props;
handleChange(seriesName, seriesIndex);
},
mouseout: () => {
currentSeries.name = '';
},
mouseover: params => {
currentSeries.name = params.seriesName;
},
contextmenu: async eventParams => {
if (onContextMenu) {
eventParams.event.stop();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ import {
import { parseYAxisBound } from '../utils/controls';
import {
getOverMaxHiddenFormatter,
currentSeries,
dedupSeries,
extractSeries,
getAxisType,
Expand Down Expand Up @@ -481,11 +480,7 @@ export default function transformProps(
seriesName: key,
formatter: primarySeries.has(key) ? formatter : formatterSecondary,
});
if (currentSeries.name === key) {
rows.push(`<span style="font-weight: 700">${content}</span>`);
} else {
rows.push(`<span style="opacity: 0.7">${content}</span>`);
}
rows.push(`<span style="opacity: 0.7">${content}</span>`);
});
return rows.join('<br />');
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@ import {
getTimeFormatter,
getColumnLabel,
getNumberFormatter,
LegendState,
} from '@superset-ui/core';
import { ViewRootGroup } from 'echarts/types/src/util/types';
import GlobalModel from 'echarts/types/src/model/Global';
import ComponentModel from 'echarts/types/src/model/Component';
import { EchartsHandler, EventHandlers } from '../types';
import Echart from '../components/Echart';
import { TimeseriesChartTransformedProps } from './types';
import { currentSeries, formatSeriesName } from '../utils/series';
import { formatSeriesName } from '../utils/series';
import { ExtraControls } from '../components/ExtraControls';

const TIMER_DURATION = 300;

// @ts-ignore
export default function EchartsTimeseries({
formData,
height,
Expand All @@ -49,6 +49,7 @@ export default function EchartsTimeseries({
setControlValue,
legendData = [],
onContextMenu,
onLegendStateChanged,
xValueFormatter,
xAxis,
refs,
Expand All @@ -59,8 +60,6 @@ export default function EchartsTimeseries({
const echartRef = useRef<EchartsHandler | null>(null);
// eslint-disable-next-line no-param-reassign
refs.echartRef = echartRef;
const lastTimeRef = useRef(Date.now());
const lastSelectedLegend = useRef('');
const clickTimer = useRef<ReturnType<typeof setTimeout>>();
const extraControlRef = useRef<HTMLDivElement>(null);
const [extraControlHeight, setExtraControlHeight] = useState(0);
Expand All @@ -69,34 +68,6 @@ export default function EchartsTimeseries({
setExtraControlHeight(updatedHeight);
}, [formData.showExtraControls]);

const handleDoubleClickChange = useCallback(
(name?: string) => {
const echartInstance = echartRef.current?.getEchartInstance();
if (!name) {
currentSeries.legend = '';
echartInstance?.dispatchAction({
type: 'legendAllSelect',
});
} else {
legendData.forEach(datum => {
if (datum === name) {
currentSeries.legend = datum;
echartInstance?.dispatchAction({
type: 'legendSelect',
name: datum,
});
} else {
echartInstance?.dispatchAction({
type: 'legendUnSelect',
name: datum,
});
}
});
}
},
[legendData],
);

const getModelInfo = (target: ViewRootGroup, globalModel: GlobalModel) => {
let el = target;
let model: ComponentModel | null = null;
Expand Down Expand Up @@ -175,30 +146,14 @@ export default function EchartsTimeseries({
handleChange(name);
}, TIMER_DURATION);
},
mouseout: () => {
currentSeries.name = '';
legendselectchanged: payload => {
onLegendStateChanged?.(payload.selected);
},
mouseover: params => {
currentSeries.name = params.seriesName;
legendselectall: payload => {
onLegendStateChanged?.(payload.selected);
},
legendselectchanged: payload => {
const currentTime = Date.now();
// TIMER_DURATION is the interval between two legendselectchanged event
if (
currentTime - lastTimeRef.current < TIMER_DURATION &&
lastSelectedLegend.current === payload.name
) {
// execute dbclick
handleDoubleClickChange(payload.name);
} else {
lastTimeRef.current = currentTime;
// remember last selected legend
lastSelectedLegend.current = payload.name;
}
// if all legend is unselected, we keep all selected
if (Object.values(payload.selected).every(i => !i)) {
handleDoubleClickChange();
}
legendinverseselect: payload => {
onLegendStateChanged?.(payload.selected);
},
contextmenu: async eventParams => {
if (onContextMenu) {
Expand Down Expand Up @@ -272,15 +227,16 @@ export default function EchartsTimeseries({
// @ts-ignore
const globalModel = echartInstance.getModel();
const model = getModelInfo(params.target, globalModel);
const seriesCount = globalModel.getSeriesCount();
const currentSeriesIndices = globalModel.getCurrentSeriesIndices();
if (model) {
const { name } = model;
if (seriesCount !== currentSeriesIndices.length) {
handleDoubleClickChange();
} else {
handleDoubleClickChange(name);
}
const legendState: LegendState = legendData.reduce(
(previous, datum) => ({
...previous,
[datum]: datum === name,
}),
{},
);
onLegendStateChanged?.(legendState);
}
}
},
Expand All @@ -292,6 +248,7 @@ export default function EchartsTimeseries({
<ExtraControls formData={formData} setControlValue={setControlValue} />
</div>
<Echart
ref={echartRef}
refs={refs}
height={height - extraControlHeight}
width={width}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ import { ForecastSeriesEnum, ForecastValue, Refs } from '../types';
import { parseYAxisBound } from '../utils/controls';
import {
calculateLowerLogTick,
currentSeries,
dedupSeries,
extractDataTotalValues,
extractSeries,
Expand Down Expand Up @@ -101,6 +100,7 @@ export default function transformProps(
width,
height,
filterState,
legendState,
formData,
hooks,
queriesData,
Expand Down Expand Up @@ -192,6 +192,7 @@ export default function transformProps(
stack,
percentageThreshold,
xAxisCol: xAxisLabel,
legendState,
},
);
const extraMetricLabels = extractExtraMetrics(chartProps.rawFormData).map(
Expand Down Expand Up @@ -221,6 +222,7 @@ export default function transformProps(
stack,
onlyTotal,
isHorizontal,
legendState,
});
const seriesContexts = extractForecastSeriesContexts(
Object.values(rawSeries).map(series => series.name as string),
Expand Down Expand Up @@ -258,6 +260,7 @@ export default function transformProps(
markerSize,
areaOpacity: opacity,
seriesType,
legendState,
stack,
formatter,
showValue,
Expand Down Expand Up @@ -379,6 +382,7 @@ export default function transformProps(
setDataMask = () => {},
setControlValue = () => {},
onContextMenu,
onLegendStateChanged,
} = hooks;

const addYAxisLabelOffset = !!yAxisTitle;
Expand Down Expand Up @@ -486,7 +490,7 @@ export default function transformProps(
seriesName: key,
formatter,
});
if (currentSeries.name === key) {
if (!legendState || legendState[key]) {
rows.push(`<span style="font-weight: 700">${content}</span>`);
} else {
rows.push(`<span style="opacity: 0.7">${content}</span>`);
Expand All @@ -506,6 +510,7 @@ export default function transformProps(
showLegend,
theme,
zoomable,
legendState,
),
data: legendData as string[],
},
Expand Down Expand Up @@ -549,6 +554,7 @@ export default function transformProps(
width,
legendData,
onContextMenu,
onLegendStateChanged,
xValueFormatter: tooltipFormatter,
xAxis: {
label: xAxisLabel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
getTimeFormatter,
IntervalAnnotationLayer,
isTimeseriesAnnotationResult,
LegendState,
NumberFormatter,
smartDateDetailedFormatter,
smartDateFormatter,
Expand Down Expand Up @@ -65,7 +66,7 @@ import {
formatAnnotationLabel,
parseAnnotationOpacity,
} from '../utils/annotation';
import { currentSeries, getChartPadding } from '../utils/series';
import { getChartPadding } from '../utils/series';
import {
OpacityEnum,
StackControlsValue,
Expand Down Expand Up @@ -156,6 +157,7 @@ export function transformSeries(
yAxisIndex?: number;
showValue?: boolean;
onlyTotal?: boolean;
legendState?: LegendState;
formatter?: NumberFormatter;
totalStackedValues?: number[];
showValueIndexes?: number[];
Expand All @@ -182,6 +184,7 @@ export function transformSeries(
showValue,
onlyTotal,
formatter,
legendState,
totalStackedValues = [],
showValueIndexes = [],
thresholdValues = [],
Expand Down Expand Up @@ -308,10 +311,14 @@ export function transformSeries(
formatter: (params: any) => {
const { value, dataIndex, seriesIndex, seriesName } = params;
const numericValue = isHorizontal ? value[0] : value[1];
const isSelectedLegend = currentSeries.legend === seriesName;
const isSelectedLegend = !legendState || legendState[seriesName];
const isAreaExpand = stack === StackControlsValue.Expand;
if (!formatter) return numericValue;
if (!stack || isSelectedLegend) return formatter(numericValue);
if (!formatter) {
return numericValue;
}
if (!stack && isSelectedLegend) {
return formatter(numericValue);
}
if (!onlyTotal) {
if (
numericValue >=
Expand Down
Loading

0 comments on commit 07cca12

Please sign in to comment.