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

rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new a067ffb92d feat(echarts-pie): add string template support for labels 
(#28774)
a067ffb92d is described below

commit a067ffb92d7f3d80bbcf8213d15a8c269c5f263b
Author: Hex CafĂ© <[email protected]>
AuthorDate: Thu Jun 13 00:18:45 2024 +0800

    feat(echarts-pie): add string template support for labels (#28774)
---
 .../plugin-chart-echarts/src/Pie/controlPanel.tsx  |  20 ++++
 .../plugin-chart-echarts/src/Pie/transformProps.ts |  46 +++++++++
 .../plugins/plugin-chart-echarts/src/Pie/types.ts  |   2 +
 .../test/Pie/transformProps.test.ts                | 111 +++++++++++++++++++++
 4 files changed, 179 insertions(+)

diff --git 
a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx 
b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx
index b4ae075b0c..7e700c18c9 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx
@@ -123,11 +123,31 @@ const config: ControlPanelConfig = {
                 ['key_percent', t('Category and Percentage')],
                 ['key_value_percent', t('Category, Value and Percentage')],
                 ['value_percent', t('Value and Percentage')],
+                ['template', t('Template')],
               ],
               description: t('What should be shown on the label?'),
             },
           },
         ],
+        [
+          {
+            name: 'label_template',
+            config: {
+              type: 'TextControl',
+              label: t('Label Template'),
+              renderTrigger: true,
+              description: t(
+                'Format data labels. ' +
+                  'Use variables: {name}, {value}, {percent}. ' +
+                  '\\n represents a new line. ' +
+                  'ECharts compatibility:\n' +
+                  '{a} (series), {b} (name), {c} (value), {d} (percentage)',
+              ),
+              visibility: ({ controls }: ControlPanelsContainerProps) =>
+                controls?.label_type?.value === 'template',
+            },
+          },
+        ],
         [
           {
             name: 'number_format',
diff --git 
a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts 
b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts
index d6400d4022..40d49908e9 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts
@@ -143,6 +143,7 @@ export default function transformProps(
     labelsOutside,
     labelLine,
     labelType,
+    labelTemplate,
     legendMargin,
     legendOrientation,
     legendType,
@@ -242,6 +243,38 @@ export default function transformProps(
     {},
   );
 
+  const formatTemplate = (
+    template: string,
+    formattedParams: {
+      name: string;
+      value: string;
+      percent: string;
+    },
+    rawParams: CallbackDataParams,
+  ) => {
+    // This function supports two forms of template variables:
+    // 1. {name}, {value}, {percent}, for values formatted by number formatter.
+    // 2. {a}, {b}, {c}, {d}, compatible with ECharts formatter.
+    //
+    // \n is supported to represent a new line.
+
+    const items = {
+      '{name}': formattedParams.name,
+      '{value}': formattedParams.value,
+      '{percent}': formattedParams.percent,
+      '{a}': rawParams.seriesName || '',
+      '{b}': rawParams.name,
+      '{c}': `${rawParams.value}`,
+      '{d}': `${rawParams.percent}`,
+      '\\n': '\n',
+    };
+
+    return Object.entries(items).reduce(
+      (acc, [key, value]) => acc.replaceAll(key, value),
+      template,
+    );
+  };
+
   const formatter = (params: CallbackDataParams) => {
     const [name, formattedValue, formattedPercent] = parseParams({
       params,
@@ -262,6 +295,19 @@ export default function transformProps(
         return `${name}: ${formattedPercent}`;
       case EchartsPieLabelType.ValuePercent:
         return `${formattedValue} (${formattedPercent})`;
+      case EchartsPieLabelType.Template:
+        if (!labelTemplate) {
+          return '';
+        }
+        return formatTemplate(
+          labelTemplate,
+          {
+            name,
+            value: formattedValue,
+            percent: formattedPercent,
+          },
+          params,
+        );
       default:
         return name;
     }
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/types.ts 
b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/types.ts
index c72da1feed..06ff2bc41a 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/types.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/types.ts
@@ -38,6 +38,7 @@ export type EchartsPieFormData = QueryFormData &
     innerRadius: number;
     labelLine: boolean;
     labelType: EchartsPieLabelType;
+    labelTemplate: string | null;
     labelsOutside: boolean;
     metric?: string;
     outerRadius: number;
@@ -56,6 +57,7 @@ export enum EchartsPieLabelType {
   KeyPercent = 'key_percent',
   KeyValuePercent = 'key_value_percent',
   ValuePercent = 'value_percent',
+  Template = 'template',
 }
 
 export interface EchartsPieChartProps
diff --git 
a/superset-frontend/plugins/plugin-chart-echarts/test/Pie/transformProps.test.ts
 
b/superset-frontend/plugins/plugin-chart-echarts/test/Pie/transformProps.test.ts
index 1add75d401..e0c1992574 100644
--- 
a/superset-frontend/plugins/plugin-chart-echarts/test/Pie/transformProps.test.ts
+++ 
b/superset-frontend/plugins/plugin-chart-echarts/test/Pie/transformProps.test.ts
@@ -22,6 +22,8 @@ import {
   SqlaFormData,
   supersetTheme,
 } from '@superset-ui/core';
+import { LabelFormatterCallback, PieSeriesOption } from 'echarts';
+import { CallbackDataParams } from 'echarts/types/src/util/types';
 import transformProps, { parseParams } from '../../src/Pie/transformProps';
 import { EchartsPieChartProps } from '../../src/Pie/types';
 
@@ -101,3 +103,112 @@ describe('formatPieLabel', () => {
     ).toEqual(['&lt;NULL&gt;', '1.23k', '12.34%']);
   });
 });
+
+describe('Pie label string template', () => {
+  const params: CallbackDataParams = {
+    componentType: '',
+    componentSubType: '',
+    componentIndex: 0,
+    seriesType: 'pie',
+    seriesIndex: 0,
+    seriesId: 'seriesId',
+    seriesName: 'test',
+    name: 'Tablet',
+    dataIndex: 0,
+    data: {},
+    value: 123456,
+    percent: 55.5,
+    $vars: [],
+  };
+
+  const getChartProps = (form: Partial<SqlaFormData>): EchartsPieChartProps => 
{
+    const formData: SqlaFormData = {
+      colorScheme: 'bnbColors',
+      datasource: '3__table',
+      granularity_sqla: 'ds',
+      metric: 'sum__num',
+      groupby: ['foo', 'bar'],
+      viz_type: 'my_viz',
+      ...form,
+    };
+
+    return new ChartProps({
+      formData,
+      width: 800,
+      height: 600,
+      queriesData: [
+        {
+          data: [
+            { foo: 'Sylvester', bar: 1, sum__num: 10 },
+            { foo: 'Arnold', bar: 2, sum__num: 2.5 },
+          ],
+        },
+      ],
+      theme: supersetTheme,
+    }) as EchartsPieChartProps;
+  };
+
+  const format = (form: Partial<SqlaFormData>) => {
+    const props = transformProps(getChartProps(form));
+    expect(props).toEqual(
+      expect.objectContaining({
+        width: 800,
+        height: 600,
+        echartOptions: expect.objectContaining({
+          series: [
+            expect.objectContaining({
+              avoidLabelOverlap: true,
+              data: expect.arrayContaining([
+                expect.objectContaining({
+                  name: 'Arnold, 2',
+                  value: 2.5,
+                }),
+                expect.objectContaining({
+                  name: 'Sylvester, 1',
+                  value: 10,
+                }),
+              ]),
+              label: expect.objectContaining({
+                formatter: expect.any(Function),
+              }),
+            }),
+          ],
+        }),
+      }),
+    );
+
+    const formatter = (props.echartOptions.series as PieSeriesOption[])[0]!
+      .label?.formatter;
+
+    return (formatter as LabelFormatterCallback)(params);
+  };
+
+  it('should generate a valid pie chart label with template', () => {
+    expect(
+      format({
+        label_type: 'template',
+        label_template: '{name}:{value}\n{percent}',
+      }),
+    ).toEqual('Tablet:123k\n55.50%');
+  });
+
+  it('should be formatted using the number formatter', () => {
+    expect(
+      format({
+        label_type: 'template',
+        label_template: '{name}:{value}\n{percent}',
+        number_format: ',d',
+      }),
+    ).toEqual('Tablet:123,456\n55.50%');
+  });
+
+  it('should be compatible with ECharts raw variable syntax', () => {
+    expect(
+      format({
+        label_type: 'template',
+        label_template: '{b}:{c}\n{d}',
+        number_format: ',d',
+      }),
+    ).toEqual('Tablet:123456\n55.5');
+  });
+});

Reply via email to