From b549977f0538d6939dce756c7615052e5f2e6c93 Mon Sep 17 00:00:00 2001 From: "Michael S. Molina" <70410625+michael-s-molina@users.noreply.github.com> Date: Tue, 7 May 2024 13:00:30 -0300 Subject: [PATCH] feat: Utility function to render chart tooltips (#27950) --- .../superset-ui-core/src/utils/index.ts | 1 + .../superset-ui-core/src/utils/tooltip.ts | 57 +++++++++ .../test/utils/tooltip.test.ts | 115 ++++++++++++++++++ .../BigNumberWithTrendline/transformProps.ts | 37 +++--- .../src/Bubble/transformProps.ts | 15 ++- .../src/Funnel/transformProps.ts | 72 ++++++----- .../src/Gauge/transformProps.ts | 6 +- .../src/Graph/transformProps.ts | 31 ++--- .../src/Heatmap/transformProps.ts | 41 ++++--- .../src/MixedTimeseries/transformProps.ts | 42 +++++-- .../src/Pie/transformProps.ts | 63 +++++----- .../src/Sunburst/transformProps.ts | 56 +++------ .../src/Timeseries/transformProps.ts | 60 ++++++--- .../src/Tree/transformProps.ts | 13 +- .../src/Treemap/transformProps.ts | 14 +-- .../src/Waterfall/transformProps.ts | 44 ++----- .../src/utils/forecast.ts | 35 +++--- .../plugin-chart-echarts/src/utils/tooltip.ts | 1 + .../test/Bubble/transformProps.test.ts | 62 +++++----- .../test/Funnel/transformProps.test.ts | 70 ++--------- .../test/Graph/transformProps.test.ts | 36 +----- .../test/Pie/transformProps.test.ts | 54 ++------ .../test/utils/forecast.test.ts | 12 +- 23 files changed, 512 insertions(+), 425 deletions(-) create mode 100644 superset-frontend/packages/superset-ui-core/src/utils/tooltip.ts create mode 100644 superset-frontend/packages/superset-ui-core/test/utils/tooltip.test.ts diff --git a/superset-frontend/packages/superset-ui-core/src/utils/index.ts b/superset-frontend/packages/superset-ui-core/src/utils/index.ts index 32fa88251ee5f..aebddc5459a10 100644 --- a/superset-frontend/packages/superset-ui-core/src/utils/index.ts +++ b/superset-frontend/packages/superset-ui-core/src/utils/index.ts @@ -32,3 +32,4 @@ export * from './featureFlags'; export * from './random'; export * from './typedMemo'; export * from './html'; +export * from './tooltip'; diff --git a/superset-frontend/packages/superset-ui-core/src/utils/tooltip.ts b/superset-frontend/packages/superset-ui-core/src/utils/tooltip.ts new file mode 100644 index 0000000000000..6105af0f715a4 --- /dev/null +++ b/superset-frontend/packages/superset-ui-core/src/utils/tooltip.ts @@ -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 { t } from '../translation'; + +const TRUNCATION_STYLE = ` + max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; +`; + +export function tooltipHtml( + data: string[][], + title?: string, + focusedRow?: number, +) { + const titleRow = title + ? `${title}` + : ''; + return ` +
+ ${titleRow} + + ${data.length === 0 ? `` : ''} + ${data + .map((row, i) => { + const rowStyle = + i === focusedRow ? 'font-weight: 700;' : 'opacity: 0.8;'; + const cells = row.map((cell, j) => { + const cellStyle = ` + text-align: ${j > 0 ? 'right' : 'left'}; + padding-left: ${j === 0 ? 0 : 16}px; + ${TRUNCATION_STYLE} + `; + return ``; + }); + return `${cells.join('')}`; + }) + .join('')} +
${t('No data')}
${cell}
+
`; +} diff --git a/superset-frontend/packages/superset-ui-core/test/utils/tooltip.test.ts b/superset-frontend/packages/superset-ui-core/test/utils/tooltip.test.ts new file mode 100644 index 0000000000000..fbd29175ec41e --- /dev/null +++ b/superset-frontend/packages/superset-ui-core/test/utils/tooltip.test.ts @@ -0,0 +1,115 @@ +/** + * 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 { tooltipHtml } from '@superset-ui/core'; + +const TITLE_STYLE = + 'style="font-weight: 700;max-width:300px;overflow:hidden;text-overflow:ellipsis;"'; +const TR_STYLE = 'style="opacity:0.8;"'; +const TR_FOCUS_STYLE = 'style="font-weight:700;"'; +const TD_TEXT_STYLE = + 'style="text-align:left;padding-left:0px;max-width:300px;overflow:hidden;text-overflow:ellipsis;"'; +const TD_NUMBER_STYLE = + 'style="text-align:right;padding-left:16px;max-width:300px;overflow:hidden;text-overflow:ellipsis;"'; + +const data = [ + ['a', 'b', 'c'], + ['1', '2', '3'], +]; + +function removeWhitespaces(text: string) { + return text.replace(/\s/g, ''); +} + +test('should return a table with the given data', () => { + const title = 'Title'; + const html = removeWhitespaces(tooltipHtml(data, title)); + const expectedHtml = removeWhitespaces(` +
+ Title + + + + + + + + + + + +
abc
123
+
`); + expect(html).toMatch(expectedHtml); +}); + +test('should return a table with the given data and a focused row', () => { + const title = 'Title'; + const focusedRow = 1; + const html = removeWhitespaces(tooltipHtml(data, title, focusedRow)); + const expectedHtml = removeWhitespaces(` +
+ Title + + + + + + + + + + + +
abc
123
+
`); + expect(html).toMatch(expectedHtml); +}); + +test('should return a table with no data', () => { + const title = 'Title'; + const html = removeWhitespaces(tooltipHtml([], title)); + const expectedHtml = removeWhitespaces(` +
+ Title + + +
No data
+
`); + expect(html).toMatch(expectedHtml); +}); + +test('should return a table with the given data and no title', () => { + const html = removeWhitespaces(tooltipHtml(data)); + const expectedHtml = removeWhitespaces(` +
+ + + + + + + + + + + +
abc
123
+
`); + expect(html).toMatch(expectedHtml); +}); diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts index c5e9e7042630e..015f0feee55b5 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts @@ -22,13 +22,11 @@ import { NumberFormats, GenericDataType, getMetricLabel, - t, - smartDateVerboseFormatter, - TimeFormatter, getXAxisLabel, Metric, - ValueFormatter, getValueFormatter, + t, + tooltipHtml, } from '@superset-ui/core'; import { EChartsCoreOption, graphic } from 'echarts'; import { @@ -41,24 +39,6 @@ import { getDateFormatter, parseMetricValue } from '../utils'; import { getDefaultTooltip } from '../../utils/tooltip'; import { Refs } from '../../types'; -const defaultNumberFormatter = getNumberFormatter(); -export function renderTooltipFactory( - formatDate: TimeFormatter = smartDateVerboseFormatter, - formatValue: ValueFormatter | TimeFormatter = defaultNumberFormatter, -) { - return function renderTooltip(params: { data: TimeSeriesDatum }[]) { - return ` - ${formatDate(params[0].data[0])} -
- - ${ - params[0].data[1] === null ? t('N/A') : formatValue(params[0].data[1]) - } - - `; - }; -} - const formatPercentChange = getNumberFormatter( NumberFormats.PERCENT_SIGNED_1_POINT, ); @@ -249,7 +229,18 @@ export default function transformProps( ...getDefaultTooltip(refs), show: !inContextMenu, trigger: 'axis', - formatter: renderTooltipFactory(formatTime, headerFormatter), + formatter: (params: { data: TimeSeriesDatum }[]) => + tooltipHtml( + [ + [ + metricName, + params[0].data[1] === null + ? t('N/A') + : headerFormatter.format(params[0].data[1]), + ], + ], + formatTime(params[0].data[0]), + ), }, aria: { enabled: true, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Bubble/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Bubble/transformProps.ts index 7731e095e0040..b60ad99161a26 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Bubble/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Bubble/transformProps.ts @@ -24,6 +24,7 @@ import { AxisType, getMetricLabel, NumberFormatter, + tooltipHtml, } from '@superset-ui/core'; import { EchartsBubbleChartProps, EchartsBubbleFormData } from './types'; import { DEFAULT_FORM_DATA, MINIMUM_BUBBLE_SIZE } from './constants'; @@ -60,13 +61,17 @@ export function formatTooltip( tooltipSizeFormatter: NumberFormatter, ) { const title = params.data[4] - ? `${params.data[3]}
${params.data[4]}` + ? `${params.data[4]} (${params.data[3]})` : params.data[3]; - return `

${title}

- ${xAxisLabel}: ${xAxisFormatter(params.data[0])}
- ${yAxisLabel}: ${yAxisFormatter(params.data[1])}
- ${sizeLabel}: ${tooltipSizeFormatter(params.data[2])}`; + return tooltipHtml( + [ + [xAxisLabel, xAxisFormatter(params.data[0])], + [yAxisLabel, yAxisFormatter(params.data[1])], + [sizeLabel, tooltipSizeFormatter(params.data[2])], + ], + title, + ); } export default function transformProps(chartProps: EchartsBubbleChartProps) { diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts index dbfb33b2e4670..daca2eb678907 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts @@ -24,6 +24,7 @@ import { getNumberFormatter, getValueFormatter, NumberFormats, + tooltipHtml, ValueFormatter, } from '@superset-ui/core'; import { CallbackDataParams } from 'echarts/types/src/util/types'; @@ -50,19 +51,17 @@ import { Refs } from '../types'; const percentFormatter = getNumberFormatter(NumberFormats.PERCENT_2_POINT); -export function formatFunnelLabel({ +export function parseParams({ params, - labelType, numberFormatter, percentCalculationType = PercentCalcType.FirstStep, sanitizeName = false, }: { params: Pick; - labelType: EchartsFunnelLabelTypeType; numberFormatter: ValueFormatter; percentCalculationType?: PercentCalcType; sanitizeName?: boolean; -}): string { +}) { const { name: rawName = '', value, percent: totalPercent, data } = params; const name = sanitizeName ? sanitizeHtml(rawName) : rawName; const formattedValue = numberFormatter(value as number); @@ -80,25 +79,7 @@ export function formatFunnelLabel({ percent = firstStepPercent ?? 0; } const formattedPercent = percentFormatter(percent); - - 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}`; - case EchartsFunnelLabelTypeType.ValuePercent: - return `${formattedValue} (${formattedPercent})`; - default: - return name; - } + return [name, formattedValue, formattedPercent]; } export default function transformProps( @@ -216,13 +197,31 @@ export default function transformProps( {}, ); - const formatter = (params: CallbackDataParams) => - formatFunnelLabel({ + const formatter = (params: CallbackDataParams) => { + const [name, formattedValue, formattedPercent] = parseParams({ params, numberFormatter, - labelType, percentCalculationType, }); + 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}`; + case EchartsFunnelLabelTypeType.ValuePercent: + return `${formattedValue} (${formattedPercent})`; + default: + return name; + } + }; const defaultLabel = { formatter, @@ -266,13 +265,26 @@ export default function transformProps( ...getDefaultTooltip(refs), show: !inContextMenu && showTooltipLabels, trigger: 'item', - formatter: (params: any) => - formatFunnelLabel({ + formatter: (params: any) => { + const [name, formattedValue, formattedPercent] = parseParams({ params, numberFormatter, - labelType: tooltipLabelType, percentCalculationType, - }), + }); + const row = []; + const enumName = EchartsFunnelLabelTypeType[tooltipLabelType]; + const title = enumName.includes('Key') ? name : undefined; + if (enumName.includes('Value') || enumName.includes('Percent')) { + row.push(metricLabel); + } + if (enumName.includes('Value')) { + row.push(formattedValue); + } + if (enumName.includes('Percent')) { + row.push(formattedPercent); + } + return tooltipHtml([row], title); + }, }, legend: { ...getLegendProps(legendType, legendOrientation, showLegend, theme), 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 09eed22b973c4..868cc0df361f7 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts @@ -24,6 +24,7 @@ import { getMetricLabel, getColumnLabel, getValueFormatter, + tooltipHtml, } from '@superset-ui/core'; import { EChartsCoreOption, GaugeSeriesOption } from 'echarts'; import { GaugeDataItemOption } from 'echarts/types/src/chart/gauge/GaugeSeries'; @@ -157,6 +158,7 @@ export default function transformProps( const detailOffsetFromTitle = FONT_SIZE_MULTIPLIERS.detailOffsetFromTitle * fontSize; const columnsLabelMap = new Map(); + const metricLabel = getMetricLabel(metric as QueryFormMetric); const transformedData: GaugeDataItemOption[] = data.map( (data_point, index) => { @@ -168,7 +170,7 @@ export default function transformProps( groupbyLabels.map(col => data_point[col] as string), ); let item: GaugeDataItemOption = { - value: data_point[getMetricLabel(metric as QueryFormMetric)] as number, + value: data_point[metricLabel] as number, name, itemStyle: { color: colorFn(index, sliceId), @@ -286,7 +288,7 @@ export default function transformProps( ...getDefaultTooltip(refs), formatter: (params: CallbackDataParams) => { const { name, value } = params; - return `${name} : ${formatValue(value as number)}`; + return tooltipHtml([[metricLabel, formatValue(value as number)]], name); }, }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Graph/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Graph/transformProps.ts index 256b984f7d0c1..e79b5ac874e06 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Graph/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Graph/transformProps.ts @@ -21,6 +21,7 @@ import { getMetricLabel, DataRecord, DataRecordValue, + tooltipHtml, } from '@superset-ui/core'; import { EChartsCoreOption, GraphSeriesOption } from 'echarts'; import { extent as d3Extent } from 'd3-array'; @@ -138,19 +139,6 @@ function getKeyByValue( return Object.keys(object).find(key => object[key] === value) as string; } -function edgeFormatter( - sourceIndex: string, - targetIndex: string, - value: number, - nodes: { [name: string]: number }, -): string { - const source = Number(sourceIndex); - const target = Number(targetIndex); - return `${sanitizeHtml(getKeyByValue(nodes, source))} > ${sanitizeHtml( - getKeyByValue(nodes, target), - )} : ${value}`; -} - function getCategoryName(columnName: string, name?: DataRecordValue) { if (name === false) { return `${columnName}: false`; @@ -321,13 +309,16 @@ export default function transformProps( tooltip: { ...getDefaultTooltip(refs), show: !inContextMenu, - formatter: (params: any): string => - edgeFormatter( - params.data.source, - params.data.target, - params.value, - nodes, - ), + formatter: (params: any): string => { + const source = sanitizeHtml( + getKeyByValue(nodes, Number(params.data.source)), + ); + const target = sanitizeHtml( + getKeyByValue(nodes, Number(params.data.target)), + ); + const title = `${source} > ${target}`; + return tooltipHtml([[metricLabel, `${params.value}`]], title); + }, }, legend: { ...getLegendProps(legendType, legendOrientation, showLegend, theme), diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Heatmap/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Heatmap/transformProps.ts index 832b868779129..90524d12d0b8d 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Heatmap/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Heatmap/transformProps.ts @@ -18,12 +18,14 @@ */ import { GenericDataType, + NumberFormats, QueryFormColumn, getColumnLabel, getMetricLabel, getSequentialSchemeRegistry, getTimeFormatter, getValueFormatter, + tooltipHtml, } from '@superset-ui/core'; import memoizeOne from 'memoize-one'; import { maxBy, minBy } from 'lodash'; @@ -34,6 +36,9 @@ import { getDefaultTooltip } from '../utils/tooltip'; import { Refs } from '../types'; import { parseAxisBound } from '../utils/controls'; import { NULL_STRING } from '../constants'; +import { getPercentFormatter } from '../utils/formatters'; + +const DEFAULT_ECHARTS_BOUNDS = [0, 200]; // Calculated totals per x and y categories plus total const calculateTotals = memoizeOne( @@ -75,7 +80,7 @@ export default function transformProps( linearColorScheme, leftMargin, legendType = 'continuous', - metric, + metric = '', normalizeAcross, normalized, showLegend, @@ -109,6 +114,7 @@ export default function transformProps( const xAxisFormatter = getAxisFormatter(coltypes[0]); const yAxisFormatter = getAxisFormatter(coltypes[1]); + const percentFormatter = getPercentFormatter(NumberFormats.PERCENT_2_POINT); const valueFormatter = getValueFormatter( metric, currencyFormats, @@ -119,10 +125,14 @@ export default function transformProps( let [min, max] = (valueBounds || []).map(parseAxisBound); if (min === undefined) { - min = minBy(data, row => row[colorColumn])?.[colorColumn] as number; + min = + (minBy(data, row => row[colorColumn])?.[colorColumn] as number) || + DEFAULT_ECHARTS_BOUNDS[0]; } if (max === undefined) { - max = maxBy(data, row => row[colorColumn])?.[colorColumn] as number; + max = + (maxBy(data, row => row[colorColumn])?.[colorColumn] as number) || + DEFAULT_ECHARTS_BOUNDS[1]; } const series: HeatmapSeriesOption[] = [ @@ -175,29 +185,22 @@ export default function transformProps( let suffix = 'heatmap'; if (typeof value === 'number') { if (normalizeAcross === 'x') { - percentage = (value / totals.x[x]) * 100; + percentage = value / totals.x[x]; suffix = formattedX; } else if (normalizeAcross === 'y') { - percentage = (value / totals.y[y]) * 100; + percentage = value / totals.y[y]; suffix = formattedY; } else { - percentage = (value / totals.total) * 100; + percentage = value / totals.total; suffix = 'heatmap'; } } - return ` -
-
${colnames[0]}: ${formattedX}
-
${colnames[1]}: ${formattedY}
-
${colnames[2]}: ${formattedValue}
- ${ - showPercentage - ? `
% (${suffix}): ${valueFormatter( - percentage, - )}%
` - : '' - } -
`; + const title = `${formattedX} (${formattedY})`; + const row = [colnames[2], formattedValue]; + if (showPercentage) { + row.push(`${percentFormatter(percentage)} (${suffix})`); + } + return tooltipHtml([row], title); }, }, visualMap: { diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts index 7488d49dbaf00..49cb210552dce 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts @@ -35,10 +35,12 @@ import { isIntervalAnnotationLayer, isPhysicalColumn, isTimeseriesAnnotationLayer, + NumberFormats, QueryFormData, QueryFormMetric, TimeseriesChartDataResponseResult, TimeseriesDataRecord, + tooltipHtml, ValueFormatter, } from '@superset-ui/core'; import { getOriginalSeries } from '@superset-ui/chart-controls'; @@ -89,6 +91,7 @@ import { import { TIMEGRAIN_TO_TIMESTAMP, TIMESERIES_CONSTANTS } from '../constants'; import { getDefaultTooltip } from '../utils/tooltip'; import { + getPercentFormatter, getTooltipTimeFormatter, getXAxisFormatter, getYAxisFormatter, @@ -231,6 +234,7 @@ export default function transformProps( const xAxisDataType = dataTypes?.[xAxisLabel] ?? dataTypes?.[xAxisOrig]; const xAxisType = getAxisType(stack, xAxisForceCategorical, xAxisDataType); const series: SeriesOption[] = []; + const percentFormatter = getPercentFormatter(NumberFormats.PERCENT_2_POINT); const formatter = contributionMode ? getNumberFormatter(',.0%') : currencyFormat?.symbol @@ -581,11 +585,23 @@ export default function transformProps( forecastValue.sort((a, b) => b.data[1] - a.data[1]); } - const rows: Array = [`${tooltipFormatter(xValue)}`]; + const rows: string[][] = []; const forecastValues = extractForecastValuesFromTooltipParams(forecastValue); - Object.keys(forecastValues).forEach(key => { + const isForecast = Object.values(forecastValues).some( + value => + value.forecastTrend || value.forecastLower || value.forecastUpper, + ); + + const total = Object.values(forecastValues).reduce( + (acc, value) => + value.observation !== undefined ? acc + value.observation : acc, + 0, + ); + const showTotal = richTooltip && !isForecast; + const keys = Object.keys(forecastValues); + keys.forEach(key => { const value = forecastValues[key]; // if there are no dimensions, key is a verbose name of a metric, // otherwise it is a comma separated string where the first part is metric name @@ -611,18 +627,30 @@ export default function transformProps( formatterKey, !!contributionMode, ); - const content = formatForecastTooltipSeries({ + const row = formatForecastTooltipSeries({ ...value, seriesName: key, formatter: primarySeries.has(key) ? tooltipFormatter : tooltipFormatterSecondary, }); - const contentStyle = - key === focusedSeries ? 'font-weight: 700' : 'opacity: 0.7'; - rows.push(`${content}`); + if (showTotal && value.observation !== undefined) { + row.push(percentFormatter.format(value.observation / (total || 1))); + } + rows.push(row); }); - return rows.join('
'); + if (showTotal) { + rows.push([ + 'Total', + formatter.format(total), + percentFormatter.format(1), + ]); + } + return tooltipHtml( + rows, + tooltipFormatter(xValue), + keys.findIndex(key => key === focusedSeries), + ); }, }, legend: { diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts index 031b072b449de..82986ffcb6367 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts @@ -26,6 +26,7 @@ import { t, ValueFormatter, getValueFormatter, + tooltipHtml, } from '@superset-ui/core'; import { CallbackDataParams } from 'echarts/types/src/util/types'; import { EChartsCoreOption, PieSeriesOption } from 'echarts'; @@ -51,40 +52,20 @@ import { Refs } from '../types'; const percentFormatter = getNumberFormatter(NumberFormats.PERCENT_2_POINT); -export function formatPieLabel({ +export function parseParams({ params, - labelType, numberFormatter, sanitizeName = false, }: { params: Pick; - labelType: EchartsPieLabelType; numberFormatter: ValueFormatter; sanitizeName?: boolean; -}): string { +}): string[] { const { name: rawName = '', value, percent } = params; const name = sanitizeName ? sanitizeHtml(rawName) : rawName; const formattedValue = numberFormatter(value as number); const formattedPercent = percentFormatter((percent as number) / 100); - - switch (labelType) { - case EchartsPieLabelType.Key: - return name; - case EchartsPieLabelType.Value: - return formattedValue; - case EchartsPieLabelType.Percent: - return formattedPercent; - case EchartsPieLabelType.KeyValue: - return `${name}: ${formattedValue}`; - case EchartsPieLabelType.KeyValuePercent: - return `${name}: ${formattedValue} (${formattedPercent})`; - case EchartsPieLabelType.KeyPercent: - return `${name}: ${formattedPercent}`; - case EchartsPieLabelType.ValuePercent: - return `${formattedValue} (${formattedPercent})`; - default: - return name; - } + return [name, formattedValue, formattedPercent]; } function getTotalValuePadding({ @@ -260,12 +241,30 @@ export default function transformProps( {}, ); - const formatter = (params: CallbackDataParams) => - formatPieLabel({ + const formatter = (params: CallbackDataParams) => { + const [name, formattedValue, formattedPercent] = parseParams({ params, numberFormatter, - labelType, }); + switch (labelType) { + case EchartsPieLabelType.Key: + return name; + case EchartsPieLabelType.Value: + return formattedValue; + case EchartsPieLabelType.Percent: + return formattedPercent; + case EchartsPieLabelType.KeyValue: + return `${name}: ${formattedValue}`; + case EchartsPieLabelType.KeyValuePercent: + return `${name}: ${formattedValue} (${formattedPercent})`; + case EchartsPieLabelType.KeyPercent: + return `${name}: ${formattedPercent}`; + case EchartsPieLabelType.ValuePercent: + return `${formattedValue} (${formattedPercent})`; + default: + return name; + } + }; const defaultLabel = { formatter, @@ -319,13 +318,17 @@ export default function transformProps( ...getDefaultTooltip(refs), show: !inContextMenu, trigger: 'item', - formatter: (params: any) => - formatPieLabel({ + formatter: (params: any) => { + const [name, formattedValue, formattedPercent] = parseParams({ params, numberFormatter, - labelType: EchartsPieLabelType.KeyValuePercent, sanitizeName: true, - }), + }); + return tooltipHtml( + [[metricLabel, formattedValue, formattedPercent]], + name, + ); + }, }, legend: { ...getLegendProps(legendType, legendOrientation, showLegend, theme), 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 d0b2d079cd25e..7006e2178db35 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts @@ -26,8 +26,8 @@ import { getTimeFormatter, getValueFormatter, NumberFormats, - SupersetTheme, t, + tooltipHtml, ValueFormatter, } from '@superset-ui/core'; import { EChartsCoreOption } from 'echarts'; @@ -100,7 +100,6 @@ export function formatTooltip({ totalValue, metricLabel, secondaryMetricLabel, - theme, }: { params: CallbackDataParams & { treePathInfo: { @@ -115,7 +114,6 @@ export function formatTooltip({ totalValue: number; metricLabel: string; secondaryMetricLabel?: string; - theme: SupersetTheme; }): string { const { data, treePathInfo = [] } = params; const node = data as TreeNode; @@ -132,44 +130,29 @@ export function formatTooltip({ const parentNode = treePathInfo.length > 2 ? treePathInfo[treePathInfo.length - 2] : undefined; - const result = [ - `
`, - `
- ${(node.name || NULL_STRING) - .toString() - .replaceAll('<', '<') - .replaceAll('>', '>')} -
`, - ` - ${absolutePercentage} of total -
`, - ]; + const title = (node.name || NULL_STRING) + .toString() + .replaceAll('<', '<') + .replaceAll('>', '>'); + const rows = [[t('% of total'), absolutePercentage]]; if (parentNode) { const conditionalPercentage = percentFormatter( node.value / parentNode.value, ); - result.push(` -
- ${conditionalPercentage} of ${parentNode.name} -
`); + rows.push([t('% of parent'), conditionalPercentage]); } - result.push( - `
- ${metricLabel}: ${formattedValue}${ - colorByCategory - ? '' - : `, ${secondaryMetricLabel}: ${formattedSecondaryValue}` - } -
`, - colorByCategory - ? '' - : `
${metricLabel}/${secondaryMetricLabel}: ${compareValuePercentage}
`, - ); - result.push(''); - return result.join('\n'); + rows.push([metricLabel, formattedValue]); + if (!colorByCategory) { + rows.push([ + secondaryMetricLabel || NULL_STRING, + formattedSecondaryValue || NULL_STRING, + ]); + rows.push([ + `${metricLabel}/${secondaryMetricLabel}`, + compareValuePercentage, + ]); + } + return tooltipHtml(rows, title); } export default function transformProps( @@ -353,7 +336,6 @@ export default function transformProps( totalValue, metricLabel, secondaryMetricLabel, - theme, }), }, series: [ diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts index 4ec49767e2697..d3e7673ea738d 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts @@ -25,6 +25,7 @@ import { CategoricalColorNamespace, CurrencyFormatter, ensureIsArray, + tooltipHtml, GenericDataType, getCustomFormatter, getMetricLabel, @@ -38,6 +39,7 @@ import { isTimeseriesAnnotationLayer, t, TimeseriesChartDataResponseResult, + NumberFormats, } from '@superset-ui/core'; import { extractExtraMetrics, @@ -254,7 +256,9 @@ export default function transformProps( const series: SeriesOption[] = []; const forcePercentFormatter = Boolean(contributionMode || isAreaExpand); - const percentFormatter = getPercentFormatter(yAxisFormat); + const percentFormatter = forcePercentFormatter + ? getPercentFormatter(yAxisFormat) + : getPercentFormatter(NumberFormats.PERCENT_2_POINT); const defaultFormatter = currencyFormat?.symbol ? new CurrencyFormatter({ d3Format: yAxisFormat, currency: currencyFormat }) : getNumberFormatter(yAxisFormat); @@ -526,36 +530,58 @@ export default function transformProps( forecastValue.sort((a, b) => b.data[yIndex] - a.data[yIndex]); } - const rows: string[] = []; const forecastValues: Record = extractForecastValuesFromTooltipParams(forecastValue, isHorizontal); - Object.keys(forecastValues).forEach(key => { + const isForecast = Object.values(forecastValues).some( + value => + value.forecastTrend || value.forecastLower || value.forecastUpper, + ); + + const formatter = forcePercentFormatter + ? percentFormatter + : getCustomFormatter(customFormatters, metrics) ?? defaultFormatter; + + const rows: string[][] = []; + const total = Object.values(forecastValues).reduce( + (acc, value) => + value.observation !== undefined ? acc + value.observation : acc, + 0, + ); + const showTotal = Boolean(isMultiSeries) && richTooltip && !isForecast; + const showPercentage = showTotal && !forcePercentFormatter; + const keys = Object.keys(forecastValues); + keys.forEach(key => { const value = forecastValues[key]; if (value.observation === 0 && stack) { return; } - // if there are no dimensions, key is a verbose name of a metric, - // otherwise it is a comma separated string where the first part is metric name - const formatterKey = - groupBy.length === 0 ? inverted[key] : labelMap[key]?.[0]; - const content = formatForecastTooltipSeries({ + const row = formatForecastTooltipSeries({ ...value, seriesName: key, - formatter: forcePercentFormatter - ? percentFormatter - : getCustomFormatter(customFormatters, metrics, formatterKey) ?? - defaultFormatter, + formatter, }); - const contentStyle = - key === focusedSeries ? 'font-weight: 700' : 'opacity: 0.7'; - rows.push(`${content}`); + if (showPercentage && value.observation !== undefined) { + row.push(percentFormatter.format(value.observation / (total || 1))); + } + rows.push(row); }); if (stack) { + keys.reverse(); rows.reverse(); } - rows.unshift(`${tooltipFormatter(xValue)}`); - return rows.join('
'); + if (showTotal) { + const totalRow = ['Total', formatter.format(total)]; + if (showPercentage) { + totalRow.push(percentFormatter.format(1)); + } + rows.push(totalRow); + } + return tooltipHtml( + rows, + tooltipFormatter(xValue), + keys.findIndex(key => key === focusedSeries), + ); }, }, legend: { diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Tree/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Tree/transformProps.ts index ca3a4a9341f8a..a0b08816db5d2 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Tree/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Tree/transformProps.ts @@ -16,7 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -import { getMetricLabel, DataRecordValue } from '@superset-ui/core'; +import { + getMetricLabel, + DataRecordValue, + tooltipHtml, +} from '@superset-ui/core'; import { EChartsCoreOption, TreeSeriesOption } from 'echarts'; import { TreeSeriesCallbackDataParams, @@ -44,11 +48,8 @@ export function formatTooltip({ const treePath = (treeAncestors ?? []) .map(pathInfo => pathInfo?.name || '') .filter(path => path !== ''); - - return [ - `
${treePath.join(' ▸ ')}
`, - value ? `${metricLabel}: ${value}` : '', - ].join(''); + const row = value ? [metricLabel, String(value)] : []; + return tooltipHtml([row], treePath.join(' ▸ ')); } export default function transformProps( diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/transformProps.ts index 8a076748ad8cb..09bdd191541dd 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/transformProps.ts @@ -25,6 +25,7 @@ import { NumberFormats, ValueFormatter, getValueFormatter, + tooltipHtml, } from '@superset-ui/core'; import { TreemapSeriesNodeItemOption } from 'echarts/types/src/chart/treemap/TreemapSeries'; import { EChartsCoreOption, TreemapSeriesOption } from 'echarts'; @@ -96,14 +97,11 @@ export function formatTooltip({ : 0; formattedPercent = percentFormatter(percent); } - - // groupby1/groupby2/... - // metric: value (percent of parent) - return [ - `
${treePath.join(' ▸ ')}
`, - `${metricLabel}: ${formattedValue}`, - formattedPercent ? ` (${formattedPercent})` : '', - ].join(''); + const row = [metricLabel, formattedValue]; + if (formattedPercent) { + row.push(formattedPercent); + } + return tooltipHtml([row], treePath.join(' ▸ ')); } export default function transformProps( diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Waterfall/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Waterfall/transformProps.ts index 09c3e4e241b1c..f9ff6c68dc8fb 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Waterfall/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Waterfall/transformProps.ts @@ -27,7 +27,7 @@ import { isAdhocColumn, NumberFormatter, rgbToHex, - SupersetTheme, + tooltipHtml, } from '@superset-ui/core'; import { EChartsOption, BarSeriesOption } from 'echarts'; import { @@ -44,13 +44,11 @@ import { Refs } from '../types'; import { NULL_STRING } from '../constants'; function formatTooltip({ - theme, params, breakdownName, defaultFormatter, xAxisFormatter, }: { - theme: SupersetTheme; params: ICallbackDataParams[]; breakdownName?: string; defaultFormatter: NumberFormatter | CurrencyFormatter; @@ -70,40 +68,19 @@ function formatTooltip({ return NULL_STRING; } - const createRow = (name: string, value: string) => ` -
- - ${name}: - - - ${value} - -
- `; - - let result = ''; - if (!isTotal || breakdownName) { - result = xAxisFormatter(series.name, series.dataIndex); - } + const title = + !isTotal || breakdownName + ? xAxisFormatter(series.name, series.dataIndex) + : undefined; + const rows: string[][] = []; if (!isTotal) { - result += createRow( + rows.push([ series.seriesName!, defaultFormatter(series.data.originalValue), - ); + ]); } - result += createRow(TOTAL_MARK, defaultFormatter(series.data.totalSum)); - return result; + rows.push([TOTAL_MARK, defaultFormatter(series.data.totalSum)]); + return tooltipHtml(rows, title); } function transformer({ @@ -463,7 +440,6 @@ export default function transformProps( show: !inContextMenu, formatter: (params: any) => formatTooltip({ - theme, params, breakdownName, defaultFormatter, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/forecast.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/forecast.ts index 21a8f54129607..a68dafe8d4e7d 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/forecast.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/forecast.ts @@ -95,23 +95,26 @@ export const formatForecastTooltipSeries = ({ seriesName: string; marker: TooltipMarker; formatter: ValueFormatter; -}): string => { - let row = `${marker}${sanitizeHtml(seriesName)}: `; - let isObservation = false; - if (isNumber(observation)) { - isObservation = true; - row += `${formatter(observation)}`; - } - if (forecastTrend) { - if (isObservation) row += ', '; - row += `ŷ = ${formatter(forecastTrend)}`; +}): string[] => { + const name = `${marker}${sanitizeHtml(seriesName)}`; + let value = isNumber(observation) ? formatter(observation) : ''; + if (forecastTrend || forecastLower || forecastUpper) { + // forecast values take the form of "20, y = 30 (10, 40)" + // where the first part is the observation, the second part is the forecast trend + // and the third part is the lower and upper bounds + if (forecastTrend) { + if (value) value += ', '; + value += `ŷ = ${formatter(forecastTrend)}`; + } + if (forecastLower && forecastUpper) { + if (value) value += ' '; + // the lower bound needs to be added to the upper bound + value += `(${formatter(forecastLower)}, ${formatter( + forecastLower + forecastUpper, + )})`; + } } - if (forecastLower && forecastUpper) - // the lower bound needs to be added to the upper bound - row = `${row.trim()} (${formatter(forecastLower)}, ${formatter( - forecastLower + forecastUpper, - )})`; - return `${row.trim()}`; + return [name, value]; }; export function rebaseForecastDatum( diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/tooltip.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/tooltip.ts index c1fd7ac0a3f7f..7110cae6d5cdb 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/tooltip.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/tooltip.ts @@ -24,6 +24,7 @@ import { Refs } from '../types'; export function getDefaultTooltip(refs: Refs) { return { appendToBody: true, + borderColor: 'transparent', position: ( canvasMousePos: [number, number], params: CallbackDataParams, diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Bubble/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Bubble/transformProps.test.ts index d93f394681205..ead2abe072ee6 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Bubble/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Bubble/transformProps.test.ts @@ -160,42 +160,44 @@ describe('Bubble formatTooltip', () => { data: [10000, 20000, 3, 'bubble title', 'bubble dimension'], }; - expect( - formatTooltip( - params, - 'x-axis-label', - 'y-axis-label', - 'size-label', - dollerFormatter, - dollerFormatter, - percentFormatter, - ), - ).toEqual( - `

bubble title
bubble dimension

- x-axis-label: $10,000.00
- y-axis-label: $20,000.00
- size-label: 300.0%`, + const html = formatTooltip( + params, + 'x-axis-label', + 'y-axis-label', + 'size-label', + dollerFormatter, + dollerFormatter, + percentFormatter, ); + expect(html).toContain('bubble title'); + expect(html).toContain('bubble dimension'); + expect(html).toContain('x-axis-label'); + expect(html).toContain('y-axis-label'); + expect(html).toContain('size-label'); + expect(html).toContain('$10,000.00'); + expect(html).toContain('$20,000.00'); + expect(html).toContain('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( - `

bubble title

- x-axis-label: $10,000.00
- y-axis-label: $25,000.00
- size-label: 300.0%`, + const html = formatTooltip( + params, + 'x-axis-label', + 'y-axis-label', + 'size-label', + dollerFormatter, + dollerFormatter, + percentFormatter, ); + expect(html).toContain('bubble title'); + expect(html).not.toContain('bubble dimension'); + expect(html).toContain('x-axis-label'); + expect(html).toContain('y-axis-label'); + expect(html).toContain('size-label'); + expect(html).toContain('$10,000.00'); + expect(html).toContain('$25,000.00'); + expect(html).toContain('300.0%'); }); }); diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Funnel/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Funnel/transformProps.test.ts index 4d326ab3020b4..08f225feda3ed 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Funnel/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Funnel/transformProps.test.ts @@ -21,12 +21,9 @@ import { getNumberFormatter, supersetTheme, } from '@superset-ui/core'; -import transformProps, { - formatFunnelLabel, -} from '../../src/Funnel/transformProps'; +import transformProps, { parseParams } from '../../src/Funnel/transformProps'; import { EchartsFunnelChartProps, - EchartsFunnelLabelTypeType, PercentCalcType, } from '../../src/Funnel/types'; @@ -89,85 +86,40 @@ describe('formatFunnelLabel', () => { data: { firstStepPercent: 0.5, prevStepPercent: 0.85 }, }; expect( - formatFunnelLabel({ + parseParams({ params, numberFormatter, - labelType: EchartsFunnelLabelTypeType.Key, percentCalculationType: PercentCalcType.Total, }), - ).toEqual('My Label'); + ).toEqual(['My Label', '1.23k', '12.34%']); expect( - formatFunnelLabel({ + parseParams({ params, numberFormatter, - labelType: EchartsFunnelLabelTypeType.Value, - percentCalculationType: PercentCalcType.Total, - }), - ).toEqual('1.23k'); - expect( - formatFunnelLabel({ - params, - numberFormatter, - labelType: EchartsFunnelLabelTypeType.Percent, - percentCalculationType: PercentCalcType.Total, - }), - ).toEqual('12.34%'); - expect( - formatFunnelLabel({ - params, - numberFormatter, - labelType: EchartsFunnelLabelTypeType.Percent, percentCalculationType: PercentCalcType.FirstStep, }), - ).toEqual('50.00%'); + ).toEqual(['My Label', '1.23k', '50.00%']); expect( - formatFunnelLabel({ + parseParams({ params, numberFormatter, - labelType: EchartsFunnelLabelTypeType.Percent, percentCalculationType: PercentCalcType.PreviousStep, }), - ).toEqual('85.00%'); - expect( - formatFunnelLabel({ - params, - numberFormatter, - labelType: EchartsFunnelLabelTypeType.KeyValue, - percentCalculationType: PercentCalcType.Total, - }), - ).toEqual('My Label: 1.23k'); - expect( - formatFunnelLabel({ - params, - numberFormatter, - labelType: EchartsFunnelLabelTypeType.KeyPercent, - percentCalculationType: PercentCalcType.Total, - }), - ).toEqual('My Label: 12.34%'); - expect( - formatFunnelLabel({ - params, - numberFormatter, - labelType: EchartsFunnelLabelTypeType.KeyValuePercent, - percentCalculationType: PercentCalcType.Total, - }), - ).toEqual('My Label: 1.23k (12.34%)'); + ).toEqual(['My Label', '1.23k', '85.00%']); expect( - formatFunnelLabel({ + parseParams({ params: { ...params, name: '' }, numberFormatter, - labelType: EchartsFunnelLabelTypeType.Key, percentCalculationType: PercentCalcType.Total, }), - ).toEqual(''); + ).toEqual(['', '1.23k', '12.34%']); expect( - formatFunnelLabel({ + parseParams({ params: { ...params, name: '' }, numberFormatter, - labelType: EchartsFunnelLabelTypeType.Key, percentCalculationType: PercentCalcType.Total, sanitizeName: true, }), - ).toEqual('<NULL>'); + ).toEqual(['<NULL>', '1.23k', '12.34%']); }); }); diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Graph/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Graph/transformProps.test.ts index 100e39a27e82a..3c2e6e238445a 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Graph/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Graph/transformProps.test.ts @@ -81,11 +81,7 @@ describe('EchartsGraph transformProps', () => { label: { fontWeight: 'bolder' }, }, symbolSize: 50, - tooltip: { - appendToBody: true, - formatter: '{b}: {c}', - position: expect.anything(), - }, + tooltip: expect.anything(), value: 6, }, { @@ -99,11 +95,7 @@ describe('EchartsGraph transformProps', () => { label: { fontWeight: 'bolder' }, }, symbolSize: 50, - tooltip: { - appendToBody: true, - formatter: '{b}: {c}', - position: expect.anything(), - }, + tooltip: expect.anything(), value: 6, }, { @@ -117,11 +109,7 @@ describe('EchartsGraph transformProps', () => { label: { fontWeight: 'bolder' }, }, symbolSize: 10, - tooltip: { - appendToBody: true, - formatter: '{b}: {c}', - position: expect.anything(), - }, + tooltip: expect.anything(), value: 5, }, { @@ -135,11 +123,7 @@ describe('EchartsGraph transformProps', () => { label: { fontWeight: 'bolder' }, }, symbolSize: 10, - tooltip: { - appendToBody: true, - formatter: '{b}: {c}', - position: expect.anything(), - }, + tooltip: expect.anything(), value: 5, }, ], @@ -239,11 +223,7 @@ describe('EchartsGraph transformProps', () => { symbolSize: 10, category: 'category_value_1', select: DEFAULT_GRAPH_SERIES_OPTION.select, - tooltip: { - appendToBody: true, - formatter: '{b}: {c}', - position: expect.anything(), - }, + tooltip: expect.anything(), label: { show: true }, }, { @@ -254,11 +234,7 @@ describe('EchartsGraph transformProps', () => { symbolSize: 10, category: 'category_value_2', select: DEFAULT_GRAPH_SERIES_OPTION.select, - tooltip: { - appendToBody: true, - formatter: '{b}: {c}', - position: expect.anything(), - }, + tooltip: expect.anything(), label: { show: true }, }, ], diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Pie/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Pie/transformProps.test.ts index 070e27df62be3..1add75d40151f 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Pie/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Pie/transformProps.test.ts @@ -22,8 +22,8 @@ import { SqlaFormData, supersetTheme, } from '@superset-ui/core'; -import transformProps, { formatPieLabel } from '../../src/Pie/transformProps'; -import { EchartsPieChartProps, EchartsPieLabelType } from '../../src/Pie/types'; +import transformProps, { parseParams } from '../../src/Pie/transformProps'; +import { EchartsPieChartProps } from '../../src/Pie/types'; describe('Pie transformProps', () => { const formData: SqlaFormData = { @@ -81,61 +81,23 @@ describe('formatPieLabel', () => { const numberFormatter = getNumberFormatter(); const params = { name: 'My Label', value: 1234, percent: 12.34 }; expect( - formatPieLabel({ + parseParams({ params, numberFormatter, - labelType: EchartsPieLabelType.Key, }), - ).toEqual('My Label'); + ).toEqual(['My Label', '1.23k', '12.34%']); expect( - formatPieLabel({ - params, - numberFormatter, - labelType: EchartsPieLabelType.Value, - }), - ).toEqual('1.23k'); - expect( - formatPieLabel({ - params, - numberFormatter, - labelType: EchartsPieLabelType.Percent, - }), - ).toEqual('12.34%'); - expect( - formatPieLabel({ - params, - numberFormatter, - labelType: EchartsPieLabelType.KeyValue, - }), - ).toEqual('My Label: 1.23k'); - expect( - formatPieLabel({ - params, - numberFormatter, - labelType: EchartsPieLabelType.KeyPercent, - }), - ).toEqual('My Label: 12.34%'); - expect( - formatPieLabel({ - params, - numberFormatter, - labelType: EchartsPieLabelType.KeyValuePercent, - }), - ).toEqual('My Label: 1.23k (12.34%)'); - expect( - formatPieLabel({ + parseParams({ params: { ...params, name: '' }, numberFormatter, - labelType: EchartsPieLabelType.Key, }), - ).toEqual(''); + ).toEqual(['', '1.23k', '12.34%']); expect( - formatPieLabel({ + parseParams({ params: { ...params, name: '' }, numberFormatter, - labelType: EchartsPieLabelType.Key, sanitizeName: true, }), - ).toEqual('<NULL>'); + ).toEqual(['<NULL>', '1.23k', '12.34%']); }); }); diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/utils/forecast.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/utils/forecast.test.ts index f3d6f60267207..1d35a2b117eda 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/utils/forecast.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/utils/forecast.test.ts @@ -234,7 +234,7 @@ test('formatForecastTooltipSeries should apply format to value', () => { observation: 10.1, formatter, }), - ).toEqual('abc: 10'); + ).toEqual(['abc', '10']); }); test('formatForecastTooltipSeries should show falsy value', () => { @@ -245,7 +245,7 @@ test('formatForecastTooltipSeries should show falsy value', () => { observation: 0, formatter, }), - ).toEqual('abc: 0'); + ).toEqual(['abc', '0']); }); test('formatForecastTooltipSeries should format full forecast', () => { @@ -259,7 +259,7 @@ test('formatForecastTooltipSeries should format full forecast', () => { forecastUpper: 7.1, formatter, }), - ).toEqual('qwerty: 10, ŷ = 20 (5, 12)'); + ).toEqual(['qwerty', '10, ŷ = 20 (5, 12)']); }); test('formatForecastTooltipSeries should format forecast without observation', () => { @@ -272,7 +272,7 @@ test('formatForecastTooltipSeries should format forecast without observation', ( forecastUpper: 7, formatter, }), - ).toEqual('qwerty: ŷ = 20 (5, 12)'); + ).toEqual(['qwerty', 'ŷ = 20 (5, 12)']); }); test('formatForecastTooltipSeries should format forecast without point estimate', () => { @@ -285,7 +285,7 @@ test('formatForecastTooltipSeries should format forecast without point estimate' forecastUpper: 7, formatter, }), - ).toEqual('qwerty: 10 (6, 13)'); + ).toEqual(['qwerty', '10 (6, 13)']); }); test('formatForecastTooltipSeries should format forecast with only confidence band', () => { @@ -297,5 +297,5 @@ test('formatForecastTooltipSeries should format forecast with only confidence ba forecastUpper: 8, formatter, }), - ).toEqual('qwerty: (7, 15)'); + ).toEqual(['qwerty', '(7, 15)']); });