Skip to content

Commit

Permalink
feat(explore): Add time shift color control to ECharts (#29897)
Browse files Browse the repository at this point in the history
  • Loading branch information
rtexelm authored Sep 12, 2024
1 parent ff3b86b commit c5594f2
Show file tree
Hide file tree
Showing 14 changed files with 134 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ const config: ControlPanelConfig = {
expanded: true,
controlSetRows: [
['color_scheme'],
['time_shift_color'],
...createCustomizeSection(t('Query A'), ''),
...createCustomizeSection(t('Query B'), 'B'),
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export default function transformProps(
areaB,
annotationLayers,
colorScheme,
timeShiftColor,
contributionMode,
legendOrientation,
legendType,
Expand Down Expand Up @@ -406,6 +407,7 @@ export default function transformProps(
showValueIndexes: showValueIndexesA,
totalStackedValues,
thresholdValues,
timeShiftColor,
},
);
if (transformedSeries) series.push(transformedSeries);
Expand Down Expand Up @@ -455,6 +457,7 @@ export default function transformProps(
showValueIndexes: showValueIndexesB,
totalStackedValues: totalStackedValuesB,
thresholdValues: thresholdValuesB,
timeShiftColor,
},
);
if (transformedSeries) series.push(transformedSeries);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const config: ControlPanelConfig = {
controlSetRows: [
...seriesOrderSection,
['color_scheme'],
['time_shift_color'],
[
{
name: 'seriesType',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ const config: ControlPanelConfig = {
controlSetRows: [
...seriesOrderSection,
['color_scheme'],
['time_shift_color'],
...showValueSection,
[minorTicks],
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const config: ControlPanelConfig = {
controlSetRows: [
...seriesOrderSection,
['color_scheme'],
['time_shift_color'],
[
{
name: 'seriesType',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const config: ControlPanelConfig = {
controlSetRows: [
...seriesOrderSection,
['color_scheme'],
['time_shift_color'],
...showValueSection,
[
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const config: ControlPanelConfig = {
controlSetRows: [
...seriesOrderSection,
['color_scheme'],
['time_shift_color'],
...showValueSectionWithoutStack,
[
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const config: ControlPanelConfig = {
controlSetRows: [
...seriesOrderSection,
['color_scheme'],
['time_shift_color'],
[
{
name: 'seriesType',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ import {
transformTimeseriesAnnotation,
} from './transformers';
import {
OpacityEnum,
StackControlsValue,
TIMEGRAIN_TO_TIMESTAMP,
TIMESERIES_CONSTANTS,
Expand Down Expand Up @@ -165,6 +166,7 @@ export default function transformProps(
sortSeriesAscending,
timeGrainSqla,
timeCompare,
timeShiftColor,
stack,
tooltipTimeFormat,
tooltipSortByMetric,
Expand Down Expand Up @@ -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);
Expand All @@ -290,6 +292,7 @@ export default function transformProps(
}
lineStyle.type = 'dashed';
lineStyle.width = offsetLineWidths[offset];
lineStyle.opacity = OpacityEnum.DerivedSeries;
}

const entryName = String(entry.name || '');
Expand Down Expand Up @@ -328,6 +331,7 @@ export default function transformProps(
isHorizontal,
lineStyle,
timeCompare: array,
timeShiftColor,
},
);
if (transformedSeries) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export function transformSeries(
lineStyle?: LineStyleOption;
queryIndex?: number;
timeCompare?: string[];
timeShiftColor?: boolean;
},
): SeriesOption | undefined {
const { name } = series;
Expand All @@ -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 =
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export type EchartsTimeseriesFormData = QueryFormData & {
annotationLayers: AnnotationLayer[];
area: boolean;
colorScheme?: string;
timeShiftColor?: boolean;
contributionMode?: ContributionType;
forecastEnabled: boolean;
forecastPeriods: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const LABEL_POSITION: [LabelPositionEnum, string][] = [
export enum OpacityEnum {
Transparent = 0,
SemiTransparent = 0.3,
DerivedSeries = 0.7,
NonTransparent = 1,
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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();
});
});

0 comments on commit c5594f2

Please sign in to comment.