feat(explore): Add time shift color control to ECharts (#29897)
This commit is contained in:
parent
ff3b86b5ff
commit
c5594f2979
|
|
@ -338,6 +338,20 @@ const color_scheme: SharedControlConfig<'ColorSchemeControl'> = {
|
|||
}),
|
||||
};
|
||||
|
||||
const time_shift_color: SharedControlConfig<'CheckboxControl'> = {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Match time shift color with original series'),
|
||||
default: true,
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'When unchecked, colors from the selected color scheme will be used for time shifted series',
|
||||
),
|
||||
visibility: ({ controls }) =>
|
||||
Boolean(
|
||||
controls?.time_compare?.value && !isEmpty(controls?.time_compare?.value),
|
||||
),
|
||||
};
|
||||
|
||||
const truncate_metric: SharedControlConfig<'CheckboxControl'> = {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Truncate Metric'),
|
||||
|
|
@ -399,6 +413,7 @@ export default {
|
|||
x_axis_time_format,
|
||||
adhoc_filters: dndAdhocFilterControl,
|
||||
color_scheme,
|
||||
time_shift_color,
|
||||
series_columns: dndColumnsControl,
|
||||
series_limit,
|
||||
series_limit_metric: dndSortByControl,
|
||||
|
|
|
|||
|
|
@ -299,6 +299,7 @@ const config: ControlPanelConfig = {
|
|||
expanded: true,
|
||||
controlSetRows: [
|
||||
['color_scheme'],
|
||||
['time_shift_color'],
|
||||
...createCustomizeSection(t('Query A'), ''),
|
||||
...createCustomizeSection(t('Query B'), 'B'),
|
||||
[
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ export default function transformProps(
|
|||
areaB,
|
||||
annotationLayers,
|
||||
colorScheme,
|
||||
timeShiftColor,
|
||||
contributionMode,
|
||||
legendOrientation,
|
||||
legendType,
|
||||
|
|
@ -406,6 +407,7 @@ export default function transformProps(
|
|||
showValueIndexes: showValueIndexesA,
|
||||
totalStackedValues,
|
||||
thresholdValues,
|
||||
timeShiftColor,
|
||||
},
|
||||
);
|
||||
if (transformedSeries) series.push(transformedSeries);
|
||||
|
|
@ -455,6 +457,7 @@ export default function transformProps(
|
|||
showValueIndexes: showValueIndexesB,
|
||||
totalStackedValues: totalStackedValuesB,
|
||||
thresholdValues: thresholdValuesB,
|
||||
timeShiftColor,
|
||||
},
|
||||
);
|
||||
if (transformedSeries) series.push(transformedSeries);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ const config: ControlPanelConfig = {
|
|||
controlSetRows: [
|
||||
...seriesOrderSection,
|
||||
['color_scheme'],
|
||||
['time_shift_color'],
|
||||
[
|
||||
{
|
||||
name: 'seriesType',
|
||||
|
|
|
|||
|
|
@ -295,6 +295,7 @@ const config: ControlPanelConfig = {
|
|||
controlSetRows: [
|
||||
...seriesOrderSection,
|
||||
['color_scheme'],
|
||||
['time_shift_color'],
|
||||
...showValueSection,
|
||||
[minorTicks],
|
||||
[
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ const config: ControlPanelConfig = {
|
|||
controlSetRows: [
|
||||
...seriesOrderSection,
|
||||
['color_scheme'],
|
||||
['time_shift_color'],
|
||||
[
|
||||
{
|
||||
name: 'seriesType',
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ const config: ControlPanelConfig = {
|
|||
controlSetRows: [
|
||||
...seriesOrderSection,
|
||||
['color_scheme'],
|
||||
['time_shift_color'],
|
||||
...showValueSection,
|
||||
[
|
||||
{
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ const config: ControlPanelConfig = {
|
|||
controlSetRows: [
|
||||
...seriesOrderSection,
|
||||
['color_scheme'],
|
||||
['time_shift_color'],
|
||||
...showValueSectionWithoutStack,
|
||||
[
|
||||
{
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ const config: ControlPanelConfig = {
|
|||
controlSetRows: [
|
||||
...seriesOrderSection,
|
||||
['color_scheme'],
|
||||
['time_shift_color'],
|
||||
[
|
||||
{
|
||||
name: 'seriesType',
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ import {
|
|||
transformTimeseriesAnnotation,
|
||||
} from './transformers';
|
||||
import {
|
||||
OpacityEnum,
|
||||
StackControlsValue,
|
||||
TIMEGRAIN_TO_TIMESTAMP,
|
||||
TIMESERIES_CONSTANTS,
|
||||
|
|
@ -165,6 +166,7 @@ export default function transformProps(
|
|||
sortSeriesAscending,
|
||||
timeGrainSqla,
|
||||
timeCompare,
|
||||
timeShiftColor,
|
||||
stack,
|
||||
tooltipTimeFormat,
|
||||
tooltipSortByMetric,
|
||||
|
|
@ -275,7 +277,7 @@ export default function transformProps(
|
|||
const array = ensureIsArray(chartProps.rawFormData?.time_compare);
|
||||
const inverted = invert(verboseMap);
|
||||
|
||||
const offsetLineWidths = {};
|
||||
const offsetLineWidths: { [key: string]: number } = {};
|
||||
|
||||
rawSeries.forEach(entry => {
|
||||
const derivedSeries = isDerivedSeries(entry, chartProps.rawFormData);
|
||||
|
|
@ -290,6 +292,7 @@ export default function transformProps(
|
|||
}
|
||||
lineStyle.type = 'dashed';
|
||||
lineStyle.width = offsetLineWidths[offset];
|
||||
lineStyle.opacity = OpacityEnum.DerivedSeries;
|
||||
}
|
||||
|
||||
const entryName = String(entry.name || '');
|
||||
|
|
@ -328,6 +331,7 @@ export default function transformProps(
|
|||
isHorizontal,
|
||||
lineStyle,
|
||||
timeCompare: array,
|
||||
timeShiftColor,
|
||||
},
|
||||
);
|
||||
if (transformedSeries) {
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ export function transformSeries(
|
|||
lineStyle?: LineStyleOption;
|
||||
queryIndex?: number;
|
||||
timeCompare?: string[];
|
||||
timeShiftColor?: boolean;
|
||||
},
|
||||
): SeriesOption | undefined {
|
||||
const { name } = series;
|
||||
|
|
@ -190,10 +191,12 @@ export function transformSeries(
|
|||
showValueIndexes = [],
|
||||
thresholdValues = [],
|
||||
richTooltip,
|
||||
seriesKey,
|
||||
sliceId,
|
||||
isHorizontal = false,
|
||||
queryIndex = 0,
|
||||
timeCompare = [],
|
||||
timeShiftColor,
|
||||
} = opts;
|
||||
const contexts = seriesContexts[name || ''] || [];
|
||||
const hasForecast =
|
||||
|
|
@ -209,7 +212,7 @@ export function transformSeries(
|
|||
filterState?.selectedValues && !filterState?.selectedValues.includes(name);
|
||||
const opacity = isFiltered
|
||||
? OpacityEnum.SemiTransparent
|
||||
: OpacityEnum.NonTransparent;
|
||||
: opts.lineStyle?.opacity || OpacityEnum.NonTransparent;
|
||||
|
||||
// don't create a series if doing a stack or area chart and the result
|
||||
// is a confidence band
|
||||
|
|
@ -241,11 +244,22 @@ export function transformSeries(
|
|||
} else {
|
||||
plotType = seriesType === 'bar' ? 'bar' : 'line';
|
||||
}
|
||||
// forcing the colorScale to return a different color for same metrics across different queries
|
||||
const itemStyle = {
|
||||
color: colorScale(colorScaleKey, sliceId),
|
||||
/**
|
||||
* if timeShiftColor is enabled the colorScaleKey forces the color to be the
|
||||
* same as the original series, otherwise uses separate colors
|
||||
* */
|
||||
const itemStyle: ItemStyleOption = {
|
||||
color: timeShiftColor
|
||||
? colorScale(colorScaleKey, sliceId)
|
||||
: colorScale(seriesKey || forecastSeries.name, sliceId),
|
||||
opacity,
|
||||
borderWidth: 0,
|
||||
};
|
||||
if (seriesType === 'bar' && connectNulls) {
|
||||
itemStyle.borderWidth = 1.5;
|
||||
itemStyle.borderType = 'dotted';
|
||||
itemStyle.borderColor = itemStyle.color;
|
||||
}
|
||||
let emphasis = {};
|
||||
let showSymbol = false;
|
||||
if (!isConfidenceBand) {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ export type EchartsTimeseriesFormData = QueryFormData & {
|
|||
annotationLayers: AnnotationLayer[];
|
||||
area: boolean;
|
||||
colorScheme?: string;
|
||||
timeShiftColor?: boolean;
|
||||
contributionMode?: ContributionType;
|
||||
forecastEnabled: boolean;
|
||||
forecastPeriods: number;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ export const LABEL_POSITION: [LabelPositionEnum, string][] = [
|
|||
export enum OpacityEnum {
|
||||
Transparent = 0,
|
||||
SemiTransparent = 0.3,
|
||||
DerivedSeries = 0.7,
|
||||
NonTransparent = 1,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { CategoricalColorScale } from '@superset-ui/core';
|
||||
import { EchartsTimeseriesSeriesType } from '@superset-ui/plugin-chart-echarts';
|
||||
import { transformSeries } from '../../src/Timeseries/transformers';
|
||||
|
||||
// Mock the colorScale function
|
||||
const mockColorScale = jest.fn(
|
||||
(key: string, sliceId?: number) => `color-for-${key}-${sliceId}`,
|
||||
) as unknown as CategoricalColorScale;
|
||||
|
||||
describe('transformSeries', () => {
|
||||
const series = { name: 'test-series' };
|
||||
|
||||
test('should use the colorScaleKey if timeShiftColor is enabled', () => {
|
||||
const opts = {
|
||||
timeShiftColor: true,
|
||||
colorScaleKey: 'test-key',
|
||||
sliceId: 1,
|
||||
};
|
||||
|
||||
const result = transformSeries(series, mockColorScale, 'test-key', opts);
|
||||
|
||||
expect((result as any)?.itemStyle.color).toBe('color-for-test-key-1');
|
||||
});
|
||||
|
||||
test('should use seriesKey if timeShiftColor is not enabled', () => {
|
||||
const opts = {
|
||||
timeShiftColor: false,
|
||||
seriesKey: 'series-key',
|
||||
sliceId: 2,
|
||||
};
|
||||
|
||||
const result = transformSeries(series, mockColorScale, 'test-key', opts);
|
||||
|
||||
expect((result as any)?.itemStyle.color).toBe('color-for-series-key-2');
|
||||
});
|
||||
|
||||
test('should apply border styles for bar series with connectNulls', () => {
|
||||
const opts = {
|
||||
seriesType: EchartsTimeseriesSeriesType.Bar,
|
||||
connectNulls: true,
|
||||
timeShiftColor: false,
|
||||
};
|
||||
|
||||
const result = transformSeries(series, mockColorScale, 'test-key', opts);
|
||||
|
||||
expect((result as any).itemStyle.borderWidth).toBe(1.5);
|
||||
expect((result as any).itemStyle.borderType).toBe('dotted');
|
||||
expect((result as any).itemStyle.borderColor).toBe(
|
||||
(result as any).itemStyle.color,
|
||||
);
|
||||
});
|
||||
|
||||
test('should not apply border styles for non-bar series', () => {
|
||||
const opts = {
|
||||
seriesType: EchartsTimeseriesSeriesType.Line,
|
||||
connectNulls: true,
|
||||
timeShiftColor: false,
|
||||
};
|
||||
|
||||
const result = transformSeries(series, mockColorScale, 'test-key', opts);
|
||||
|
||||
expect((result as any).itemStyle.borderWidth).toBe(0);
|
||||
expect((result as any).itemStyle.borderType).toBeUndefined();
|
||||
expect((result as any).itemStyle.borderColor).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue