From b303d1e156185d134927246004a4804931cd6bca Mon Sep 17 00:00:00 2001 From: Stephen Liu <750188453@qq.com> Date: Tue, 16 Aug 2022 21:54:17 +0800 Subject: [PATCH] fix(plugin-chart-echarts): gauge chart enhancements and fixes (#21007) * fix(plugin-chart-echarts): gauge chart enhancements and fixes * fix lint --- .../src/Gauge/controlPanel.tsx | 6 +- .../src/Gauge/transformProps.ts | 79 +++++++++++++------ .../plugin-chart-echarts/src/Gauge/types.ts | 8 +- 3 files changed, 60 insertions(+), 33 deletions(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx index 14f5460c8ac14..7ffa62fda0ffa 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx @@ -17,7 +17,7 @@ * under the License. */ import React from 'react'; -import { t, validateNonEmpty, validateInteger } from '@superset-ui/core'; +import { t } from '@superset-ui/core'; import { sharedControls, ControlPanelConfig, @@ -82,8 +82,7 @@ const config: ControlPanelConfig = { config: { type: 'TextControl', isInt: true, - default: String(DEFAULT_FORM_DATA.minVal), - validators: [validateNonEmpty, validateInteger], + default: DEFAULT_FORM_DATA.minVal, renderTrigger: true, label: t('Min'), description: t('Minimum value on the gauge axis'), @@ -95,7 +94,6 @@ const config: ControlPanelConfig = { type: 'TextControl', isInt: true, default: DEFAULT_FORM_DATA.maxVal, - validators: [validateNonEmpty, validateInteger], renderTrigger: true, label: t('Max'), description: t('Maximum value on the gauge axis'), diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts index 59a2e21f8e663..edd31d9d0d192 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts @@ -28,6 +28,7 @@ import { } from '@superset-ui/core'; import { EChartsCoreOption, GaugeSeriesOption } from 'echarts'; import { GaugeDataItemOption } from 'echarts/types/src/chart/gauge/GaugeSeries'; +import { CallbackDataParams } from 'echarts/types/src/util/types'; import range from 'lodash/range'; import { parseNumbersList } from '../utils/controls'; import { @@ -80,6 +81,12 @@ const calculateAxisLineWidth = ( overlap: boolean, ): number => (overlap ? fontSize : data.length * fontSize); +const calculateMin = (data: GaugeDataItemOption[]) => + 2 * Math.min(...data.map(d => d.value as number).concat([0])); + +const calculateMax = (data: GaugeDataItemOption[]) => + 2 * Math.max(...data.map(d => d.value as number).concat([0])); + export default function transformProps( chartProps: EchartsGaugeChartProps, ): GaugeChartTransformedProps { @@ -115,12 +122,7 @@ export default function transformProps( const data = (queriesData[0]?.data || []) as DataRecord[]; const numberFormatter = getNumberFormatter(numberFormat); const colorFn = CategoricalColorNamespace.getScale(colorScheme as string); - const normalizer = maxVal; const axisLineWidth = calculateAxisLineWidth(data, fontSize, overlap); - const axisLabels = range(minVal, maxVal, (maxVal - minVal) / splitNumber); - const axisLabelLength = Math.max( - ...axisLabels.map(label => numberFormatter(label).length).concat([1]), - ); const groupbyLabels = groupby.map(getColumnLabel); const formatValue = (value: number) => valueFormatter.replace('{value}', numberFormatter(value)); @@ -130,12 +132,6 @@ export default function transformProps( FONT_SIZE_MULTIPLIERS.titleOffsetFromTitle * fontSize; const detailOffsetFromTitle = FONT_SIZE_MULTIPLIERS.detailOffsetFromTitle * fontSize; - const intervalBoundsAndColors = setIntervalBoundsAndColors( - intervals, - intervalColorIndices, - colorFn, - normalizer, - ); const columnsLabelMap = new Map(); const transformedData: GaugeDataItemOption[] = data.map( @@ -196,6 +192,33 @@ export default function transformProps( const { setDataMask = () => {}, onContextMenu } = hooks; + const min = minVal ?? calculateMin(transformedData); + const max = maxVal ?? calculateMax(transformedData); + const axisLabels = range(min, max, (max - min) / splitNumber); + const axisLabelLength = Math.max( + ...axisLabels.map(label => numberFormatter(label).length).concat([1]), + ); + const normalizer = max; + const intervalBoundsAndColors = setIntervalBoundsAndColors( + intervals, + intervalColorIndices, + colorFn, + normalizer, + ); + const splitLineDistance = + axisLineWidth + splitLineLength + OFFSETS.ticksFromLine; + const axisLabelDistance = + FONT_SIZE_MULTIPLIERS.axisLabelDistance * + fontSize * + FONT_SIZE_MULTIPLIERS.axisLabelLength * + axisLabelLength + + (showSplitLine ? splitLineLength : 0) + + (showAxisTick ? axisTickLength : 0) + + OFFSETS.ticksFromLine - + axisLineWidth; + const axisTickDistance = + axisLineWidth + axisTickLength + OFFSETS.ticksFromLine; + const progress = { show: showProgress, overlap, @@ -204,7 +227,7 @@ export default function transformProps( }; const splitLine = { show: showSplitLine, - distance: -axisLineWidth - splitLineLength - OFFSETS.ticksFromLine, + distance: -splitLineDistance, length: splitLineLength, lineStyle: { width: FONT_SIZE_MULTIPLIERS.splitLineWidth * fontSize, @@ -219,22 +242,14 @@ export default function transformProps( }, }; const axisLabel = { - distance: - axisLineWidth - - FONT_SIZE_MULTIPLIERS.axisLabelDistance * - fontSize * - FONT_SIZE_MULTIPLIERS.axisLabelLength * - axisLabelLength - - (showSplitLine ? splitLineLength : 0) - - (showAxisTick ? axisTickLength : 0) - - OFFSETS.ticksFromLine, + distance: -axisLabelDistance, fontSize, formatter: numberFormatter, color: gaugeSeriesOptions.axisLabel?.color, }; const axisTick = { show: showAxisTick, - distance: -axisLineWidth - axisTickLength - OFFSETS.ticksFromLine, + distance: -axisTickDistance, length: axisTickLength, lineStyle: gaugeSeriesOptions.axisTick?.lineStyle as AxisTickLineStyle, }; @@ -243,8 +258,14 @@ export default function transformProps( formatter: (value: number) => formatValue(value), color: gaugeSeriesOptions.detail?.color, }; - let pointer; + const tooltip = { + formatter: (params: CallbackDataParams) => { + const { name, value } = params; + return `${name} : ${formatValue(value as number)}`; + }, + }; + let pointer; if (intervalBoundsAndColors.length) { splitLine.lineStyle.color = INTERVAL_GAUGE_SERIES_OPTION.splitLine?.lineStyle?.color; @@ -269,8 +290,8 @@ export default function transformProps( type: 'gauge', startAngle, endAngle, - min: minVal, - max: maxVal, + min, + max, progress, animation, axisLine: axisLine as GaugeSeriesOption['axisLine'], @@ -280,11 +301,19 @@ export default function transformProps( axisTick, pointer, detail, + tooltip, + radius: + Math.min(width, height) / 2 - axisLabelDistance - axisTickDistance, + center: ['50%', '55%'], data: transformedData, }, ]; const echartOptions: EChartsCoreOption = { + tooltip: { + appendToBody: true, + trigger: 'item', + }, series, }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/types.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/types.ts index 7ae2a555951b2..4824d579c4040 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/types.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/types.ts @@ -35,8 +35,8 @@ export type EchartsGaugeFormData = QueryFormData & { groupby: QueryFormColumn[]; metric?: string; rowLimit: number; - minVal: number; - maxVal: number; + minVal: number | null; + maxVal: number | null; fontSize: number; numberFormat: string; animation: boolean; @@ -59,8 +59,8 @@ export const DEFAULT_FORM_DATA: Partial = { ...DEFAULT_LEGEND_FORM_DATA, groupby: [], rowLimit: 10, - minVal: 0, - maxVal: 100, + minVal: null, + maxVal: null, fontSize: 15, numberFormat: 'SMART_NUMBER', animation: true,