feat: Adds the ECharts Bubble chart (#22107)
Co-authored-by: Michael S. Molina <michael.s.molina@gmail.com>
This commit is contained in:
parent
50b0816e37
commit
c81c60c91f
|
|
@ -0,0 +1,128 @@
|
|||
/**
|
||||
* 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,
|
||||
number,
|
||||
select,
|
||||
text,
|
||||
withKnobs,
|
||||
} from '@storybook/addon-knobs';
|
||||
import {
|
||||
EchartsBubbleChartPlugin,
|
||||
BubbleTransformProps,
|
||||
} from '@superset-ui/plugin-chart-echarts';
|
||||
import { simpleBubbleData } from './data';
|
||||
import { withResizableChartDemo } from '../../../../shared/components/ResizableChartDemo';
|
||||
|
||||
new EchartsBubbleChartPlugin().configure({ key: 'bubble_v2' }).register();
|
||||
|
||||
getChartTransformPropsRegistry().registerValue(
|
||||
'bubble_v2',
|
||||
BubbleTransformProps,
|
||||
);
|
||||
|
||||
export default {
|
||||
title: 'Chart Plugins/plugin-chart-echarts/Bubble',
|
||||
decorators: [withKnobs, withResizableChartDemo],
|
||||
};
|
||||
|
||||
export const SimpleBubble = ({ width, height }) => (
|
||||
<SuperChart
|
||||
chartType="bubble_v2"
|
||||
width={width}
|
||||
height={height}
|
||||
queriesData={[{ data: simpleBubbleData }]}
|
||||
formData={{
|
||||
entity: 'customer_name',
|
||||
x: 'count',
|
||||
y: {
|
||||
aggregate: 'SUM',
|
||||
column: {
|
||||
advanced_data_type: null,
|
||||
certification_details: null,
|
||||
certified_by: null,
|
||||
column_name: 'price_each',
|
||||
description: null,
|
||||
expression: null,
|
||||
filterable: true,
|
||||
groupby: true,
|
||||
id: 570,
|
||||
is_certified: false,
|
||||
is_dttm: false,
|
||||
python_date_format: null,
|
||||
type: 'DOUBLE PRECISION',
|
||||
type_generic: 0,
|
||||
verbose_name: null,
|
||||
warning_markdown: null,
|
||||
},
|
||||
expressionType: 'SIMPLE',
|
||||
hasCustomLabel: false,
|
||||
isNew: false,
|
||||
label: 'SUM(price_each)',
|
||||
optionName: 'metric_d9rpclvys0a_fs4bs0m2l1f',
|
||||
sqlExpression: null,
|
||||
},
|
||||
adhocFilters: [],
|
||||
size: {
|
||||
aggregate: 'SUM',
|
||||
column: {
|
||||
advanced_data_type: null,
|
||||
certification_details: null,
|
||||
certified_by: null,
|
||||
column_name: 'sales',
|
||||
description: null,
|
||||
expression: null,
|
||||
filterable: true,
|
||||
groupby: true,
|
||||
id: 571,
|
||||
is_certified: false,
|
||||
is_dttm: false,
|
||||
python_date_format: null,
|
||||
type: 'DOUBLE PRECISION',
|
||||
type_generic: 0,
|
||||
verbose_name: null,
|
||||
warning_markdown: null,
|
||||
},
|
||||
expressionType: 'SIMPLE',
|
||||
hasCustomLabel: false,
|
||||
isNew: false,
|
||||
label: 'SUM(sales)',
|
||||
optionName: 'metric_itj9wncjxk_dp3yibib0q',
|
||||
sqlExpression: null,
|
||||
},
|
||||
limit: 10,
|
||||
colorScheme: 'supersetColors',
|
||||
maxBubbleSize: select('Max bubble size', [5, 10, 25, 50, 100, 125], 10),
|
||||
xAxisTitle: text('X axis title', ''),
|
||||
xAxisTitleMargin: number('X axis title margin', 30),
|
||||
yAxisTitle: text('Y axis title', ''),
|
||||
yAxisTitleMargin: number('Y axis title margin', 30),
|
||||
yAxisTitlePosition: 'Left',
|
||||
xAxisFormat: null,
|
||||
logYAxis: boolean('Log Y axis', false),
|
||||
yAxisFormat: null,
|
||||
logXAxis: boolean('Log X axis', false),
|
||||
truncateYAxis: false,
|
||||
yAxisBounds: [],
|
||||
extraFormData: {},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* 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 const simpleBubbleData = [
|
||||
{
|
||||
customer_name: 'AV Stores, Co.',
|
||||
count: 51,
|
||||
'SUM(price_each)': 3975.33,
|
||||
'SUM(sales)': 157807.80999999997,
|
||||
},
|
||||
{
|
||||
customer_name: 'Alpha Cognac',
|
||||
count: 20,
|
||||
'SUM(price_each)': 1701.95,
|
||||
'SUM(sales)': 70488.44,
|
||||
},
|
||||
{
|
||||
customer_name: 'Amica Models & Co.',
|
||||
count: 26,
|
||||
'SUM(price_each)': 2218.41,
|
||||
'SUM(sales)': 94117.26000000002,
|
||||
},
|
||||
{
|
||||
customer_name: "Anna's Decorations, Ltd",
|
||||
count: 46,
|
||||
'SUM(price_each)': 3843.67,
|
||||
'SUM(sales)': 153996.13000000003,
|
||||
},
|
||||
{
|
||||
customer_name: 'Atelier graphique',
|
||||
count: 7,
|
||||
'SUM(price_each)': 558.4300000000001,
|
||||
'SUM(sales)': 24179.96,
|
||||
},
|
||||
{
|
||||
customer_name: 'Australian Collectables, Ltd',
|
||||
count: 23,
|
||||
'SUM(price_each)': 1809.7099999999998,
|
||||
'SUM(sales)': 64591.46000000001,
|
||||
},
|
||||
{
|
||||
customer_name: 'Australian Collectors, Co.',
|
||||
count: 55,
|
||||
'SUM(price_each)': 4714.479999999999,
|
||||
'SUM(sales)': 200995.40999999997,
|
||||
},
|
||||
{
|
||||
customer_name: 'Australian Gift Network, Co',
|
||||
count: 15,
|
||||
'SUM(price_each)': 1271.05,
|
||||
'SUM(sales)': 59469.11999999999,
|
||||
},
|
||||
{
|
||||
customer_name: 'Auto Assoc. & Cie.',
|
||||
count: 18,
|
||||
'SUM(price_each)': 1484.8600000000001,
|
||||
'SUM(sales)': 64834.32000000001,
|
||||
},
|
||||
{
|
||||
customer_name: 'Auto Canal Petit',
|
||||
count: 27,
|
||||
'SUM(price_each)': 2188.82,
|
||||
'SUM(sales)': 93170.65999999999,
|
||||
},
|
||||
];
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { t, ChartMetadata, ChartPlugin } from '@superset-ui/core';
|
||||
import { t, ChartMetadata, ChartPlugin, ChartLabel } from '@superset-ui/core';
|
||||
import transformProps from '../transformProps';
|
||||
import example from './images/example.jpg';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
|
|
@ -29,7 +29,8 @@ const metadata = new ChartMetadata({
|
|||
'Visualizes a metric across three dimensions of data in a single chart (X axis, Y axis, and bubble size). Bubbles from the same group can be showcased using bubble color.',
|
||||
),
|
||||
exampleGallery: [{ url: example }],
|
||||
name: t('Bubble Chart'),
|
||||
label: ChartLabel.DEPRECATED,
|
||||
name: t('Bubble Chart (legacy)'),
|
||||
tags: [
|
||||
t('Multi-Dimensions'),
|
||||
t('Aesthetic'),
|
||||
|
|
@ -39,11 +40,15 @@ const metadata = new ChartMetadata({
|
|||
t('Time'),
|
||||
t('Trend'),
|
||||
t('nvd3'),
|
||||
t('Deprecated'),
|
||||
],
|
||||
thumbnail,
|
||||
useLegacyApi: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* @deprecated in version 4.0.
|
||||
*/
|
||||
export default class BubbleChartPlugin extends ChartPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* 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 { BubbleChartTransformedProps } from './types';
|
||||
import Echart from '../components/Echart';
|
||||
|
||||
export default function EchartsBubble(props: BubbleChartTransformedProps) {
|
||||
const { height, width, echartOptions, refs } = props;
|
||||
return (
|
||||
<Echart
|
||||
height={height}
|
||||
width={width}
|
||||
echartOptions={echartOptions}
|
||||
refs={refs}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* 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 {
|
||||
buildQueryContext,
|
||||
ensureIsArray,
|
||||
QueryFormData,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
export default function buildQuery(formData: QueryFormData) {
|
||||
const columns = [
|
||||
...ensureIsArray(formData.entity),
|
||||
...ensureIsArray(formData.series),
|
||||
];
|
||||
|
||||
return buildQueryContext(formData, baseQueryObject => [
|
||||
{
|
||||
...baseQueryObject,
|
||||
columns,
|
||||
orderby: baseQueryObject.orderby
|
||||
? [[baseQueryObject.orderby[0], !baseQueryObject.order_desc]]
|
||||
: undefined,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* 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 { DEFAULT_LEGEND_FORM_DATA } from '../constants';
|
||||
import { EchartsBubbleFormData } from './types';
|
||||
|
||||
export const DEFAULT_FORM_DATA: Partial<EchartsBubbleFormData> = {
|
||||
...DEFAULT_LEGEND_FORM_DATA,
|
||||
emitFilter: false,
|
||||
logXAis: false,
|
||||
logYAxis: false,
|
||||
xAxisTitleMargin: 30,
|
||||
yAxisTitleMargin: 30,
|
||||
truncateYAxis: false,
|
||||
yAxisBounds: [null, null],
|
||||
xAxisLabelRotation: 0,
|
||||
opacity: 0.6,
|
||||
};
|
||||
|
||||
export const MINIMUM_BUBBLE_SIZE = 5;
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
/**
|
||||
* 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 { t } from '@superset-ui/core';
|
||||
import {
|
||||
ControlPanelConfig,
|
||||
formatSelectOptions,
|
||||
sections,
|
||||
ControlPanelsContainerProps,
|
||||
sharedControls,
|
||||
} from '@superset-ui/chart-controls';
|
||||
|
||||
import { DEFAULT_FORM_DATA } from './constants';
|
||||
import { legendSection } from '../controls';
|
||||
|
||||
const { logAxis, truncateYAxis, yAxisBounds, xAxisLabelRotation, opacity } =
|
||||
DEFAULT_FORM_DATA;
|
||||
|
||||
const config: ControlPanelConfig = {
|
||||
controlPanelSections: [
|
||||
{
|
||||
label: t('Query'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
['series'],
|
||||
['entity'],
|
||||
['x'],
|
||||
['y'],
|
||||
['adhoc_filters'],
|
||||
['size'],
|
||||
['orderby'],
|
||||
[
|
||||
{
|
||||
name: 'order_desc',
|
||||
config: {
|
||||
...sharedControls.order_desc,
|
||||
visibility: ({ controls }) => Boolean(controls.orderby.value),
|
||||
},
|
||||
},
|
||||
],
|
||||
['row_limit'],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Chart Options'),
|
||||
expanded: true,
|
||||
tabOverride: 'customize',
|
||||
controlSetRows: [
|
||||
['color_scheme'],
|
||||
...legendSection,
|
||||
[
|
||||
{
|
||||
name: 'max_bubble_size',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
renderTrigger: true,
|
||||
freeForm: true,
|
||||
label: t('Max Bubble Size'),
|
||||
default: '25',
|
||||
choices: formatSelectOptions([
|
||||
'5',
|
||||
'10',
|
||||
'15',
|
||||
'25',
|
||||
'50',
|
||||
'75',
|
||||
'100',
|
||||
]),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'tooltipSizeFormat',
|
||||
config: {
|
||||
...sharedControls.y_axis_format,
|
||||
label: t('Bubble size number format'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'opacity',
|
||||
config: {
|
||||
type: 'SliderControl',
|
||||
label: t('Bubble Opacity'),
|
||||
renderTrigger: true,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.1,
|
||||
default: opacity,
|
||||
description: t(
|
||||
'Opacity of bubbles, 0 means completely transparent, 1 means opaque',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('X Axis'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'x_axis_label',
|
||||
config: {
|
||||
type: 'TextControl',
|
||||
label: t('X Axis Title'),
|
||||
renderTrigger: true,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'xAxisLabelRotation',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: false,
|
||||
label: t('Rotate x axis label'),
|
||||
choices: [
|
||||
[0, '0°'],
|
||||
[45, '45°'],
|
||||
],
|
||||
default: xAxisLabelRotation,
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'Input field supports custom rotation. e.g. 30 for 30°',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'x_axis_title_margin',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: true,
|
||||
label: t('X AXIS TITLE MARGIN'),
|
||||
renderTrigger: true,
|
||||
default: sections.TITLE_MARGIN_OPTIONS[1],
|
||||
choices: formatSelectOptions(sections.TITLE_MARGIN_OPTIONS),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'xAxisFormat',
|
||||
config: {
|
||||
...sharedControls.y_axis_format,
|
||||
label: t('X Axis Format'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'logXAxis',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Logarithmic x-axis'),
|
||||
renderTrigger: true,
|
||||
default: logAxis,
|
||||
description: t('Logarithmic x-axis'),
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Y Axis'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'y_axis_label',
|
||||
config: {
|
||||
type: 'TextControl',
|
||||
label: t('Y Axis Title'),
|
||||
renderTrigger: true,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'yAxisLabelRotation',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: false,
|
||||
label: t('Rotate y axis label'),
|
||||
choices: [
|
||||
[0, '0°'],
|
||||
[45, '45°'],
|
||||
],
|
||||
default: xAxisLabelRotation,
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'Input field supports custom rotation. e.g. 30 for 30°',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'y_axis_title_margin',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: true,
|
||||
label: t('Y AXIS TITLE MARGIN'),
|
||||
renderTrigger: true,
|
||||
default: sections.TITLE_MARGIN_OPTIONS[1],
|
||||
choices: formatSelectOptions(sections.TITLE_MARGIN_OPTIONS),
|
||||
},
|
||||
},
|
||||
],
|
||||
['y_axis_format'],
|
||||
[
|
||||
{
|
||||
name: 'logYAxis',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Logarithmic y-axis'),
|
||||
renderTrigger: true,
|
||||
default: logAxis,
|
||||
description: t('Logarithmic y-axis'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'truncateYAxis',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Truncate Y Axis'),
|
||||
default: truncateYAxis,
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'Truncate Y Axis. Can be overridden by specifying a min or max bound.',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'y_axis_bounds',
|
||||
config: {
|
||||
type: 'BoundsControl',
|
||||
label: t('Y Axis Bounds'),
|
||||
renderTrigger: true,
|
||||
default: yAxisBounds,
|
||||
description: t(
|
||||
'Bounds for the Y-axis. When left empty, the bounds are ' +
|
||||
'dynamically defined based on the min/max of the data. Note that ' +
|
||||
"this feature will only expand the axis range. It won't " +
|
||||
"narrow the data's extent.",
|
||||
),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
Boolean(controls?.truncateYAxis?.value),
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 132 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* 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 { Behavior, ChartMetadata, ChartPlugin, t } from '@superset-ui/core';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
import transformProps from './transformProps';
|
||||
import buildQuery from './buildQuery';
|
||||
import controlPanel from './controlPanel';
|
||||
import example1 from './images/example1.png';
|
||||
import example2 from './images/example2.png';
|
||||
import { EchartsBubbleChartProps, EchartsBubbleFormData } from './types';
|
||||
|
||||
export default class EchartsBubbleChartPlugin extends ChartPlugin<
|
||||
EchartsBubbleFormData,
|
||||
EchartsBubbleChartProps
|
||||
> {
|
||||
constructor() {
|
||||
super({
|
||||
buildQuery,
|
||||
controlPanel,
|
||||
loadChart: () => import('./EchartsBubble'),
|
||||
metadata: new ChartMetadata({
|
||||
behaviors: [Behavior.INTERACTIVE_CHART],
|
||||
category: t('Correlation'),
|
||||
credits: ['https://echarts.apache.org'],
|
||||
description: t(
|
||||
'Visualizes a metric across three dimensions of data in a single chart (X axis, Y axis, and bubble size). Bubbles from the same group can be showcased using bubble color.',
|
||||
),
|
||||
exampleGallery: [{ url: example1 }, { url: example2 }],
|
||||
name: t('Bubble Chart'),
|
||||
tags: [
|
||||
t('Multi-Dimensions'),
|
||||
t('Aesthetic'),
|
||||
t('Comparison'),
|
||||
t('Scatter'),
|
||||
t('Time'),
|
||||
t('Trend'),
|
||||
t('ECharts'),
|
||||
],
|
||||
thumbnail,
|
||||
}),
|
||||
transformProps,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
/**
|
||||
* 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 { EChartsCoreOption, ScatterSeriesOption } from 'echarts';
|
||||
import { extent } from 'd3-array';
|
||||
import {
|
||||
CategoricalColorNamespace,
|
||||
getNumberFormatter,
|
||||
AxisType,
|
||||
getMetricLabel,
|
||||
NumberFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { EchartsBubbleChartProps, EchartsBubbleFormData } from './types';
|
||||
import { DEFAULT_FORM_DATA, MINIMUM_BUBBLE_SIZE } from './constants';
|
||||
import { defaultGrid } from '../defaults';
|
||||
import { getLegendProps } from '../utils/series';
|
||||
import { Refs } from '../types';
|
||||
import { parseYAxisBound } from '../utils/controls';
|
||||
import { getDefaultTooltip } from '../utils/tooltip';
|
||||
import { getPadding } from '../Timeseries/transformers';
|
||||
import { convertInteger } from '../utils/convertInteger';
|
||||
import { NULL_STRING } from '../constants';
|
||||
|
||||
function normalizeSymbolSize(
|
||||
nodes: ScatterSeriesOption[],
|
||||
maxBubbleValue: number,
|
||||
) {
|
||||
const [bubbleMinValue, bubbleMaxValue] = extent(nodes, x => x.data![0][2]);
|
||||
const nodeSpread = bubbleMaxValue - bubbleMinValue;
|
||||
nodes.forEach(node => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
node.symbolSize =
|
||||
(((node.data![0][2] - bubbleMinValue) / nodeSpread) *
|
||||
(maxBubbleValue * 2) || 0) + MINIMUM_BUBBLE_SIZE;
|
||||
});
|
||||
}
|
||||
|
||||
export function formatTooltip(
|
||||
params: any,
|
||||
xAxisLabel: string,
|
||||
yAxisLabel: string,
|
||||
sizeLabel: string,
|
||||
xAxisFormatter: NumberFormatter,
|
||||
yAxisFormatter: NumberFormatter,
|
||||
tooltipSizeFormatter: NumberFormatter,
|
||||
) {
|
||||
const title = params.data[4]
|
||||
? `${params.data[3]} </br> ${params.data[4]}`
|
||||
: params.data[3];
|
||||
|
||||
return `<p>${title}</p>
|
||||
${xAxisLabel}: ${xAxisFormatter(params.data[0])} <br/>
|
||||
${yAxisLabel}: ${yAxisFormatter(params.data[1])} <br/>
|
||||
${sizeLabel}: ${tooltipSizeFormatter(params.data[2])}`;
|
||||
}
|
||||
|
||||
export default function transformProps(chartProps: EchartsBubbleChartProps) {
|
||||
const { height, width, hooks, queriesData, formData, inContextMenu, theme } =
|
||||
chartProps;
|
||||
|
||||
const { data = [] } = queriesData[0];
|
||||
const {
|
||||
x,
|
||||
y,
|
||||
size,
|
||||
entity,
|
||||
maxBubbleSize,
|
||||
colorScheme,
|
||||
series: bubbleSeries,
|
||||
xAxisLabel: bubbleXAxisTitle,
|
||||
yAxisLabel: bubbleYAxisTitle,
|
||||
xAxisFormat,
|
||||
yAxisFormat,
|
||||
yAxisBounds,
|
||||
logXAxis,
|
||||
logYAxis,
|
||||
xAxisTitleMargin,
|
||||
yAxisTitleMargin,
|
||||
truncateYAxis,
|
||||
xAxisLabelRotation,
|
||||
yAxisLabelRotation,
|
||||
tooltipSizeFormat,
|
||||
opacity,
|
||||
showLegend,
|
||||
legendOrientation,
|
||||
legendMargin,
|
||||
legendType,
|
||||
}: EchartsBubbleFormData = { ...DEFAULT_FORM_DATA, ...formData };
|
||||
|
||||
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
|
||||
|
||||
const legends: string[] = [];
|
||||
const series: ScatterSeriesOption[] = [];
|
||||
|
||||
const xAxisLabel: string = getMetricLabel(x);
|
||||
const yAxisLabel: string = getMetricLabel(y);
|
||||
const sizeLabel: string = getMetricLabel(size);
|
||||
|
||||
const refs: Refs = {};
|
||||
|
||||
data.forEach(datum => {
|
||||
const name =
|
||||
((bubbleSeries ? datum[bubbleSeries] : datum[entity]) as string) ||
|
||||
NULL_STRING;
|
||||
const bubbleSeriesValue = bubbleSeries ? datum[bubbleSeries] : null;
|
||||
|
||||
series.push({
|
||||
name,
|
||||
data: [
|
||||
[
|
||||
datum[xAxisLabel],
|
||||
datum[yAxisLabel],
|
||||
datum[sizeLabel],
|
||||
datum[entity],
|
||||
bubbleSeriesValue as any,
|
||||
],
|
||||
],
|
||||
type: 'scatter',
|
||||
itemStyle: { color: colorFn(name), opacity },
|
||||
});
|
||||
legends.push(name);
|
||||
});
|
||||
|
||||
normalizeSymbolSize(series, maxBubbleSize);
|
||||
|
||||
const xAxisFormatter = getNumberFormatter(xAxisFormat);
|
||||
const yAxisFormatter = getNumberFormatter(yAxisFormat);
|
||||
const tooltipSizeFormatter = getNumberFormatter(tooltipSizeFormat);
|
||||
|
||||
const [min, max] = yAxisBounds.map(parseYAxisBound);
|
||||
|
||||
const padding = getPadding(
|
||||
showLegend,
|
||||
legendOrientation,
|
||||
true,
|
||||
false,
|
||||
legendMargin,
|
||||
true,
|
||||
'Left',
|
||||
convertInteger(yAxisTitleMargin),
|
||||
convertInteger(xAxisTitleMargin),
|
||||
);
|
||||
|
||||
const echartOptions: EChartsCoreOption = {
|
||||
series,
|
||||
xAxis: {
|
||||
axisLabel: { formatter: xAxisFormatter },
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
},
|
||||
},
|
||||
nameRotate: xAxisLabelRotation,
|
||||
scale: true,
|
||||
name: bubbleXAxisTitle,
|
||||
nameLocation: 'middle',
|
||||
nameTextStyle: {
|
||||
fontWight: 'bolder',
|
||||
},
|
||||
nameGap: convertInteger(xAxisTitleMargin),
|
||||
type: logXAxis ? AxisType.log : AxisType.value,
|
||||
},
|
||||
yAxis: {
|
||||
axisLabel: { formatter: yAxisFormatter },
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
},
|
||||
},
|
||||
nameRotate: yAxisLabelRotation,
|
||||
scale: truncateYAxis,
|
||||
name: bubbleYAxisTitle,
|
||||
nameLocation: 'middle',
|
||||
nameTextStyle: {
|
||||
fontWight: 'bolder',
|
||||
},
|
||||
nameGap: convertInteger(yAxisTitleMargin),
|
||||
min,
|
||||
max,
|
||||
type: logYAxis ? AxisType.log : AxisType.value,
|
||||
},
|
||||
legend: {
|
||||
...getLegendProps(legendType, legendOrientation, showLegend, theme),
|
||||
data: legends,
|
||||
},
|
||||
tooltip: {
|
||||
show: !inContextMenu,
|
||||
...getDefaultTooltip(refs),
|
||||
formatter: (params: any): string =>
|
||||
formatTooltip(
|
||||
params,
|
||||
xAxisLabel,
|
||||
yAxisLabel,
|
||||
sizeLabel,
|
||||
xAxisFormatter,
|
||||
yAxisFormatter,
|
||||
tooltipSizeFormatter,
|
||||
),
|
||||
},
|
||||
grid: { ...defaultGrid, ...padding },
|
||||
};
|
||||
|
||||
const { onContextMenu, setDataMask = () => {} } = hooks;
|
||||
|
||||
return {
|
||||
refs,
|
||||
height,
|
||||
width,
|
||||
echartOptions,
|
||||
onContextMenu,
|
||||
setDataMask,
|
||||
formData,
|
||||
};
|
||||
}
|
||||
|
|
@ -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 {
|
||||
ChartProps,
|
||||
ChartDataResponseResult,
|
||||
QueryFormData,
|
||||
} from '@superset-ui/core';
|
||||
import {
|
||||
LegendFormData,
|
||||
BaseTransformedProps,
|
||||
CrossFilterTransformedProps,
|
||||
} from '../types';
|
||||
|
||||
export type EchartsBubbleFormData = QueryFormData &
|
||||
LegendFormData & {
|
||||
series?: string;
|
||||
entity: string;
|
||||
xAxisFormat: string;
|
||||
yAXisFormat: string;
|
||||
logXAxis: boolean;
|
||||
logYAxis: boolean;
|
||||
xAxisBounds: [number | undefined | null, number | undefined | null];
|
||||
yAxisBounds: [number | undefined | null, number | undefined | null];
|
||||
xAxisLabel?: string;
|
||||
colorScheme?: string;
|
||||
defaultValue?: string[] | null;
|
||||
dateFormat: string;
|
||||
emitFilter: boolean;
|
||||
tooltipFormat: string;
|
||||
x: string;
|
||||
y: string;
|
||||
};
|
||||
|
||||
export interface EchartsBubbleChartProps
|
||||
extends ChartProps<EchartsBubbleFormData> {
|
||||
formData: EchartsBubbleFormData;
|
||||
queriesData: ChartDataResponseResult[];
|
||||
}
|
||||
|
||||
export type BubbleChartTransformedProps =
|
||||
BaseTransformedProps<EchartsBubbleFormData> & CrossFilterTransformedProps;
|
||||
|
|
@ -34,6 +34,7 @@ export { default as EchartsTreeChartPlugin } from './Tree';
|
|||
export { default as EchartsTreemapChartPlugin } from './Treemap';
|
||||
export { BigNumberChartPlugin, BigNumberTotalChartPlugin } from './BigNumber';
|
||||
export { default as EchartsSunburstChartPlugin } from './Sunburst';
|
||||
export { default as EchartsBubbleChartPlugin } from './Bubble';
|
||||
|
||||
export { default as BoxPlotTransformProps } from './BoxPlot/transformProps';
|
||||
export { default as FunnelTransformProps } from './Funnel/transformProps';
|
||||
|
|
@ -46,6 +47,7 @@ export { default as TimeseriesTransformProps } from './Timeseries/transformProps
|
|||
export { default as TreeTransformProps } from './Tree/transformProps';
|
||||
export { default as TreemapTransformProps } from './Treemap/transformProps';
|
||||
export { default as SunburstTransformProps } from './Sunburst/transformProps';
|
||||
export { default as BubbleTransformProps } from './Bubble/transformProps';
|
||||
|
||||
export { DEFAULT_FORM_DATA as TimeseriesDefaultFormData } from './Timeseries/constants';
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* 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 buildQuery from '../../src/Bubble/buildQuery';
|
||||
|
||||
describe('Bubble buildQuery', () => {
|
||||
const formData = {
|
||||
datasource: '1__table',
|
||||
viz_type: 'echarts_bubble',
|
||||
entity: 'customer_name',
|
||||
x: 'count',
|
||||
y: {
|
||||
aggregate: 'sum',
|
||||
column: {
|
||||
column_name: 'price_each',
|
||||
},
|
||||
expressionType: 'simple',
|
||||
label: 'SUM(price_each)',
|
||||
},
|
||||
size: {
|
||||
aggregate: 'sum',
|
||||
column: {
|
||||
column_name: 'sales',
|
||||
},
|
||||
expressionType: 'simple',
|
||||
label: 'SUM(sales)',
|
||||
},
|
||||
};
|
||||
|
||||
it('Should build query without dimension', () => {
|
||||
const queryContext = buildQuery(formData);
|
||||
const [query] = queryContext.queries;
|
||||
expect(query.columns).toEqual(['customer_name']);
|
||||
expect(query.metrics).toEqual([
|
||||
'count',
|
||||
{
|
||||
aggregate: 'sum',
|
||||
column: {
|
||||
column_name: 'price_each',
|
||||
},
|
||||
expressionType: 'simple',
|
||||
label: 'SUM(price_each)',
|
||||
},
|
||||
{
|
||||
aggregate: 'sum',
|
||||
column: {
|
||||
column_name: 'sales',
|
||||
},
|
||||
expressionType: 'simple',
|
||||
label: 'SUM(sales)',
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('Should build query with dimension', () => {
|
||||
const queryContext = buildQuery({ ...formData, series: 'state' });
|
||||
const [query] = queryContext.queries;
|
||||
expect(query.columns).toEqual(['customer_name', 'state']);
|
||||
expect(query.metrics).toEqual([
|
||||
'count',
|
||||
{
|
||||
aggregate: 'sum',
|
||||
column: {
|
||||
column_name: 'price_each',
|
||||
},
|
||||
expressionType: 'simple',
|
||||
label: 'SUM(price_each)',
|
||||
},
|
||||
{
|
||||
aggregate: 'sum',
|
||||
column: {
|
||||
column_name: 'sales',
|
||||
},
|
||||
expressionType: 'simple',
|
||||
label: 'SUM(sales)',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
/**
|
||||
* 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 {
|
||||
ChartProps,
|
||||
getNumberFormatter,
|
||||
SqlaFormData,
|
||||
supersetTheme,
|
||||
} from '@superset-ui/core';
|
||||
import { EchartsBubbleChartProps } from 'plugins/plugin-chart-echarts/src/Bubble/types';
|
||||
|
||||
import transformProps, { formatTooltip } from '../../src/Bubble/transformProps';
|
||||
|
||||
describe('Bubble transformProps', () => {
|
||||
const formData: SqlaFormData = {
|
||||
datasource: '1__table',
|
||||
viz_type: 'echarts_bubble',
|
||||
entity: 'customer_name',
|
||||
x: 'count',
|
||||
y: {
|
||||
aggregate: 'sum',
|
||||
column: {
|
||||
column_name: 'price_each',
|
||||
},
|
||||
expressionType: 'simple',
|
||||
label: 'SUM(price_each)',
|
||||
},
|
||||
size: {
|
||||
aggregate: 'sum',
|
||||
column: {
|
||||
column_name: 'sales',
|
||||
},
|
||||
expressionType: 'simple',
|
||||
label: 'SUM(sales)',
|
||||
},
|
||||
yAxisBounds: [null, null],
|
||||
};
|
||||
const chartProps = new ChartProps({
|
||||
formData,
|
||||
height: 800,
|
||||
width: 800,
|
||||
queriesData: [
|
||||
{
|
||||
data: [
|
||||
{
|
||||
customer_name: 'AV Stores, Co.',
|
||||
count: 10,
|
||||
'SUM(price_each)': 20,
|
||||
'SUM(sales)': 30,
|
||||
},
|
||||
{
|
||||
customer_name: 'Alpha Cognac',
|
||||
count: 40,
|
||||
'SUM(price_each)': 50,
|
||||
'SUM(sales)': 60,
|
||||
},
|
||||
{
|
||||
customer_name: 'Amica Models & Co.',
|
||||
count: 70,
|
||||
'SUM(price_each)': 80,
|
||||
'SUM(sales)': 90,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
theme: supersetTheme,
|
||||
});
|
||||
|
||||
it('Should transform props for viz', () => {
|
||||
expect(transformProps(chartProps as EchartsBubbleChartProps)).toEqual(
|
||||
expect.objectContaining({
|
||||
width: 800,
|
||||
height: 800,
|
||||
echartOptions: expect.objectContaining({
|
||||
series: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
data: expect.arrayContaining([
|
||||
[10, 20, 30, 'AV Stores, Co.', null],
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
data: expect.arrayContaining([
|
||||
[40, 50, 60, 'Alpha Cognac', null],
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
data: expect.arrayContaining([
|
||||
[70, 80, 90, 'Amica Models & Co.', null],
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bubble formatTooltip', () => {
|
||||
const dollerFormatter = getNumberFormatter('$,.2f');
|
||||
const percentFormatter = getNumberFormatter(',.1%');
|
||||
|
||||
it('Should generate correct bubble label content with dimension', () => {
|
||||
const params = {
|
||||
data: [10000, 20000, 3, 'bubble title', 'bubble dimension'],
|
||||
};
|
||||
|
||||
expect(
|
||||
formatTooltip(
|
||||
params,
|
||||
'x-axis-label',
|
||||
'y-axis-label',
|
||||
'size-label',
|
||||
dollerFormatter,
|
||||
dollerFormatter,
|
||||
percentFormatter,
|
||||
),
|
||||
).toEqual(
|
||||
`<p>bubble title </br> bubble dimension</p>
|
||||
x-axis-label: $10,000.00 <br/>
|
||||
y-axis-label: $20,000.00 <br/>
|
||||
size-label: 300.0%`,
|
||||
);
|
||||
});
|
||||
it('Should generate correct bubble label content without dimension', () => {
|
||||
const params = {
|
||||
data: [10000, 25000, 3, 'bubble title', null],
|
||||
};
|
||||
expect(
|
||||
formatTooltip(
|
||||
params,
|
||||
'x-axis-label',
|
||||
'y-axis-label',
|
||||
'size-label',
|
||||
dollerFormatter,
|
||||
dollerFormatter,
|
||||
percentFormatter,
|
||||
),
|
||||
).toEqual(
|
||||
`<p>bubble title</p>
|
||||
x-axis-label: $10,000.00 <br/>
|
||||
y-axis-label: $25,000.00 <br/>
|
||||
size-label: 300.0%`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -101,6 +101,7 @@ const DEFAULT_ORDER = [
|
|||
'cal_heatmap',
|
||||
'rose',
|
||||
'bubble',
|
||||
'bubble_v2',
|
||||
'deck_geojson',
|
||||
'horizon',
|
||||
'deck_multi',
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ import {
|
|||
EchartsMixedTimeseriesChartPlugin,
|
||||
EchartsTreeChartPlugin,
|
||||
EchartsSunburstChartPlugin,
|
||||
EchartsBubbleChartPlugin,
|
||||
} from '@superset-ui/plugin-chart-echarts';
|
||||
import {
|
||||
SelectFilterPlugin,
|
||||
|
|
@ -160,6 +161,7 @@ export default class MainPreset extends Preset {
|
|||
new EchartsTreeChartPlugin().configure({ key: 'tree_chart' }),
|
||||
new EchartsSunburstChartPlugin().configure({ key: 'sunburst_v2' }),
|
||||
new HandlebarsChartPlugin().configure({ key: 'handlebars' }),
|
||||
new EchartsBubbleChartPlugin().configure({ key: 'bubble_v2' }),
|
||||
...experimentalplugins,
|
||||
],
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue