diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Funnel/Stories.tsx b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Funnel/Stories.tsx new file mode 100644 index 0000000000000..7f721f4d05733 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Funnel/Stories.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { SuperChart, getChartTransformPropsRegistry } from '@superset-ui/core'; +import { boolean, number, select, withKnobs } from '@storybook/addon-knobs'; +import { EchartsFunnelChartPlugin } from '@superset-ui/plugin-chart-echarts'; +import transformProps from '@superset-ui/plugin-chart-echarts/lib/Funnel/transformProps'; +import { dataSource } from './constants'; +import { withResizableChartDemo } from '../../../../shared/components/ResizableChartDemo'; + +new EchartsFunnelChartPlugin().configure({ key: 'echarts-funnel' }).register(); + +getChartTransformPropsRegistry().registerValue('echarts-funnel', transformProps); + +export default { + title: 'Chart Plugins|plugin-chart-echarts/Funnel', + decorators: [withKnobs, withResizableChartDemo], +}; + +export const Funnel = ({ width, height }) => { + return ( + + ); +}; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Funnel/constants.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Funnel/constants.ts new file mode 100644 index 0000000000000..27885a6df44cc --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Funnel/constants.ts @@ -0,0 +1,6 @@ +export const dataSource = [ + { value: 89439, name: 'pv' }, + { value: 5526, name: 'cart' }, + { value: 2824, name: 'fav' }, + { value: 2211, name: 'buy' }, +]; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/EchartsFunnel.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/EchartsFunnel.tsx new file mode 100644 index 0000000000000..6e617c71f5e30 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/EchartsFunnel.tsx @@ -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 React, { useCallback } from 'react'; +import { FunnelChartTransformedProps } from './types'; +import Echart from '../components/Echart'; +import { EventHandlers } from '../types'; + +export default function EchartsFunnel({ + height, + width, + echartOptions, + setDataMask, + labelMap, + groupby, + selectedValues, + formData, +}: FunnelChartTransformedProps) { + const handleChange = useCallback( + (values: string[]) => { + if (!formData.emitFilter) { + return; + } + + const groupbyValues = values.map(value => labelMap[value]); + + setDataMask({ + extraFormData: { + filters: + values.length === 0 + ? [] + : groupby.map((col, idx) => { + const val = groupbyValues.map(v => v[idx]); + if (val === null || val === undefined) + return { + col, + op: 'IS NULL', + }; + return { + col, + op: 'IN', + val: val as (string | number | boolean)[], + }; + }), + }, + filterState: { + value: groupbyValues.length ? groupbyValues : null, + }, + ownState: { + selectedValues: values.length ? values : null, + }, + }); + }, + [groupby, labelMap, setDataMask, selectedValues], + ); + + const eventHandlers: EventHandlers = { + click: props => { + const { name } = props; + const values = Object.values(selectedValues); + if (values.includes(name)) { + handleChange(values.filter(v => v !== name)); + } else { + handleChange([...values, name]); + } + }, + }; + + return ( + + ); +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/buildQuery.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/buildQuery.ts new file mode 100644 index 0000000000000..257fc15faeac6 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/buildQuery.ts @@ -0,0 +1,27 @@ +/** + * 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, QueryFormData } from '@superset-ui/core'; + +export default function buildQuery(formData: QueryFormData) { + return buildQueryContext(formData, baseQueryObject => [ + { + ...baseQueryObject, + }, + ]); +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/controlPanel.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/controlPanel.tsx new file mode 100644 index 0000000000000..81674f43febff --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/controlPanel.tsx @@ -0,0 +1,192 @@ +/** + * 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 { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core'; +import { + ControlPanelConfig, + D3_FORMAT_OPTIONS, + sections, + sharedControls, +} from '@superset-ui/chart-controls'; +import { DEFAULT_FORM_DATA, EchartsFunnelLabelTypeType } from './types'; +import { + legendMarginControl, + legendOrientationControl, + legendTypeControl, + showLegendControl, +} from '../controls'; + +const { + sort, + orient, + labelLine, + labelType, + numberFormat, + showLabels, + emitFilter, +} = DEFAULT_FORM_DATA; + +const config: ControlPanelConfig = { + controlPanelSections: [ + sections.legacyRegularTime, + { + label: t('Query'), + expanded: true, + controlSetRows: [ + ['groupby'], + ['metric'], + ['adhoc_filters'], + [ + { + name: 'row_limit', + config: { + ...sharedControls.row_limit, + default: 10, + }, + }, + ], + ], + }, + { + label: t('Chart Options'), + expanded: true, + controlSetRows: [ + ['color_scheme'], + isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS) + ? [ + { + name: 'emit_filter', + config: { + type: 'CheckboxControl', + label: t('Enable emitting filters'), + default: emitFilter, + renderTrigger: true, + description: t('Enable emmiting filters.'), + }, + }, + ] + : [], + // eslint-disable-next-line react/jsx-key + [

{t('Legend')}

], + [showLegendControl], + [legendTypeControl], + [legendOrientationControl], + [legendMarginControl], + // eslint-disable-next-line react/jsx-key + [

{t('Labels')}

], + [ + { + name: 'label_type', + config: { + type: 'SelectControl', + label: t('Label Type'), + default: labelType, + renderTrigger: true, + choices: [ + [EchartsFunnelLabelTypeType.Key, 'Category Name'], + [EchartsFunnelLabelTypeType.Value, 'Value'], + [EchartsFunnelLabelTypeType.Percent, 'Percentage'], + [EchartsFunnelLabelTypeType.KeyValue, 'Category and Value'], + [EchartsFunnelLabelTypeType.KeyPercent, 'Category and Percentage'], + [EchartsFunnelLabelTypeType.KeyValuePercent, 'Category, Value and Percentage'], + ], + description: t('What should be shown on the label?'), + }, + }, + ], + [ + { + name: 'number_format', + config: { + type: 'SelectControl', + freeForm: true, + label: t('Number format'), + renderTrigger: true, + default: numberFormat, + choices: D3_FORMAT_OPTIONS, + description: `${t('D3 format syntax: https://github.com/d3/d3-format')} ${t( + 'Only applies when "Label Type" is set to show values.', + )}`, + }, + }, + ], + [ + { + name: 'show_labels', + config: { + type: 'CheckboxControl', + label: t('Show Labels'), + renderTrigger: true, + default: showLabels, + description: t('Whether to display the labels.'), + }, + }, + ], + [ + { + name: 'label_line', + config: { + type: 'CheckboxControl', + label: t('Label Line'), + default: labelLine, + renderTrigger: true, + description: t('Draw line from Funnel to label when labels outside?'), + }, + }, + ], + // eslint-disable-next-line react/jsx-key + [

{t('Funnel shape')}

], + [ + { + name: 'sort', + config: { + type: 'SelectControl', + label: t('sort'), + default: sort, + renderTrigger: true, + choices: [ + [null, 'Default'], + ['ascending', 'Ascending'], + ['descending', 'Descending'], + ], + description: t('Sort data'), + }, + }, + { + name: 'orient', + config: { + type: 'SelectControl', + label: t('orient'), + default: orient, + renderTrigger: true, + choices: [ + [null, 'Default'], + ['vertical', 'Vertical'], + ['horizontal', 'Horizontal'], + ], + description: t('Funnel chart orientation. The options are vertical, horizontal'), + }, + }, + ], + ], + }, + ], +}; + +export default config; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/images/thumbnail.png b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/images/thumbnail.png new file mode 100644 index 0000000000000..6f594d447fb57 Binary files /dev/null and b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/images/thumbnail.png differ diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/index.ts new file mode 100644 index 0000000000000..cf126e5a0d920 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/index.ts @@ -0,0 +1,55 @@ +/** + * 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 buildQuery from './buildQuery'; +import controlPanel from './controlPanel'; +import transformProps from './transformProps'; +import thumbnail from './images/thumbnail.png'; +import { EchartsFunnelChartProps, EchartsFunnelFormData } from './types'; + +export default class EchartsFunnelChartPlugin extends ChartPlugin< + EchartsFunnelFormData, + EchartsFunnelChartProps +> { + /** + * The constructor is used to pass relevant metadata and callbacks that get + * registered in respective registries that are used throughout the library + * and application. A more thorough description of each property is given in + * the respective imported file. + * + * It is worth noting that `buildQuery` and is optional, and only needed for + * advanced visualizations that require either post processing operations + * (pivoting, rolling aggregations, sorting etc) or submitting multiple queries. + */ + constructor() { + super({ + buildQuery, + controlPanel, + loadChart: () => import('./EchartsFunnel'), + metadata: new ChartMetadata({ + behaviors: [Behavior.INTERACTIVE_CHART], + credits: ['https://echarts.apache.org'], + description: 'Funnel Chart (Apache ECharts)', + name: t('Funnel Chart'), + thumbnail, + }), + transformProps, + }); + } +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts new file mode 100644 index 0000000000000..aa07ac0bdbfc3 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts @@ -0,0 +1,208 @@ +/** + * 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 { + CategoricalColorNamespace, + DataRecordValue, + DataRecord, + getMetricLabel, + getNumberFormatter, + NumberFormats, + NumberFormatter, +} from '@superset-ui/core'; +import { CallbackDataParams } from 'echarts/types/src/util/types'; +import { EChartsOption, FunnelSeriesOption } from 'echarts'; +import { + DEFAULT_FORM_DATA as DEFAULT_FUNNEL_FORM_DATA, + EchartsFunnelChartProps, + EchartsFunnelFormData, + EchartsFunnelLabelTypeType, + FunnelChartTransformedProps, +} from './types'; +import { DEFAULT_LEGEND_FORM_DATA } from '../types'; +import { extractGroupbyLabel, getChartPadding, getLegendProps } from '../utils/series'; +import { defaultGrid, defaultTooltip } from '../defaults'; + +const percentFormatter = getNumberFormatter(NumberFormats.PERCENT_2_POINT); + +export function formatFunnelLabel({ + params, + labelType, + numberFormatter, +}: { + params: CallbackDataParams; + labelType: EchartsFunnelLabelTypeType; + numberFormatter: NumberFormatter; +}): string { + const { name = '', value, percent } = params; + const formattedValue = numberFormatter(value as number); + const formattedPercent = percentFormatter((percent as number) / 100); + switch (labelType) { + case EchartsFunnelLabelTypeType.Key: + return name; + case EchartsFunnelLabelTypeType.Value: + return formattedValue; + case EchartsFunnelLabelTypeType.Percent: + return formattedPercent; + case EchartsFunnelLabelTypeType.KeyValue: + return `${name}: ${formattedValue}`; + case EchartsFunnelLabelTypeType.KeyValuePercent: + return `${name}: ${formattedValue} (${formattedPercent})`; + case EchartsFunnelLabelTypeType.KeyPercent: + return `${name}: ${formattedPercent}`; + default: + return name; + } +} + +export default function transformProps( + chartProps: EchartsFunnelChartProps, +): FunnelChartTransformedProps { + const { formData, height, hooks, ownState, queriesData, width } = chartProps; + const data: DataRecord[] = queriesData[0].data || []; + + const { + colorScheme, + groupby, + orient, + sort, + gap, + labelLine, + labelType, + legendMargin, + legendOrientation, + legendType, + metric = '', + numberFormat, + showLabels, + showLegend, + emitFilter, + }: EchartsFunnelFormData = { + ...DEFAULT_LEGEND_FORM_DATA, + ...DEFAULT_FUNNEL_FORM_DATA, + ...formData, + }; + const metricLabel = getMetricLabel(metric); + const keys = data.map(datum => extractGroupbyLabel({ datum, groupby, coltypeMapping: {} })); + const labelMap = data.reduce((acc: Record, datum) => { + const label = extractGroupbyLabel({ + datum, + groupby, + coltypeMapping: {}, + }); + return { + ...acc, + [label]: groupby.map(col => datum[col]), + }; + }, {}); + + const { setDataMask = () => {} } = hooks; + + const colorFn = CategoricalColorNamespace.getScale(colorScheme as string); + const numberFormatter = getNumberFormatter(numberFormat); + + const transformedData: FunnelSeriesOption[] = data.map(datum => { + const name = extractGroupbyLabel({ datum, groupby, coltypeMapping: {} }); + return { + value: datum[metricLabel], + name, + itemStyle: { + color: colorFn(name), + }, + }; + }); + + const selectedValues = (ownState.selectedValues || []).reduce( + (acc: Record, selectedValue: string) => { + const index = transformedData.findIndex(({ name }) => name === selectedValue); + return { + ...acc, + [index]: selectedValue, + }; + }, + {}, + ); + + const formatter = (params: CallbackDataParams) => + formatFunnelLabel({ params, numberFormatter, labelType }); + + const defaultLabel = { + formatter, + show: showLabels, + color: '#000000', + }; + + const series: FunnelSeriesOption[] = [ + { + type: 'funnel', + ...getChartPadding(showLegend, legendOrientation, legendMargin), + animation: true, + minSize: '0%', + maxSize: '100%', + sort, + orient, + gap, + funnelAlign: 'center', + labelLine: { show: !!labelLine }, + label: { + ...defaultLabel, + position: labelLine ? 'outer' : 'inner', + }, + emphasis: { + label: { + show: true, + fontWeight: 'bold', + }, + }, + data: transformedData, + }, + ]; + + const echartOptions: EChartsOption = { + grid: { + ...defaultGrid, + }, + tooltip: { + ...defaultTooltip, + trigger: 'item', + formatter: (params: any) => + formatFunnelLabel({ + params, + numberFormatter, + labelType: EchartsFunnelLabelTypeType.KeyValuePercent, + }), + }, + legend: { + ...getLegendProps(legendType, legendOrientation, showLegend), + data: keys, + }, + series, + }; + + return { + formData, + width, + height, + echartOptions, + setDataMask, + emitFilter, + labelMap, + groupby, + selectedValues, + }; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/types.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/types.ts new file mode 100644 index 0000000000000..a265efca0310c --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Funnel/types.ts @@ -0,0 +1,89 @@ +/** + * 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 { EChartsOption } from 'echarts'; +import { + ChartDataResponseResult, + ChartProps, + DataRecordValue, + QueryFormData, + SetDataMaskHook, +} from '@superset-ui/core'; +import { + DEFAULT_LEGEND_FORM_DATA, + EchartsLegendFormData, + LegendOrientation, + LegendType, +} from '../types'; + +export type EchartsFunnelFormData = QueryFormData & + EchartsLegendFormData & { + colorScheme?: string; + groupby: string[]; + labelLine: boolean; + labelType: EchartsFunnelLabelTypeType; + metric?: string; + showLabels: boolean; + numberFormat: string; + gap: number; + sort: 'descending' | 'ascending' | 'none' | undefined; + orient: 'vertical' | 'horizontal' | undefined; + emitFilter: boolean; + }; + +export enum EchartsFunnelLabelTypeType { + Key, + Value, + Percent, + KeyValue, + KeyPercent, + KeyValuePercent, +} + +export interface EchartsFunnelChartProps extends ChartProps { + formData: EchartsFunnelFormData; + queriesData: ChartDataResponseResult[]; +} + +// @ts-ignore +export const DEFAULT_FORM_DATA: EchartsFunnelFormData = { + ...DEFAULT_LEGEND_FORM_DATA, + groupby: [], + labelLine: false, + labelType: EchartsFunnelLabelTypeType.Key, + legendOrientation: LegendOrientation.Top, + legendType: LegendType.Scroll, + numberFormat: 'SMART_NUMBER', + showLabels: true, + sort: 'descending', + orient: 'vertical', + gap: 0, + emitFilter: false, +}; + +export interface FunnelChartTransformedProps { + formData: EchartsFunnelFormData; + height: number; + width: number; + echartOptions: EChartsOption; + emitFilter: boolean; + setDataMask: SetDataMaskHook; + labelMap: Record; + groupby: string[]; + selectedValues: Record; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/index.ts index 721f11ffa6f81..264170457ab6f 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/index.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/index.ts @@ -23,6 +23,7 @@ export { default as EchartsPieChartPlugin } from './Pie'; export { default as EchartsGraphChartPlugin } from './Graph'; export { default as EchartsGaugeChartPlugin } from './Gauge'; export { default as EchartsRadarChartPlugin } from './Radar'; +export { default as EchartsFunnelChartPlugin } from './Funnel'; /** * Note: this file exports the default export from EchartsTimeseries.tsx. diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/test/Funnel/buildQuery.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/test/Funnel/buildQuery.test.ts new file mode 100644 index 0000000000000..09a5974537a6e --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/test/Funnel/buildQuery.test.ts @@ -0,0 +1,36 @@ +/** + * 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/Funnel/buildQuery'; + +describe('Funnel buildQuery', () => { + const formData = { + datasource: '5__table', + granularity_sqla: 'ds', + metric: 'foo', + groupby: ['bar'], + viz_type: 'my_chart', + }; + + it('should build query fields from form data', () => { + const queryContext = buildQuery(formData); + const [query] = queryContext.queries; + expect(query.metrics).toEqual(['foo']); + expect(query.columns).toEqual(['bar']); + }); +}); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/test/Funnel/transformProps.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/test/Funnel/transformProps.test.ts new file mode 100644 index 0000000000000..9a4b481041674 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/test/Funnel/transformProps.test.ts @@ -0,0 +1,106 @@ +/** + * 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 } from '@superset-ui/core'; +import transformProps, { formatFunnelLabel } from '../../src/Funnel/transformProps'; +import { EchartsFunnelLabelTypeType } from '../../src/Funnel/types'; + +describe('Funnel tranformProps', () => { + const formData = { + colorScheme: 'bnbColors', + datasource: '3__table', + granularity_sqla: 'ds', + metric: 'sum__num', + groupby: ['foo', 'bar'], + }; + const chartProps = new ChartProps({ + formData, + width: 800, + height: 600, + queriesData: [ + { + data: [ + { foo: 'Sylvester', bar: 1, sum__num: 10 }, + { foo: 'Arnold', bar: 2, sum__num: 2.5 }, + ], + }, + ], + }); + + it('should tranform chart props for viz', () => { + expect(transformProps(chartProps)).toEqual( + expect.objectContaining({ + width: 800, + height: 600, + echartOptions: expect.objectContaining({ + series: [ + expect.objectContaining({ + data: expect.arrayContaining([ + expect.objectContaining({ + name: 'Arnold, 2', + value: 2.5, + }), + expect.objectContaining({ + name: 'Sylvester, 1', + value: 10, + }), + ]), + }), + ], + }), + }), + ); + }); +}); + +describe('formatFunnelLabel', () => { + it('should generate a valid funnel chart label', () => { + const numberFormatter = getNumberFormatter(); + const params = { name: 'My Label', value: 1234, percent: 12.34 }; + expect( + formatFunnelLabel({ params, numberFormatter, labelType: EchartsFunnelLabelTypeType.Key }), + ).toEqual('My Label'); + expect( + formatFunnelLabel({ params, numberFormatter, labelType: EchartsFunnelLabelTypeType.Value }), + ).toEqual('1.23k'); + expect( + formatFunnelLabel({ params, numberFormatter, labelType: EchartsFunnelLabelTypeType.Percent }), + ).toEqual('12.34%'); + expect( + formatFunnelLabel({ + params, + numberFormatter, + labelType: EchartsFunnelLabelTypeType.KeyValue, + }), + ).toEqual('My Label: 1.23k'); + expect( + formatFunnelLabel({ + params, + numberFormatter, + labelType: EchartsFunnelLabelTypeType.KeyPercent, + }), + ).toEqual('My Label: 12.34%'); + expect( + formatFunnelLabel({ + params, + numberFormatter, + labelType: EchartsFunnelLabelTypeType.KeyValuePercent, + }), + ).toEqual('My Label: 1.23k (12.34%)'); + }); +}); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/test/index.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/test/index.test.ts index 9de7f1c5bd83e..c6272ce4116d0 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/test/index.test.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/test/index.test.ts @@ -21,6 +21,7 @@ import { EchartsPieChartPlugin, EchartsTimeseriesChartPlugin, EchartsGraphChartPlugin, + EchartsFunnelChartPlugin, } from '../src'; describe('@superset-ui/plugin-chart-echarts', () => { @@ -29,5 +30,6 @@ describe('@superset-ui/plugin-chart-echarts', () => { expect(EchartsPieChartPlugin).toBeDefined(); expect(EchartsTimeseriesChartPlugin).toBeDefined(); expect(EchartsGraphChartPlugin).toBeDefined(); + expect(EchartsFunnelChartPlugin).toBeDefined(); }); });