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 2023/01/31 16:39:30 UTC

[superset] branch master updated: feat: Adds the ECharts Sunburst chart (#22833)

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

michaelsmolina 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 30abefb519 feat: Adds the ECharts Sunburst chart (#22833)
30abefb519 is described below

commit 30abefb519978e2760a492de51dc0d19803edf3a
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Tue Jan 31 11:39:18 2023 -0500

    feat: Adds the ECharts Sunburst chart (#22833)
---
 .../plugin-chart-echarts/Sunburst/Stories.tsx      |  57 +++++++++++++++++++
 .../plugins/plugin-chart-echarts/Sunburst/data.ts  |  32 +++++++++++
 .../src/Sunburst/EchartsSunburst.tsx               |  28 +++++-----
 .../src/Sunburst/controlPanel.tsx                  |   5 +-
 .../src/Sunburst/images/Sunburst1.png              | Bin 0 -> 130270 bytes
 .../src/Sunburst/images/Sunburst2.png              | Bin 0 -> 126313 bytes
 .../src/Sunburst/images/thumbnail.png              | Bin 5658 -> 130270 bytes
 .../plugin-chart-echarts/src/Sunburst/index.ts     |   6 +-
 .../src/Sunburst/transformProps.ts                 |  55 ++++++++++++------
 .../plugin-chart-echarts/src/Sunburst/types.ts     |   7 +++
 .../plugins/plugin-chart-echarts/src/defaults.ts   |  62 +--------------------
 .../plugin-chart-echarts/src/utils/treeBuilder.ts  |  15 ++---
 .../src/visualizations/presets/MainPreset.js       |   2 +
 13 files changed, 166 insertions(+), 103 deletions(-)

diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/Stories.tsx b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/Stories.tsx
new file mode 100644
index 0000000000..7742f1ecfe
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/Stories.tsx
@@ -0,0 +1,57 @@
+/*
+ * 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 React from 'react';
+import { SuperChart, getChartTransformPropsRegistry } from '@superset-ui/core';
+import { boolean, withKnobs } from '@storybook/addon-knobs';
+import {
+  EchartsSunburstChartPlugin,
+  SunburstTransformProps,
+} from '@superset-ui/plugin-chart-echarts';
+import { withResizableChartDemo } from '../../../../shared/components/ResizableChartDemo';
+import data from './data';
+
+new EchartsSunburstChartPlugin()
+  .configure({ key: 'echarts-sunburst' })
+  .register();
+
+getChartTransformPropsRegistry().registerValue(
+  'echarts-sunburst',
+  SunburstTransformProps,
+);
+
+export default {
+  title: 'Chart Plugins/plugin-chart-echarts/Sunburst',
+  decorators: [withKnobs, withResizableChartDemo],
+};
+
+export const Sunburst = ({ width, height }) => (
+  <SuperChart
+    chartType="echarts-sunburst"
+    width={width}
+    height={height}
+    queriesData={[{ data }]}
+    formData={{
+      columns: ['genre', 'platform'],
+      metric: 'count',
+      showLabels: boolean('Show labels', true),
+      showTotal: boolean('Show total', true),
+    }}
+  />
+);
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/data.ts b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/data.ts
new file mode 100644
index 0000000000..35675465df
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/data.ts
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+export default [
+  { genre: 'Adventure', platform: 'Wii', count: 84 },
+  { genre: 'Adventure', platform: 'N64', count: 14 },
+  { genre: 'Adventure', platform: 'XOne', count: 12 },
+  { genre: 'Adventure', platform: 'PS4', count: 19 },
+  { genre: 'Strategy', platform: 'Wii', count: 25 },
+  { genre: 'Strategy', platform: 'PS4', count: 15 },
+  { genre: 'Strategy', platform: 'N64', count: 29 },
+  { genre: 'Strategy', platform: 'XOne', count: 23 },
+  { genre: 'Simulation', platform: 'PS4', count: 15 },
+  { genre: 'Simulation', platform: 'XOne', count: 36 },
+  { genre: 'Simulation', platform: 'N64', count: 20 },
+  { genre: 'Simulation', platform: 'Wii', count: 50 },
+];
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/EchartsSunburst.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/EchartsSunburst.tsx
index 3dd8dc931a..390c830c45 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/EchartsSunburst.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/EchartsSunburst.tsx
@@ -94,22 +94,22 @@ export default function EchartsSunburst(props: SunburstTransformedProps) {
     contextmenu: eventParams => {
       if (onContextMenu) {
         eventParams.event.stop();
+        const { data } = eventParams;
+        const { records } = data;
         const treePath = extractTreePathInfo(eventParams.treePathInfo);
-        if (treePath.length > 0) {
-          const pointerEvent = eventParams.event.event;
-          const filters: BinaryQueryObjectFilterClause[] = [];
-          if (columns) {
-            treePath.forEach((path, i) =>
-              filters.push({
-                col: columns[i],
-                op: '==',
-                val: path,
-                formattedVal: path,
-              }),
-            );
-          }
-          onContextMenu(pointerEvent.clientX, pointerEvent.clientY, filters);
+        const pointerEvent = eventParams.event.event;
+        const filters: BinaryQueryObjectFilterClause[] = [];
+        if (columns?.length) {
+          treePath.forEach((path, i) =>
+            filters.push({
+              col: columns[i],
+              op: '==',
+              val: records[i],
+              formattedVal: path,
+            }),
+          );
         }
+        onContextMenu(pointerEvent.clientX, pointerEvent.clientY, filters);
       }
     },
   };
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/controlPanel.tsx
index 7ad3618a5a..2c90dff452 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/controlPanel.tsx
@@ -189,9 +189,10 @@ const config: ControlPanelConfig = {
             controls?.secondary_metric?.value !== controls?.metric.value,
         ),
     },
-    groupby: {
+    columns: {
       label: t('Hierarchy'),
-      description: t('This defines the level of the hierarchy'),
+      description: t(`Sets the hierarchy levels of the chart. Each level is
+        represented by one ring with the innermost circle as the top of the hierarchy.`),
     },
   },
   formDataOverrides: formData => ({
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/images/Sunburst1.png b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/images/Sunburst1.png
new file mode 100644
index 0000000000..87c140e7c3
Binary files /dev/null and b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/images/Sunburst1.png differ
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/images/Sunburst2.png b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/images/Sunburst2.png
new file mode 100644
index 0000000000..677b2a1966
Binary files /dev/null and b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/images/Sunburst2.png differ
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/images/thumbnail.png b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/images/thumbnail.png
index 7afef30bd4..87c140e7c3 100644
Binary files a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/images/thumbnail.png and b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/images/thumbnail.png differ
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/index.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/index.ts
index fe75a7916f..5ca8d5a8fc 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/index.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/index.ts
@@ -21,6 +21,8 @@ import transformProps from './transformProps';
 import thumbnail from './images/thumbnail.png';
 import controlPanel from './controlPanel';
 import buildQuery from './buildQuery';
+import example1 from './images/Sunburst1.png';
+import example2 from './images/Sunburst2.png';
 
 export default class EchartsSunburstChartPlugin extends ChartPlugin {
   constructor() {
@@ -29,13 +31,13 @@ export default class EchartsSunburstChartPlugin extends ChartPlugin {
       controlPanel,
       loadChart: () => import('./EchartsSunburst'),
       metadata: new ChartMetadata({
-        behaviors: [Behavior.INTERACTIVE_CHART],
+        behaviors: [Behavior.INTERACTIVE_CHART, Behavior.DRILL_TO_DETAIL],
         category: t('Part of a Whole'),
         credits: ['https://echarts.apache.org'],
         description: t(
           'Uses circles to visualize the flow of data through different stages of a system. Hover over individual paths in the visualization to understand the stages a value took. Useful for multi-stage, multi-group visualizing funnels and pipelines.',
         ),
-        exampleGallery: [],
+        exampleGallery: [{ url: example1 }, { url: example2 }],
         name: t('Sunburst Chart v2'),
         tags: [
           t('ECharts'),
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts
index 9873d1dc32..51e89f8c6c 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts
@@ -16,9 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 import {
   CategoricalColorNamespace,
+  DataRecordValue,
   getColumnLabel,
   getMetricLabel,
   getNumberFormatter,
@@ -26,21 +26,23 @@ import {
   getTimeFormatter,
   NumberFormats,
   NumberFormatter,
+  SupersetTheme,
   t,
 } from '@superset-ui/core';
 import { EChartsCoreOption } from 'echarts';
-import { SunburstSeriesNodeItemOption } from 'echarts/types/src/chart/sunburst/SunburstSeries';
 import { CallbackDataParams } from 'echarts/types/src/util/types';
 import { OpacityEnum } from '../constants';
-import { defaultGrid, defaultTooltip } from '../defaults';
+import { defaultGrid } from '../defaults';
 import { Refs } from '../types';
 import { formatSeriesName, getColtypesMapping } from '../utils/series';
 import { treeBuilder, TreeNode } from '../utils/treeBuilder';
 import {
   EchartsSunburstChartProps,
   EchartsSunburstLabelType,
+  NodeItemOption,
   SunburstTransformedProps,
 } from './types';
+import { getDefaultTooltip } from '../utils/tooltip';
 
 export function getLinearDomain(
   treeData: TreeNode[],
@@ -96,6 +98,7 @@ export function formatTooltip({
   totalValue,
   metricLabel,
   secondaryMetricLabel,
+  theme,
 }: {
   params: CallbackDataParams & {
     treePathInfo: {
@@ -109,9 +112,9 @@ export function formatTooltip({
   totalValue: number;
   metricLabel: string;
   secondaryMetricLabel?: string;
+  theme: SupersetTheme;
 }): string {
   const { data, treePathInfo = [] } = params;
-  treePathInfo.shift();
   const node = data as TreeNode;
   const formattedValue = numberFormatter(node.value);
   const formattedSecondaryValue = numberFormatter(node.secondaryValue);
@@ -121,33 +124,43 @@ export function formatTooltip({
     node.secondaryValue / node.value,
   );
   const absolutePercentage = percentFormatter(node.value / totalValue);
-  const parentNode = treePathInfo[treePathInfo.length - 1];
+  const parentNode =
+    treePathInfo.length > 2 ? treePathInfo[treePathInfo.length - 2] : undefined;
+
   const result = [
-    `<div style="font-size: 14px;font-weight: 600">${absolutePercentage} of total</div>`,
+    `<div style="
+      font-size: ${theme.typography.sizes.m}px;
+      color: ${theme.colors.grayscale.base}"
+     >`,
+    `<div style="font-weight: ${theme.typography.weights.bold}">
+      ${node.name}
+     </div>`,
+    `<div">
+      ${absolutePercentage} of total
+     </div>`,
   ];
   if (parentNode) {
     const conditionalPercentage = percentFormatter(
       node.value / parentNode.value,
     );
     result.push(`
-    <div style="font-size: 12px;">
-      ${conditionalPercentage} of parent
+    <div>
+      ${conditionalPercentage} of ${parentNode.name}
     </div>`);
   }
   result.push(
-    `<div style="color: '#666666'">
+    `<div>
     ${metricLabel}: ${formattedValue}${
       colorByCategory
         ? ''
         : `, ${secondaryMetricLabel}: ${formattedSecondaryValue}`
     }
-    </div>`,
+     </div>`,
     colorByCategory
       ? ''
-      : `<div style="color: '#666666'">
-       ${metricLabel}/${secondaryMetricLabel}: ${compareValuePercentage}
-      </div>`,
+      : `<div>${metricLabel}/${secondaryMetricLabel}: ${compareValuePercentage}</div>`,
   );
+  result.push('</div>');
   return result.join('\n');
 }
 
@@ -247,9 +260,14 @@ export default function transformProps(
     linearColorScale(totalSecondaryValue / totalValue);
   }
 
-  const traverse = (treeNodes: TreeNode[], path: string[]) =>
+  const traverse = (
+    treeNodes: TreeNode[],
+    path: string[],
+    pathRecords?: DataRecordValue[],
+  ) =>
     treeNodes.map(treeNode => {
       const { name: nodeName, value, secondaryValue, groupBy } = treeNode;
+      const records = [...(pathRecords || []), nodeName];
       let name = formatSeriesName(nodeName, {
         numberFormatter,
         timeFormatter: getTimeFormatter(dateFormat),
@@ -258,10 +276,10 @@ export default function transformProps(
         }),
       });
       const newPath = path.concat(name);
-      let item: SunburstSeriesNodeItemOption = {
+      let item: NodeItemOption = {
+        records,
         name,
         value,
-        // @ts-ignore
         secondaryValue,
         itemStyle: {
           color: colorByCategory
@@ -270,7 +288,7 @@ export default function transformProps(
         },
       };
       if (treeNode.children?.length) {
-        item.children = traverse(treeNode.children, newPath);
+        item.children = traverse(treeNode.children, newPath, records);
       } else {
         name = newPath.join(',');
       }
@@ -295,7 +313,7 @@ export default function transformProps(
       ...defaultGrid,
     },
     tooltip: {
-      ...defaultTooltip,
+      ...getDefaultTooltip(refs),
       show: !inContextMenu,
       trigger: 'item',
       formatter: (params: any) =>
@@ -306,6 +324,7 @@ export default function transformProps(
           totalValue,
           metricLabel,
           secondaryMetricLabel,
+          theme,
         }),
     },
     series: [
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/types.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/types.ts
index 91030fd7b5..37844addea 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/types.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/types.ts
@@ -20,10 +20,12 @@
 import {
   ChartDataResponseResult,
   ChartProps,
+  DataRecordValue,
   QueryFormColumn,
   QueryFormData,
   QueryFormMetric,
 } from '@superset-ui/core';
+import { SunburstSeriesNodeItemOption } from 'echarts/types/src/chart/sunburst/SunburstSeries';
 import {
   BaseTransformedProps,
   ContextMenuTransformedProps,
@@ -62,3 +64,8 @@ export type SunburstTransformedProps =
   BaseTransformedProps<EchartsSunburstFormData> &
     ContextMenuTransformedProps &
     CrossFilterTransformedProps;
+
+export type NodeItemOption = SunburstSeriesNodeItemOption & {
+  records: DataRecordValue[];
+  secondaryValue: number;
+};
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/defaults.ts b/superset-frontend/plugins/plugin-chart-echarts/src/defaults.ts
index d76de5b53d..c5ada14932 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/defaults.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/defaults.ts
@@ -1,7 +1,3 @@
-import { CallbackDataParams } from 'echarts/types/src/util/types';
-import { LegendOrientation } from './types';
-import { TOOLTIP_POINTER_MARGIN, TOOLTIP_OVERFLOW_MARGIN } from './constants';
-
 /**
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -20,66 +16,12 @@ import { TOOLTIP_POINTER_MARGIN, TOOLTIP_OVERFLOW_MARGIN } from './constants';
  * specific language governing permissions and limitations
  * under the License.
  */
+import { LegendOrientation } from './types';
+
 export const defaultGrid = {
   containLabel: true,
 };
 
-export const defaultTooltip = {
-  position: (
-    canvasMousePos: [number, number],
-    params: CallbackDataParams,
-    tooltipDom: HTMLElement,
-    rect: any,
-    sizes: { contentSize: [number, number]; viewSize: [number, number] },
-  ) => {
-    // algorithm copy-pasted from here:
-    // https://github.com/apache/echarts/issues/5004#issuecomment-559668309
-
-    // The chart canvas position
-    const canvasRect = tooltipDom.parentElement
-      ?.getElementsByTagName('canvas')[0]
-      .getBoundingClientRect();
-
-    // The mouse coordinates relative to the whole window
-    // The first parameter to the position function is the mouse position relative to the canvas
-    const mouseX = canvasMousePos[0] + (canvasRect?.x || 0);
-    const mouseY = canvasMousePos[1] + (canvasRect?.y || 0);
-
-    // The width and height of the tooltip dom element
-    const tooltipWidth = sizes.contentSize[0];
-    const tooltipHeight = sizes.contentSize[1];
-
-    // Start by placing the tooltip top and right relative to the mouse position
-    let xPos = mouseX + TOOLTIP_POINTER_MARGIN;
-    let yPos = mouseY - TOOLTIP_POINTER_MARGIN - tooltipHeight;
-
-    // The tooltip is overflowing past the right edge of the window
-    if (xPos + tooltipWidth >= document.documentElement.clientWidth) {
-      // Attempt to place the tooltip to the left of the mouse position
-      xPos = mouseX - TOOLTIP_POINTER_MARGIN - tooltipWidth;
-
-      // The tooltip is overflowing past the left edge of the window
-      if (xPos <= 0)
-        // Place the tooltip a fixed distance from the left edge of the window
-        xPos = TOOLTIP_OVERFLOW_MARGIN;
-    }
-
-    // The tooltip is overflowing past the top edge of the window
-    if (yPos <= 0) {
-      // Attempt to place the tooltip to the bottom of the mouse position
-      yPos = mouseY + TOOLTIP_POINTER_MARGIN;
-
-      // The tooltip is overflowing past the bottom edge of the window
-      if (yPos + tooltipHeight >= document.documentElement.clientHeight)
-        // Place the tooltip a fixed distance from the top edge of the window
-        yPos = TOOLTIP_OVERFLOW_MARGIN;
-    }
-
-    // Return the position (converted back to a relative position on the canvas)
-    return [xPos - (canvasRect?.x || 0), yPos - (canvasRect?.y || 0)];
-  },
-};
-
 export const defaultYAxis = {
   scale: true,
 };
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/treeBuilder.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/treeBuilder.ts
index 32e0416a6b..97916997d4 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/treeBuilder.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/treeBuilder.ts
@@ -16,17 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { DataRecord } from '@superset-ui/core';
+import { DataRecord, DataRecordValue } from '@superset-ui/core';
 import _ from 'lodash';
 
 export type TreeNode = {
-  name: string;
+  name: DataRecordValue;
   value: number;
   secondaryValue: number;
   groupBy: string;
   children?: TreeNode[];
 };
 
+function getMetricValue(datum: DataRecord, metric: string) {
+  return _.isNumber(datum[metric]) ? (datum[metric] as number) : 0;
+}
+
 export function treeBuilder(
   data: DataRecord[],
   groupBy: string[],
@@ -37,7 +41,8 @@ export function treeBuilder(
   const curData = _.groupBy(data, curGroupBy);
   return _.transform(
     curData,
-    (result, value, name) => {
+    (result, value, key) => {
+      const name = curData[key][0][curGroupBy]!;
       if (!restGroupby.length) {
         (value ?? []).forEach(datum => {
           const metricValue = getMetricValue(datum, metric);
@@ -81,7 +86,3 @@ export function treeBuilder(
     [] as TreeNode[],
   );
 }
-
-function getMetricValue(datum: DataRecord, metric: string) {
-  return _.isNumber(datum[metric]) ? (datum[metric] as number) : 0;
-}
diff --git a/superset-frontend/src/visualizations/presets/MainPreset.js b/superset-frontend/src/visualizations/presets/MainPreset.js
index 88c1975d01..2bce5ae096 100644
--- a/superset-frontend/src/visualizations/presets/MainPreset.js
+++ b/superset-frontend/src/visualizations/presets/MainPreset.js
@@ -68,6 +68,7 @@ import {
   EchartsTreemapChartPlugin,
   EchartsMixedTimeseriesChartPlugin,
   EchartsTreeChartPlugin,
+  EchartsSunburstChartPlugin,
 } from '@superset-ui/plugin-chart-echarts';
 import {
   SelectFilterPlugin,
@@ -165,6 +166,7 @@ export default class MainPreset extends Preset {
         new TimeColumnFilterPlugin().configure({ key: 'filter_timecolumn' }),
         new TimeGrainFilterPlugin().configure({ key: 'filter_timegrain' }),
         new EchartsTreeChartPlugin().configure({ key: 'tree_chart' }),
+        new EchartsSunburstChartPlugin().configure({ key: 'sunburst_v2' }),
         new HandlebarsChartPlugin().configure({ key: 'handlebars' }),
         ...experimentalplugins,
       ],