mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
feat(timeseries-chart): add percentage threshold input control (#17758)
* feat(timeseries-chart): add percentage threshold control for stack series labels * feat: move threshold vlues to an array * add tests for showValue, onlyTotal, and percentThreshold * feat: add another test * revert ChartProps typesetting, fix misnamed variable on form data type, and other minor changes * fix percentage threshold push equation * fix percentage threshold push equation in tests * change default on control to match form * attempt fix form defaults import Co-authored-by: Corbin Robb <corbin@Corbins-MacBook-Pro.local>
This commit is contained in:
@@ -22,10 +22,13 @@ import {
|
||||
FormulaAnnotationLayer,
|
||||
IntervalAnnotationLayer,
|
||||
TimeseriesAnnotationLayer,
|
||||
AnnotationStyle,
|
||||
AnnotationType,
|
||||
AnnotationSourceType,
|
||||
} from '@superset-ui/core';
|
||||
import transformProps from '../../src/Timeseries/transformProps';
|
||||
|
||||
describe('EchartsTimeseries tranformProps', () => {
|
||||
describe('EchartsTimeseries transformProps', () => {
|
||||
const formData = {
|
||||
colorScheme: 'bnbColors',
|
||||
datasource: '3__table',
|
||||
@@ -82,9 +85,10 @@ describe('EchartsTimeseries tranformProps', () => {
|
||||
it('should add a formula annotation to viz', () => {
|
||||
const formula: FormulaAnnotationLayer = {
|
||||
name: 'My Formula',
|
||||
annotationType: 'FORMULA',
|
||||
annotationType: AnnotationType.Formula,
|
||||
value: 'x+1',
|
||||
style: 'solid',
|
||||
style: AnnotationStyle.Solid,
|
||||
showLabel: true,
|
||||
show: true,
|
||||
};
|
||||
const chartProps = new ChartProps({
|
||||
@@ -132,33 +136,36 @@ describe('EchartsTimeseries tranformProps', () => {
|
||||
|
||||
it('should add an interval, event and timeseries annotation to viz', () => {
|
||||
const event: EventAnnotationLayer = {
|
||||
annotationType: 'EVENT',
|
||||
annotationType: AnnotationType.Event,
|
||||
name: 'My Event',
|
||||
show: true,
|
||||
sourceType: 'NATIVE',
|
||||
style: 'solid',
|
||||
showLabel: true,
|
||||
sourceType: AnnotationSourceType.Native,
|
||||
style: AnnotationStyle.Solid,
|
||||
value: 1,
|
||||
};
|
||||
|
||||
const interval: IntervalAnnotationLayer = {
|
||||
annotationType: 'INTERVAL',
|
||||
annotationType: AnnotationType.Interval,
|
||||
name: 'My Interval',
|
||||
show: true,
|
||||
sourceType: 'table',
|
||||
showLabel: true,
|
||||
sourceType: AnnotationSourceType.Table,
|
||||
titleColumn: '',
|
||||
timeColumn: 'start',
|
||||
intervalEndColumn: '',
|
||||
descriptionColumns: [],
|
||||
style: 'dashed',
|
||||
style: AnnotationStyle.Dashed,
|
||||
value: 2,
|
||||
};
|
||||
|
||||
const timeseries: TimeseriesAnnotationLayer = {
|
||||
annotationType: 'TIME_SERIES',
|
||||
annotationType: AnnotationType.Timeseries,
|
||||
name: 'My Timeseries',
|
||||
show: true,
|
||||
sourceType: 'line',
|
||||
style: 'solid',
|
||||
showLabel: true,
|
||||
sourceType: AnnotationSourceType.Line,
|
||||
style: AnnotationStyle.Solid,
|
||||
titleColumn: '',
|
||||
value: 3,
|
||||
};
|
||||
@@ -244,3 +251,198 @@ describe('EchartsTimeseries tranformProps', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Does transformProps transform series correctly', () => {
|
||||
type seriesDataType = [Date, number];
|
||||
type labelFormatterType = (params: {
|
||||
value: seriesDataType;
|
||||
dataIndex: number;
|
||||
seriesIndex: number;
|
||||
}) => string;
|
||||
type seriesType = {
|
||||
label: { show: boolean; formatter: labelFormatterType };
|
||||
data: seriesDataType[];
|
||||
name: string;
|
||||
};
|
||||
|
||||
const formData = {
|
||||
colorScheme: 'bnbColors',
|
||||
datasource: '3__table',
|
||||
granularity_sqla: 'ds',
|
||||
metric: 'sum__num',
|
||||
groupby: ['foo', 'bar'],
|
||||
showValue: true,
|
||||
stack: true,
|
||||
onlyTotal: false,
|
||||
percentageThreshold: 50,
|
||||
};
|
||||
const queriesData = [
|
||||
{
|
||||
data: [
|
||||
{
|
||||
'San Francisco': 1,
|
||||
'New York': 2,
|
||||
Boston: 1,
|
||||
__timestamp: 599616000000,
|
||||
},
|
||||
{
|
||||
'San Francisco': 3,
|
||||
'New York': 4,
|
||||
Boston: 1,
|
||||
__timestamp: 599916000000,
|
||||
},
|
||||
{
|
||||
'San Francisco': 5,
|
||||
'New York': 8,
|
||||
Boston: 6,
|
||||
__timestamp: 600216000000,
|
||||
},
|
||||
{
|
||||
'San Francisco': 2,
|
||||
'New York': 7,
|
||||
Boston: 2,
|
||||
__timestamp: 600516000000,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const chartPropsConfig = {
|
||||
formData,
|
||||
width: 800,
|
||||
height: 600,
|
||||
queriesData,
|
||||
};
|
||||
|
||||
const totalStackedValues = queriesData[0].data.reduce(
|
||||
(totals, currentStack) => {
|
||||
const total = Object.keys(currentStack).reduce((stackSum, key) => {
|
||||
if (key === '__timestamp') return stackSum;
|
||||
return stackSum + currentStack[key];
|
||||
}, 0);
|
||||
totals.push(total);
|
||||
return totals;
|
||||
},
|
||||
[] as number[],
|
||||
);
|
||||
|
||||
it('should show labels when showValue is true', () => {
|
||||
const chartProps = new ChartProps(chartPropsConfig);
|
||||
|
||||
const transformedSeries = transformProps(chartProps).echartOptions
|
||||
.series as seriesType[];
|
||||
|
||||
transformedSeries.forEach(series => {
|
||||
expect(series.label.show).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not show labels when showValue is false', () => {
|
||||
const updatedChartPropsConfig = {
|
||||
...chartPropsConfig,
|
||||
formData: { ...formData, showValue: false },
|
||||
};
|
||||
|
||||
const chartProps = new ChartProps(updatedChartPropsConfig);
|
||||
|
||||
const transformedSeries = transformProps(chartProps).echartOptions
|
||||
.series as seriesType[];
|
||||
|
||||
transformedSeries.forEach(series => {
|
||||
expect(series.label.show).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should show only totals when onlyTotal is true', () => {
|
||||
const updatedChartPropsConfig = {
|
||||
...chartPropsConfig,
|
||||
formData: { ...formData, onlyTotal: true },
|
||||
};
|
||||
|
||||
const chartProps = new ChartProps(updatedChartPropsConfig);
|
||||
|
||||
const transformedSeries = transformProps(chartProps).echartOptions
|
||||
.series as seriesType[];
|
||||
|
||||
const showValueIndexes: number[] = [];
|
||||
|
||||
transformedSeries.forEach((entry, seriesIndex) => {
|
||||
const { data = [] } = entry;
|
||||
(data as [Date, number][]).forEach((datum, dataIndex) => {
|
||||
if (datum[1] !== null) {
|
||||
showValueIndexes[dataIndex] = seriesIndex;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
transformedSeries.forEach((series, seriesIndex) => {
|
||||
expect(series.label.show).toBe(true);
|
||||
series.data.forEach((value, dataIndex) => {
|
||||
const params = {
|
||||
value,
|
||||
dataIndex,
|
||||
seriesIndex,
|
||||
};
|
||||
|
||||
let expectedLabel: string;
|
||||
|
||||
if (seriesIndex === showValueIndexes[dataIndex]) {
|
||||
expectedLabel = String(totalStackedValues[dataIndex]);
|
||||
} else {
|
||||
expectedLabel = '';
|
||||
}
|
||||
|
||||
expect(series.label.formatter(params)).toBe(expectedLabel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should show labels on values >= percentageThreshold if onlyTotal is false', () => {
|
||||
const chartProps = new ChartProps(chartPropsConfig);
|
||||
|
||||
const transformedSeries = transformProps(chartProps).echartOptions
|
||||
.series as seriesType[];
|
||||
|
||||
const expectedThresholds = totalStackedValues.map(
|
||||
total => ((formData.percentageThreshold || 0) / 100) * total,
|
||||
);
|
||||
|
||||
transformedSeries.forEach((series, seriesIndex) => {
|
||||
expect(series.label.show).toBe(true);
|
||||
series.data.forEach((value, dataIndex) => {
|
||||
const params = {
|
||||
value,
|
||||
dataIndex,
|
||||
seriesIndex,
|
||||
};
|
||||
const expectedLabel =
|
||||
value[1] >= expectedThresholds[dataIndex] ? String(value[1]) : '';
|
||||
expect(series.label.formatter(params)).toBe(expectedLabel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not apply percentage threshold when showValue is true and stack is false', () => {
|
||||
const updatedChartPropsConfig = {
|
||||
...chartPropsConfig,
|
||||
formData: { ...formData, stack: false },
|
||||
};
|
||||
|
||||
const chartProps = new ChartProps(updatedChartPropsConfig);
|
||||
|
||||
const transformedSeries = transformProps(chartProps).echartOptions
|
||||
.series as seriesType[];
|
||||
|
||||
transformedSeries.forEach((series, seriesIndex) => {
|
||||
expect(series.label.show).toBe(true);
|
||||
series.data.forEach((value, dataIndex) => {
|
||||
const params = {
|
||||
value,
|
||||
dataIndex,
|
||||
seriesIndex,
|
||||
};
|
||||
const expectedLabel = String(value[1]);
|
||||
expect(series.label.formatter(params)).toBe(expectedLabel);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user