fix(plugin-chart-echarts): use scale for truncating x-axis (#26269)
This commit is contained in:
parent
429e2a33c3
commit
67468c46c0
|
|
@ -35,6 +35,8 @@ import { EchartsTimeseriesSeriesType } from '../Timeseries/types';
|
|||
import {
|
||||
legendSection,
|
||||
richTooltipSection,
|
||||
truncateXAxis,
|
||||
xAxisBounds,
|
||||
xAxisLabelRotation,
|
||||
} from '../controls';
|
||||
|
||||
|
|
@ -333,6 +335,8 @@ const config: ControlPanelConfig = {
|
|||
},
|
||||
},
|
||||
],
|
||||
[truncateXAxis],
|
||||
[xAxisBounds],
|
||||
[
|
||||
{
|
||||
name: 'truncateYAxis',
|
||||
|
|
|
|||
|
|
@ -20,32 +20,32 @@
|
|||
import { invert } from 'lodash';
|
||||
import {
|
||||
AnnotationLayer,
|
||||
buildCustomFormatters,
|
||||
CategoricalColorNamespace,
|
||||
CurrencyFormatter,
|
||||
ensureIsArray,
|
||||
GenericDataType,
|
||||
getCustomFormatter,
|
||||
getNumberFormatter,
|
||||
getXAxisLabel,
|
||||
isDefined,
|
||||
isEventAnnotationLayer,
|
||||
isFormulaAnnotationLayer,
|
||||
isIntervalAnnotationLayer,
|
||||
isPhysicalColumn,
|
||||
isTimeseriesAnnotationLayer,
|
||||
QueryFormData,
|
||||
QueryFormMetric,
|
||||
TimeseriesChartDataResponseResult,
|
||||
TimeseriesDataRecord,
|
||||
getXAxisLabel,
|
||||
isPhysicalColumn,
|
||||
isDefined,
|
||||
ensureIsArray,
|
||||
buildCustomFormatters,
|
||||
ValueFormatter,
|
||||
QueryFormMetric,
|
||||
getCustomFormatter,
|
||||
CurrencyFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { getOriginalSeries } from '@superset-ui/chart-controls';
|
||||
import { EChartsCoreOption, SeriesOption } from 'echarts';
|
||||
import {
|
||||
DEFAULT_FORM_DATA,
|
||||
EchartsMixedTimeseriesFormData,
|
||||
EchartsMixedTimeseriesChartTransformedProps,
|
||||
EchartsMixedTimeseriesFormData,
|
||||
EchartsMixedTimeseriesProps,
|
||||
} from './types';
|
||||
import {
|
||||
|
|
@ -55,14 +55,15 @@ import {
|
|||
} from '../types';
|
||||
import { parseAxisBound } from '../utils/controls';
|
||||
import {
|
||||
getOverMaxHiddenFormatter,
|
||||
dedupSeries,
|
||||
extractDataTotalValues,
|
||||
extractSeries,
|
||||
extractShowValueIndexes,
|
||||
getAxisType,
|
||||
getColtypesMapping,
|
||||
getLegendProps,
|
||||
extractDataTotalValues,
|
||||
extractShowValueIndexes,
|
||||
getMinAndMaxFromBounds,
|
||||
getOverMaxHiddenFormatter,
|
||||
} from '../utils/series';
|
||||
import {
|
||||
extractAnnotationLabels,
|
||||
|
|
@ -84,7 +85,7 @@ import {
|
|||
transformSeries,
|
||||
transformTimeseriesAnnotation,
|
||||
} from '../Timeseries/transformers';
|
||||
import { TIMESERIES_CONSTANTS, TIMEGRAIN_TO_TIMESTAMP } from '../constants';
|
||||
import { TIMEGRAIN_TO_TIMESTAMP, TIMESERIES_CONSTANTS } from '../constants';
|
||||
import { getDefaultTooltip } from '../utils/tooltip';
|
||||
import {
|
||||
getTooltipTimeFormatter,
|
||||
|
|
@ -166,6 +167,7 @@ export default function transformProps(
|
|||
showValueB,
|
||||
stack,
|
||||
stackB,
|
||||
truncateXAxis,
|
||||
truncateYAxis,
|
||||
tooltipTimeFormat,
|
||||
yAxisFormat,
|
||||
|
|
@ -181,6 +183,7 @@ export default function transformProps(
|
|||
zoomable,
|
||||
richTooltip,
|
||||
tooltipSortByMetric,
|
||||
xAxisBounds,
|
||||
xAxisLabelRotation,
|
||||
groupby,
|
||||
groupbyB,
|
||||
|
|
@ -345,7 +348,8 @@ export default function transformProps(
|
|||
});
|
||||
|
||||
// yAxisBounds need to be parsed to replace incompatible values with undefined
|
||||
let [min, max] = (yAxisBounds || []).map(parseAxisBound);
|
||||
const [xAxisMin, xAxisMax] = (xAxisBounds || []).map(parseAxisBound);
|
||||
let [yAxisMin, yAxisMax] = (yAxisBounds || []).map(parseAxisBound);
|
||||
let [minSecondary, maxSecondary] = (yAxisBoundsSecondary || []).map(
|
||||
parseAxisBound,
|
||||
);
|
||||
|
|
@ -386,7 +390,7 @@ export default function transformProps(
|
|||
formatter:
|
||||
seriesType === EchartsTimeseriesSeriesType.Bar
|
||||
? getOverMaxHiddenFormatter({
|
||||
max,
|
||||
max: yAxisMax,
|
||||
formatter: seriesFormatter,
|
||||
})
|
||||
: seriesFormatter,
|
||||
|
|
@ -447,8 +451,8 @@ export default function transformProps(
|
|||
|
||||
// default to 0-100% range when doing row-level contribution chart
|
||||
if (contributionMode === 'row' && stack) {
|
||||
if (min === undefined) min = 0;
|
||||
if (max === undefined) max = 1;
|
||||
if (yAxisMin === undefined) yAxisMin = 0;
|
||||
if (yAxisMax === undefined) yAxisMax = 1;
|
||||
if (minSecondary === undefined) minSecondary = 0;
|
||||
if (maxSecondary === undefined) maxSecondary = 1;
|
||||
}
|
||||
|
|
@ -499,13 +503,23 @@ export default function transformProps(
|
|||
xAxisType === 'time' && timeGrainSqla
|
||||
? TIMEGRAIN_TO_TIMESTAMP[timeGrainSqla]
|
||||
: 0,
|
||||
...getMinAndMaxFromBounds(
|
||||
xAxisType,
|
||||
truncateXAxis,
|
||||
xAxisMin,
|
||||
xAxisMax,
|
||||
seriesType === EchartsTimeseriesSeriesType.Bar ||
|
||||
seriesTypeB === EchartsTimeseriesSeriesType.Bar
|
||||
? EchartsTimeseriesSeriesType.Bar
|
||||
: undefined,
|
||||
),
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
...defaultYAxis,
|
||||
type: logAxis ? 'log' : 'value',
|
||||
min,
|
||||
max,
|
||||
min: yAxisMin,
|
||||
max: yAxisMax,
|
||||
minorTick: { show: true },
|
||||
minorSplitLine: { show: minorSplitLine },
|
||||
axisLabel: {
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ export const DEFAULT_FORM_DATA: EchartsMixedTimeseriesFormData = {
|
|||
yAxisFormatSecondary: TIMESERIES_DEFAULTS.yAxisFormat,
|
||||
yAxisTitleSecondary: DEFAULT_TITLE_FORM_DATA.yAxisTitle,
|
||||
tooltipTimeFormat: TIMESERIES_DEFAULTS.tooltipTimeFormat,
|
||||
xAxisBounds: TIMESERIES_DEFAULTS.xAxisBounds,
|
||||
xAxisTimeFormat: TIMESERIES_DEFAULTS.xAxisTimeFormat,
|
||||
area: TIMESERIES_DEFAULTS.area,
|
||||
areaB: TIMESERIES_DEFAULTS.area,
|
||||
|
|
|
|||
|
|
@ -460,7 +460,13 @@ export default function transformProps(
|
|||
xAxisType === AxisType.time && timeGrainSqla
|
||||
? TIMEGRAIN_TO_TIMESTAMP[timeGrainSqla]
|
||||
: 0,
|
||||
...getMinAndMaxFromBounds(xAxisType, truncateXAxis, xAxisMin, xAxisMax),
|
||||
...getMinAndMaxFromBounds(
|
||||
xAxisType,
|
||||
truncateXAxis,
|
||||
xAxisMin,
|
||||
xAxisMax,
|
||||
seriesType,
|
||||
),
|
||||
};
|
||||
|
||||
let yAxis: any = {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
import { validateNumber } from '@superset-ui/core';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function parseAxisBound(
|
||||
bound?: string | number | null,
|
||||
): number | undefined {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,12 @@ import {
|
|||
StackControlsValue,
|
||||
TIMESERIES_CONSTANTS,
|
||||
} from '../constants';
|
||||
import { LegendOrientation, LegendType, StackType } from '../types';
|
||||
import {
|
||||
EchartsTimeseriesSeriesType,
|
||||
LegendOrientation,
|
||||
LegendType,
|
||||
StackType,
|
||||
} from '../types';
|
||||
import { defaultLegendPadding } from '../defaults';
|
||||
|
||||
function isDefined<T>(value: T | undefined | null): boolean {
|
||||
|
|
@ -547,16 +552,35 @@ export function calculateLowerLogTick(minPositiveValue: number) {
|
|||
return Math.pow(10, logBase10);
|
||||
}
|
||||
|
||||
type BoundsType = {
|
||||
min?: number | 'dataMin';
|
||||
max?: number | 'dataMax';
|
||||
scale?: true;
|
||||
};
|
||||
|
||||
export function getMinAndMaxFromBounds(
|
||||
axisType: AxisType,
|
||||
truncateAxis: boolean,
|
||||
min?: number,
|
||||
max?: number,
|
||||
): { min: number | 'dataMin'; max: number | 'dataMax' } | {} {
|
||||
return truncateAxis && axisType === AxisType.value
|
||||
? {
|
||||
min: min === undefined ? 'dataMin' : min,
|
||||
max: max === undefined ? 'dataMax' : max,
|
||||
}
|
||||
: {};
|
||||
seriesType?: EchartsTimeseriesSeriesType,
|
||||
): BoundsType | {} {
|
||||
if (axisType === AxisType.value && truncateAxis) {
|
||||
const ret: BoundsType = {};
|
||||
if (seriesType === EchartsTimeseriesSeriesType.Bar) {
|
||||
ret.scale = true;
|
||||
}
|
||||
if (min !== undefined) {
|
||||
ret.min = min;
|
||||
} else if (seriesType !== EchartsTimeseriesSeriesType.Bar) {
|
||||
ret.min = 'dataMin';
|
||||
}
|
||||
if (max !== undefined) {
|
||||
ret.max = max;
|
||||
} else if (seriesType !== EchartsTimeseriesSeriesType.Bar) {
|
||||
ret.max = 'dataMax';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,11 @@ import {
|
|||
sortAndFilterSeries,
|
||||
sortRows,
|
||||
} from '../../src/utils/series';
|
||||
import { LegendOrientation, LegendType } from '../../src/types';
|
||||
import {
|
||||
EchartsTimeseriesSeriesType,
|
||||
LegendOrientation,
|
||||
LegendType,
|
||||
} from '../../src/types';
|
||||
import { defaultLegendPadding } from '../../src/defaults';
|
||||
import { NULL_STRING } from '../../src/constants';
|
||||
|
||||
|
|
@ -885,28 +889,109 @@ test('getAxisType', () => {
|
|||
});
|
||||
|
||||
test('getMinAndMaxFromBounds returns empty object when not truncating', () => {
|
||||
expect(getMinAndMaxFromBounds(AxisType.value, false, 10, 100)).toEqual({});
|
||||
expect(
|
||||
getMinAndMaxFromBounds(
|
||||
AxisType.value,
|
||||
false,
|
||||
10,
|
||||
100,
|
||||
EchartsTimeseriesSeriesType.Bar,
|
||||
),
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
test('getMinAndMaxFromBounds returns automatic bounds when truncating', () => {
|
||||
test('getMinAndMaxFromBounds returns empty object for categorical axis', () => {
|
||||
expect(
|
||||
getMinAndMaxFromBounds(AxisType.value, true, undefined, undefined),
|
||||
getMinAndMaxFromBounds(
|
||||
AxisType.category,
|
||||
false,
|
||||
10,
|
||||
100,
|
||||
EchartsTimeseriesSeriesType.Bar,
|
||||
),
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
test('getMinAndMaxFromBounds returns empty object for time axis', () => {
|
||||
expect(
|
||||
getMinAndMaxFromBounds(
|
||||
AxisType.time,
|
||||
false,
|
||||
10,
|
||||
100,
|
||||
EchartsTimeseriesSeriesType.Bar,
|
||||
),
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
test('getMinAndMaxFromBounds returns dataMin/dataMax for non-bar charts', () => {
|
||||
expect(
|
||||
getMinAndMaxFromBounds(
|
||||
AxisType.value,
|
||||
true,
|
||||
undefined,
|
||||
undefined,
|
||||
EchartsTimeseriesSeriesType.Line,
|
||||
),
|
||||
).toEqual({
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
});
|
||||
});
|
||||
|
||||
test('getMinAndMaxFromBounds returns automatic upper bound when truncating', () => {
|
||||
expect(getMinAndMaxFromBounds(AxisType.value, true, 10, undefined)).toEqual({
|
||||
test('getMinAndMaxFromBounds returns bound without scale for non-bar charts', () => {
|
||||
expect(
|
||||
getMinAndMaxFromBounds(
|
||||
AxisType.value,
|
||||
true,
|
||||
10,
|
||||
undefined,
|
||||
EchartsTimeseriesSeriesType.Line,
|
||||
),
|
||||
).toEqual({
|
||||
min: 10,
|
||||
max: 'dataMax',
|
||||
});
|
||||
});
|
||||
|
||||
test('getMinAndMaxFromBounds returns automatic lower bound when truncating', () => {
|
||||
expect(getMinAndMaxFromBounds(AxisType.value, true, undefined, 100)).toEqual({
|
||||
min: 'dataMin',
|
||||
max: 100,
|
||||
test('getMinAndMaxFromBounds returns scale when truncating without bounds', () => {
|
||||
expect(
|
||||
getMinAndMaxFromBounds(
|
||||
AxisType.value,
|
||||
true,
|
||||
undefined,
|
||||
undefined,
|
||||
EchartsTimeseriesSeriesType.Bar,
|
||||
),
|
||||
).toEqual({ scale: true });
|
||||
});
|
||||
|
||||
test('getMinAndMaxFromBounds returns automatic upper bound when truncating', () => {
|
||||
expect(
|
||||
getMinAndMaxFromBounds(
|
||||
AxisType.value,
|
||||
true,
|
||||
10,
|
||||
undefined,
|
||||
EchartsTimeseriesSeriesType.Bar,
|
||||
),
|
||||
).toEqual({
|
||||
min: 10,
|
||||
scale: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('getMinAndMaxFromBounds returns automatic lower bound when truncating', () => {
|
||||
expect(
|
||||
getMinAndMaxFromBounds(
|
||||
AxisType.value,
|
||||
true,
|
||||
undefined,
|
||||
100,
|
||||
EchartsTimeseriesSeriesType.Bar,
|
||||
),
|
||||
).toEqual({
|
||||
max: 100,
|
||||
scale: true,
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue