You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by mi...@apache.org on 2025/08/15 16:10:18 UTC
(superset) branch 5.0 updated: fix: Timeseries annotation layers (#34709)
This is an automated email from the ASF dual-hosted git repository.
michaelsmolina pushed a commit to branch 5.0
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/5.0 by this push:
new e986496ef4 fix: Timeseries annotation layers (#34709)
e986496ef4 is described below
commit e986496ef4dac31079f595754bca7cfd838df894
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Fri Aug 15 12:59:30 2025 -0300
fix: Timeseries annotation layers (#34709)
(cherry picked from commit fc95c4fc89effdc9c9558b35e44c0c1f5d83f54c)
---
.../src/query/types/AnnotationLayer.ts | 25 +----------
.../test/query/types/AnnotationLayer.test.ts | 45 -------------------
.../src/MixedTimeseries/transformProps.ts | 2 +-
.../src/Timeseries/Regular/Line/index.ts | 1 +
.../src/Timeseries/transformProps.ts | 2 +-
.../src/Timeseries/transformers.ts | 45 ++++++++++---------
.../plugin-chart-echarts/src/utils/annotation.ts | 52 +++++++++-------------
.../test/Timeseries/transformProps.test.ts | 25 ++++-------
.../test/utils/annotation.test.ts | 17 +------
.../test/utils/transformers.test.ts | 35 ++++-----------
.../AnnotationLayerControl/AnnotationLayer.jsx | 3 +-
11 files changed, 69 insertions(+), 183 deletions(-)
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/AnnotationLayer.ts b/superset-frontend/packages/superset-ui-core/src/query/types/AnnotationLayer.ts
index d5b2981163..24f42ba0d9 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/AnnotationLayer.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/AnnotationLayer.ts
@@ -158,31 +158,10 @@ export function isTableAnnotationLayer(
return layer.sourceType === AnnotationSourceType.Table;
}
-export type RecordAnnotationResult = {
- records: DataRecord[];
+export type AnnotationResult = {
+ records?: DataRecord[];
};
-export type TimeseriesAnnotationResult = {
- key: string;
- values: { x: string | number; y?: number }[];
-}[];
-
-export type AnnotationResult =
- | RecordAnnotationResult
- | TimeseriesAnnotationResult;
-
-export function isTimeseriesAnnotationResult(
- result: AnnotationResult,
-): result is TimeseriesAnnotationResult {
- return Array.isArray(result);
-}
-
-export function isRecordAnnotationResult(
- result: any,
-): result is RecordAnnotationResult {
- return Array.isArray(result?.records);
-}
-
export type AnnotationData = { [key: string]: AnnotationResult };
export type Annotation = {
diff --git a/superset-frontend/packages/superset-ui-core/test/query/types/AnnotationLayer.test.ts b/superset-frontend/packages/superset-ui-core/test/query/types/AnnotationLayer.test.ts
index f6a695eb91..a2752c5cbc 100644
--- a/superset-frontend/packages/superset-ui-core/test/query/types/AnnotationLayer.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/query/types/AnnotationLayer.test.ts
@@ -27,14 +27,10 @@ import {
isEventAnnotationLayer,
isFormulaAnnotationLayer,
isIntervalAnnotationLayer,
- isRecordAnnotationResult,
isTableAnnotationLayer,
isTimeseriesAnnotationLayer,
- isTimeseriesAnnotationResult,
- RecordAnnotationResult,
TableAnnotationLayer,
TimeseriesAnnotationLayer,
- TimeseriesAnnotationResult,
} from '@superset-ui/core';
describe('AnnotationLayer type guards', () => {
@@ -82,23 +78,6 @@ describe('AnnotationLayer type guards', () => {
show: true,
showLabel: false,
};
- const timeseriesAnnotationResult: TimeseriesAnnotationResult = [
- {
- key: 'My Key',
- values: [
- { x: -1000, y: 0 },
- { x: 0, y: 1000 },
- { x: 1000, y: 2000 },
- ],
- },
- ];
- const recordAnnotationResult: RecordAnnotationResult = {
- records: [
- { a: 1, b: 2 },
- { a: 2, b: 3 },
- ],
- };
-
describe('isFormulaAnnotationLayer', () => {
it('should return true when it is the correct type', () => {
expect(isFormulaAnnotationLayer(formulaAnnotationLayer)).toEqual(true);
@@ -161,28 +140,4 @@ describe('AnnotationLayer type guards', () => {
expect(isTableAnnotationLayer(formulaAnnotationLayer)).toEqual(false);
});
});
-
- describe('isTimeseriesAnnotationResult', () => {
- it('should return true when it is the correct type', () => {
- expect(isTimeseriesAnnotationResult(timeseriesAnnotationResult)).toEqual(
- true,
- );
- });
- it('should return false otherwise', () => {
- expect(isTimeseriesAnnotationResult(recordAnnotationResult)).toEqual(
- false,
- );
- });
- });
-
- describe('isRecordAnnotationResult', () => {
- it('should return true when it is the correct type', () => {
- expect(isRecordAnnotationResult(recordAnnotationResult)).toEqual(true);
- });
- it('should return false otherwise', () => {
- expect(isRecordAnnotationResult(timeseriesAnnotationResult)).toEqual(
- false,
- );
- });
- });
});
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
index 7526b820d0..6d50c68f04 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
@@ -662,7 +662,7 @@ export default function transformProps(
ForecastSeriesEnum.Observation,
)
.map(entry => entry.name || '')
- .concat(extractAnnotationLabels(annotationLayers, annotationData)),
+ .concat(extractAnnotationLabels(annotationLayers)),
},
series: dedupSeries(reorderForecastSeries(series) as SeriesOption[]),
toolbox: {
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/index.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/index.ts
index a63168617d..d087b41b85 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/index.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/index.ts
@@ -60,6 +60,7 @@ export default class EchartsTimeseriesLineChartPlugin extends EchartsChartPlugin
'Line chart is used to visualize measurements taken over a given category. Line chart is a type of chart which displays information as a series of data points connected by straight line segments. It is a basic type of chart common in many fields.',
),
exampleGallery: [{ url: example1 }, { url: example2 }],
+ canBeAnnotationTypes: [AnnotationType.Timeseries],
supportedAnnotationTypes: [
AnnotationType.Event,
AnnotationType.Formula,
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
index bb62d6f128..33200a5413 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
@@ -476,7 +476,7 @@ export default function transformProps(
ForecastSeriesEnum.Observation,
)
.map(entry => entry.name || '')
- .concat(extractAnnotationLabels(annotationLayers, annotationData));
+ .concat(extractAnnotationLabels(annotationLayers));
let xAxis: any = {
type: xAxisType,
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts
index 0abefc95e3..9b1f4f973f 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts
@@ -25,7 +25,6 @@ import {
FilterState,
FormulaAnnotationLayer,
IntervalAnnotationLayer,
- isTimeseriesAnnotationResult,
LegendState,
SupersetTheme,
TimeseriesAnnotationLayer,
@@ -554,26 +553,30 @@ export function transformTimeseriesAnnotation(
const { hideLine, name, opacity, showMarkers, style, width, color } = layer;
const result = annotationData[name];
const isHorizontal = orientation === OrientationType.Horizontal;
- if (isTimeseriesAnnotationResult(result)) {
- result.forEach(annotation => {
- const { key, values } = annotation;
- series.push({
- type: 'line',
- id: key,
- name: key,
- data: values.map(({ x, y }) =>
- isHorizontal
- ? ([y, x] as [number, OptionName])
- : ([x, y] as [OptionName, number]),
- ),
- symbolSize: showMarkers ? markerSize : 0,
- lineStyle: {
- opacity: parseAnnotationOpacity(opacity),
- type: style as ZRLineType,
- width: hideLine ? 0 : width,
- color: color || colorScale(name, sliceId),
- },
- });
+ const { records } = result;
+ if (records) {
+ const data = records.map(record => {
+ const keys = Object.keys(record);
+ const x = keys.length > 0 ? record[keys[0]] : 0;
+ const y = keys.length > 1 ? record[keys[1]] : 0;
+ return isHorizontal
+ ? ([y, x] as [number, OptionName])
+ : ([x, y] as [OptionName, number]);
+ });
+ const computedStyle = {
+ opacity: parseAnnotationOpacity(opacity),
+ type: style as ZRLineType,
+ width: hideLine ? 0 : width,
+ color: color || colorScale(name, sliceId),
+ };
+ series.push({
+ type: 'line',
+ id: name,
+ name,
+ data,
+ symbolSize: showMarkers ? markerSize : 0,
+ itemStyle: computedStyle,
+ lineStyle: computedStyle,
});
}
return series;
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/annotation.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/annotation.ts
index df6515d204..90a450c3c3 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/annotation.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/annotation.ts
@@ -29,9 +29,7 @@ import {
DataRecord,
evalExpression,
FormulaAnnotationLayer,
- isRecordAnnotationResult,
isTableAnnotationLayer,
- isTimeseriesAnnotationResult,
} from '@superset-ui/core';
import { EchartsTimeseriesChartProps } from '../types';
import { EchartsMixedTimeseriesProps } from '../MixedTimeseries/types';
@@ -79,27 +77,24 @@ export function extractRecordAnnotations(
): Annotation[] {
const { name } = annotationLayer;
const result = annotationData[name];
- if (isRecordAnnotationResult(result)) {
- const { records } = result;
- const {
- descriptionColumns = [],
- intervalEndColumn = '',
- timeColumn = '',
- titleColumn = '',
- } = isTableAnnotationLayer(annotationLayer)
- ? annotationLayer
- : NATIVE_COLUMN_NAMES;
+ const records = result?.records || [];
+ const {
+ descriptionColumns = [],
+ intervalEndColumn = '',
+ timeColumn = '',
+ titleColumn = '',
+ } = isTableAnnotationLayer(annotationLayer)
+ ? annotationLayer
+ : NATIVE_COLUMN_NAMES;
- return records.map(record => ({
- descriptions: descriptionColumns.map(
- column => (record[column] || '') as string,
- ) as string[],
- intervalEnd: (record[intervalEndColumn] || '') as string,
- time: (record[timeColumn] || '') as string,
- title: (record[titleColumn] || '') as string,
- }));
- }
- throw new Error('Please rerun the query.');
+ return records.map(record => ({
+ descriptions: descriptionColumns.map(
+ column => (record[column] || '') as string,
+ ) as string[],
+ intervalEnd: (record[intervalEndColumn] || '') as string,
+ time: (record[timeColumn] || '') as string,
+ title: (record[titleColumn] || '') as string,
+ }));
}
export function formatAnnotationLabel(
@@ -120,23 +115,16 @@ export function formatAnnotationLabel(
return labels.join('\n\n');
}
-export function extractAnnotationLabels(
- layers: AnnotationLayer[],
- data: AnnotationData,
-): string[] {
+export function extractAnnotationLabels(layers: AnnotationLayer[]): string[] {
const formulaAnnotationLabels = layers
.filter(anno => anno.annotationType === AnnotationType.Formula && anno.show)
.map(anno => anno.name);
+
const timeseriesAnnotationLabels = layers
.filter(
anno => anno.annotationType === AnnotationType.Timeseries && anno.show,
)
- .flatMap(anno => {
- const result = data[anno.name];
- return isTimeseriesAnnotationResult(result)
- ? result.map(annoSeries => annoSeries.key)
- : [];
- });
+ .map(anno => anno.name);
return formulaAnnotationLabels.concat(timeseriesAnnotationLabels);
}
diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts
index bc2aa2bd58..f3f0d10864 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts
@@ -240,21 +240,12 @@ describe('EchartsTimeseries transformProps', () => {
},
],
},
- 'My Timeseries': [
- {
- key: 'My Line',
- values: [
- {
- x: 10000,
- y: 11000,
- },
- {
- x: 20000,
- y: 21000,
- },
- ],
- },
- ],
+ 'My Timeseries': {
+ records: [
+ { x: 10000, y: 11000 },
+ { x: 20000, y: 21000 },
+ ],
+ },
};
const chartProps = new ChartProps({
...chartPropsConfig,
@@ -274,12 +265,12 @@ describe('EchartsTimeseries transformProps', () => {
expect.objectContaining({
echartOptions: expect.objectContaining({
legend: expect.objectContaining({
- data: ['San Francisco', 'New York', 'My Line'],
+ data: ['San Francisco', 'New York', 'My Timeseries'],
}),
series: expect.arrayContaining([
expect.objectContaining({
type: 'line',
- id: 'My Line',
+ id: 'My Timeseries',
}),
expect.objectContaining({
type: 'line',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/utils/annotation.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/utils/annotation.test.ts
index fd2f4acf24..fd54fcd9e1 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/test/utils/annotation.test.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/test/utils/annotation.test.ts
@@ -17,7 +17,6 @@
* under the License.
*/
import {
- AnnotationData,
AnnotationLayer,
AnnotationOpacity,
AnnotationSourceType,
@@ -128,21 +127,7 @@ describe('extractAnnotationLabels', () => {
showLabel: true,
},
];
- const results: AnnotationData = {
- 'My Interval': {
- records: [{ col: 1 }],
- },
- 'My Line': [
- { key: 'Line 1', values: [] },
- { key: 'Line 2', values: [] },
- ],
- };
-
- expect(extractAnnotationLabels(layers, results)).toEqual([
- 'My Formula',
- 'Line 1',
- 'Line 2',
- ]);
+ expect(extractAnnotationLabels(layers)).toEqual(['My Formula', 'My Line']);
});
});
diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/utils/transformers.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/utils/transformers.test.ts
index 113b416f9c..9555abfffc 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/test/utils/transformers.test.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/test/utils/transformers.test.ts
@@ -282,30 +282,13 @@ const mockTimeseriesAnnotationLayer: TimeseriesAnnotationLayer = {
};
const mockTimeseriesAnnotationData: AnnotationData = {
- 'Timeseries annotation layer': [
- {
- key: 'Key 1',
- values: [
- {
- x: 10,
- y: 12,
- },
- ],
- },
- {
- key: 'Key 2',
- values: [
- {
- x: 12,
- y: 15,
- },
- {
- x: 15,
- y: 20,
- },
- ],
- },
- ],
+ 'Timeseries annotation layer': {
+ records: [
+ { x: 10, y: 12 },
+ { x: 12, y: 15 },
+ { x: 15, y: 20 },
+ ],
+ },
};
describe('transformTimeseriesAnnotation', () => {
@@ -319,8 +302,8 @@ describe('transformTimeseriesAnnotation', () => {
CategoricalColorNamespace.getScale(''),
).map(annotation => annotation.data),
).toEqual([
- [[10, 12]],
[
+ [10, 12],
[12, 15],
[15, 20],
],
@@ -339,8 +322,8 @@ describe('transformTimeseriesAnnotation', () => {
OrientationType.Horizontal,
).map(annotation => annotation.data),
).toEqual([
- [[12, 10]],
[
+ [12, 10],
[15, 12],
[20, 15],
],
diff --git a/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.jsx b/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.jsx
index 905fd04f9c..fb3103646c 100644
--- a/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.jsx
+++ b/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.jsx
@@ -31,6 +31,7 @@ import {
styled,
getColumnLabel,
withTheme,
+ VizType,
} from '@superset-ui/core';
import SelectControl from 'src/explore/components/controls/SelectControl';
import { AsyncSelect } from 'src/components';
@@ -246,7 +247,7 @@ class AnnotationLayer extends PureComponent {
chartMetadata.canBeAnnotationType(annotationType),
)
.map(({ key, value: chartMetadata }) => ({
- value: key,
+ value: key === VizType.Line ? 'line' : key,
label: chartMetadata.name,
}));
// Prepend native source if applicable