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(['<NULL>', '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');
+ });
+});