This is an automated email from the ASF dual-hosted git repository.

justinpark pushed a commit to branch 4.1-airbnb
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/4.1-airbnb by this push:
     new 21ef47b691 fix: annotations on horizontal bar chart (#31308)
21ef47b691 is described below

commit 21ef47b691388e116d95955070cacf7c5aa2ea89
Author: Damian Pendrak <[email protected]>
AuthorDate: Thu Dec 5 22:20:22 2024 +0100

    fix: annotations on horizontal bar chart (#31308)
    
    (cherry picked from commit 2816a70af3ae0675110c8738246e97ce99c6f9be)
---
 .../src/Timeseries/transformProps.ts               |   4 +
 .../src/Timeseries/transformers.ts                 |  28 +-
 .../test/utils/transformers.test.ts                | 349 +++++++++++++++++++++
 3 files changed, 374 insertions(+), 7 deletions(-)

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 a89439448a..440ec19bd2 100644
--- 
a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
+++ 
b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
@@ -381,6 +381,7 @@ export default function transformProps(
             xAxisType,
             colorScale,
             sliceId,
+            orientation,
           ),
         );
       else if (isIntervalAnnotationLayer(layer)) {
@@ -392,6 +393,7 @@ export default function transformProps(
             colorScale,
             theme,
             sliceId,
+            orientation,
           ),
         );
       } else if (isEventAnnotationLayer(layer)) {
@@ -403,6 +405,7 @@ export default function transformProps(
             colorScale,
             theme,
             sliceId,
+            orientation,
           ),
         );
       } else if (isTimeseriesAnnotationLayer(layer)) {
@@ -414,6 +417,7 @@ export default function transformProps(
             annotationData,
             colorScale,
             sliceId,
+            orientation,
           ),
         );
       }
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 796fab0339..cfb36e111e 100644
--- 
a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts
+++ 
b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts
@@ -53,6 +53,7 @@ import {
   EchartsTimeseriesSeriesType,
   ForecastSeriesEnum,
   LegendOrientation,
+  OrientationType,
   StackType,
 } from '../types';
 
@@ -350,8 +351,11 @@ export function transformFormulaAnnotation(
   xAxisType: AxisType,
   colorScale: CategoricalColorScale,
   sliceId?: number,
+  orientation?: OrientationType,
 ): SeriesOption {
   const { name, color, opacity, width, style } = layer;
+  const isHorizontal = orientation === OrientationType.Horizontal;
+
   return {
     name,
     id: name,
@@ -365,7 +369,9 @@ export function transformFormulaAnnotation(
     },
     type: 'line',
     smooth: true,
-    data: evalFormula(layer, data, xAxisCol, xAxisType),
+    data: evalFormula(layer, data, xAxisCol, xAxisType).map(([x, y]) =>
+      isHorizontal ? [y, x] : [x, y],
+    ),
     symbolSize: 0,
   };
 }
@@ -377,6 +383,7 @@ export function transformIntervalAnnotation(
   colorScale: CategoricalColorScale,
   theme: SupersetTheme,
   sliceId?: number,
+  orientation?: OrientationType,
 ): SeriesOption[] {
   const series: SeriesOption[] = [];
   const annotations = extractRecordAnnotations(layer, annotationData);
@@ -384,6 +391,7 @@ export function transformIntervalAnnotation(
     const { name, color, opacity, showLabel } = layer;
     const { descriptions, intervalEnd, time, title } = annotation;
     const label = formatAnnotationLabel(name, title, descriptions);
+    const isHorizontal = orientation === OrientationType.Horizontal;
     const intervalData: (
       | MarkArea1DDataItemOption
       | MarkArea2DDataItemOption
@@ -391,11 +399,9 @@ export function transformIntervalAnnotation(
       [
         {
           name: label,
-          xAxis: time,
-        },
-        {
-          xAxis: intervalEnd,
+          ...(isHorizontal ? { yAxis: time } : { xAxis: time }),
         },
+        isHorizontal ? { yAxis: intervalEnd } : { xAxis: intervalEnd },
       ],
     ];
     const intervalLabel: SeriesLabelOption = showLabel
@@ -452,6 +458,7 @@ export function transformEventAnnotation(
   colorScale: CategoricalColorScale,
   theme: SupersetTheme,
   sliceId?: number,
+  orientation?: OrientationType,
 ): SeriesOption[] {
   const series: SeriesOption[] = [];
   const annotations = extractRecordAnnotations(layer, annotationData);
@@ -459,10 +466,11 @@ export function transformEventAnnotation(
     const { name, color, opacity, style, width, showLabel } = layer;
     const { descriptions, time, title } = annotation;
     const label = formatAnnotationLabel(name, title, descriptions);
+    const isHorizontal = orientation === OrientationType.Horizontal;
     const eventData: MarkLine1DDataItemOption[] = [
       {
         name: label,
-        xAxis: time,
+        ...(isHorizontal ? { yAxis: time } : { xAxis: time }),
       },
     ];
 
@@ -525,10 +533,12 @@ export function transformTimeseriesAnnotation(
   annotationData: AnnotationData,
   colorScale: CategoricalColorScale,
   sliceId?: number,
+  orientation?: OrientationType,
 ): SeriesOption[] {
   const series: SeriesOption[] = [];
   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;
@@ -536,7 +546,11 @@ export function transformTimeseriesAnnotation(
         type: 'line',
         id: key,
         name: key,
-        data: values.map(row => [row.x, row.y] as [OptionName, number]),
+        data: values.map(({ x, y }) =>
+          isHorizontal
+            ? ([y, x] as [number, OptionName])
+            : ([x, y] as [OptionName, number]),
+        ),
         symbolSize: showMarkers ? markerSize : 0,
         lineStyle: {
           opacity: parseAnnotationOpacity(opacity),
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
new file mode 100644
index 0000000000..113b416f9c
--- /dev/null
+++ 
b/superset-frontend/plugins/plugin-chart-echarts/test/utils/transformers.test.ts
@@ -0,0 +1,349 @@
+/**
+ * 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 {
+  AnnotationData,
+  AnnotationSourceType,
+  AnnotationStyle,
+  AnnotationType,
+  AxisType,
+  CategoricalColorNamespace,
+  EventAnnotationLayer,
+  FormulaAnnotationLayer,
+  IntervalAnnotationLayer,
+  supersetTheme,
+  TimeseriesAnnotationLayer,
+  TimeseriesDataRecord,
+} from '@superset-ui/core';
+import { OrientationType } from '@superset-ui/plugin-chart-echarts';
+import {
+  transformEventAnnotation,
+  transformFormulaAnnotation,
+  transformIntervalAnnotation,
+  transformTimeseriesAnnotation,
+} from '../../src/Timeseries/transformers';
+
+const mockData: TimeseriesDataRecord[] = [
+  {
+    __timestamp: 10,
+  },
+  {
+    __timestamp: 20,
+  },
+];
+
+const mockFormulaAnnotationLayer: FormulaAnnotationLayer = {
+  annotationType: AnnotationType.Formula as const,
+  name: 'My Formula',
+  show: true,
+  style: AnnotationStyle.Solid,
+  value: '50',
+  showLabel: true,
+};
+
+describe('transformFormulaAnnotation', () => {
+  it('should transform data correctly', () => {
+    expect(
+      transformFormulaAnnotation(
+        mockFormulaAnnotationLayer,
+        mockData,
+        '__timestamp',
+        AxisType.Value,
+        CategoricalColorNamespace.getScale(''),
+        undefined,
+      ).data,
+    ).toEqual([
+      [10, 50],
+      [20, 50],
+    ]);
+  });
+
+  it('should swap x and y for horizontal chart', () => {
+    expect(
+      transformFormulaAnnotation(
+        mockFormulaAnnotationLayer,
+        mockData,
+        '__timestamp',
+        AxisType.Value,
+        CategoricalColorNamespace.getScale(''),
+        undefined,
+        OrientationType.Horizontal,
+      ).data,
+    ).toEqual([
+      [50, 10],
+      [50, 20],
+    ]);
+  });
+});
+
+const mockIntervalAnnotationLayer: IntervalAnnotationLayer = {
+  name: 'Interval annotation layer',
+  annotationType: AnnotationType.Interval as const,
+  sourceType: AnnotationSourceType.Native as const,
+  color: null,
+  style: AnnotationStyle.Solid,
+  width: 1,
+  show: true,
+  showLabel: false,
+  value: 1,
+};
+
+const mockIntervalAnnotationData: AnnotationData = {
+  'Interval annotation layer': {
+    records: [
+      {
+        start_dttm: 10,
+        end_dttm: 12,
+        short_descr: 'Timeseries 1',
+        long_descr: '',
+        json_metadata: '',
+      },
+      {
+        start_dttm: 13,
+        end_dttm: 15,
+        short_descr: 'Timeseries 2',
+        long_descr: '',
+        json_metadata: '',
+      },
+    ],
+  },
+};
+
+describe('transformIntervalAnnotation', () => {
+  it('should transform data correctly', () => {
+    expect(
+      transformIntervalAnnotation(
+        mockIntervalAnnotationLayer,
+        mockData,
+        mockIntervalAnnotationData,
+        CategoricalColorNamespace.getScale(''),
+        supersetTheme,
+      )
+        .map(annotation => annotation.markArea)
+        .map(markArea => markArea.data),
+    ).toEqual([
+      [
+        [
+          { name: 'Interval annotation layer - Timeseries 1', xAxis: 10 },
+          { xAxis: 12 },
+        ],
+      ],
+      [
+        [
+          { name: 'Interval annotation layer - Timeseries 2', xAxis: 13 },
+          { xAxis: 15 },
+        ],
+      ],
+    ]);
+  });
+
+  it('should use yAxis for horizontal chart data', () => {
+    expect(
+      transformIntervalAnnotation(
+        mockIntervalAnnotationLayer,
+        mockData,
+        mockIntervalAnnotationData,
+        CategoricalColorNamespace.getScale(''),
+        supersetTheme,
+        undefined,
+        OrientationType.Horizontal,
+      )
+        .map(annotation => annotation.markArea)
+        .map(markArea => markArea.data),
+    ).toEqual([
+      [
+        [
+          { name: 'Interval annotation layer - Timeseries 1', yAxis: 10 },
+          { yAxis: 12 },
+        ],
+      ],
+      [
+        [
+          { name: 'Interval annotation layer - Timeseries 2', yAxis: 13 },
+          { yAxis: 15 },
+        ],
+      ],
+    ]);
+  });
+});
+
+const mockEventAnnotationLayer: EventAnnotationLayer = {
+  annotationType: AnnotationType.Event,
+  color: null,
+  name: 'Event annotation layer',
+  show: true,
+  showLabel: false,
+  sourceType: AnnotationSourceType.Native,
+  style: AnnotationStyle.Solid,
+  value: 1,
+  width: 1,
+};
+
+const mockEventAnnotationData: AnnotationData = {
+  'Event annotation layer': {
+    records: [
+      {
+        start_dttm: 10,
+        end_dttm: 12,
+        short_descr: 'Test annotation',
+        long_descr: '',
+        json_metadata: '',
+      },
+      {
+        start_dttm: 13,
+        end_dttm: 15,
+        short_descr: 'Test annotation 2',
+        long_descr: '',
+        json_metadata: '',
+      },
+    ],
+  },
+};
+
+describe('transformEventAnnotation', () => {
+  it('should transform data correctly', () => {
+    expect(
+      transformEventAnnotation(
+        mockEventAnnotationLayer,
+        mockData,
+        mockEventAnnotationData,
+        CategoricalColorNamespace.getScale(''),
+        supersetTheme,
+      )
+        .map(annotation => annotation.markLine)
+        .map(markLine => markLine.data),
+    ).toEqual([
+      [
+        {
+          name: 'Event annotation layer - Test annotation',
+          xAxis: 10,
+        },
+      ],
+      [{ name: 'Event annotation layer - Test annotation 2', xAxis: 13 }],
+    ]);
+  });
+
+  it('should use yAxis for horizontal chart data', () => {
+    expect(
+      transformEventAnnotation(
+        mockEventAnnotationLayer,
+        mockData,
+        mockEventAnnotationData,
+        CategoricalColorNamespace.getScale(''),
+        supersetTheme,
+        undefined,
+        OrientationType.Horizontal,
+      )
+        .map(annotation => annotation.markLine)
+        .map(markLine => markLine.data),
+    ).toEqual([
+      [
+        {
+          name: 'Event annotation layer - Test annotation',
+          yAxis: 10,
+        },
+      ],
+      [{ name: 'Event annotation layer - Test annotation 2', yAxis: 13 }],
+    ]);
+  });
+});
+
+const mockTimeseriesAnnotationLayer: TimeseriesAnnotationLayer = {
+  annotationType: AnnotationType.Timeseries,
+  color: null,
+  hideLine: false,
+  name: 'Timeseries annotation layer',
+  overrides: {
+    time_range: null,
+  },
+  show: true,
+  showLabel: false,
+  showMarkers: false,
+  sourceType: AnnotationSourceType.Line,
+  style: AnnotationStyle.Solid,
+  value: 1,
+  width: 1,
+};
+
+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,
+        },
+      ],
+    },
+  ],
+};
+
+describe('transformTimeseriesAnnotation', () => {
+  it('should transform data correctly', () => {
+    expect(
+      transformTimeseriesAnnotation(
+        mockTimeseriesAnnotationLayer,
+        1,
+        mockData,
+        mockTimeseriesAnnotationData,
+        CategoricalColorNamespace.getScale(''),
+      ).map(annotation => annotation.data),
+    ).toEqual([
+      [[10, 12]],
+      [
+        [12, 15],
+        [15, 20],
+      ],
+    ]);
+  });
+
+  it('should swap x and y for horizontal chart', () => {
+    expect(
+      transformTimeseriesAnnotation(
+        mockTimeseriesAnnotationLayer,
+        1,
+        mockData,
+        mockTimeseriesAnnotationData,
+        CategoricalColorNamespace.getScale(''),
+        undefined,
+        OrientationType.Horizontal,
+      ).map(annotation => annotation.data),
+    ).toEqual([
+      [[12, 10]],
+      [
+        [15, 12],
+        [20, 15],
+      ],
+    ]);
+  });
+});

Reply via email to