mirror of
https://github.com/apache/superset.git
synced 2026-04-19 16:14:52 +00:00
feat: Utility function to render chart tooltips (#27950)
This commit is contained in:
committed by
GitHub
parent
467e612533
commit
b549977f05
@@ -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])}
|
||||
<br />
|
||||
<strong>
|
||||
${
|
||||
params[0].data[1] === null ? t('N/A') : formatValue(params[0].data[1])
|
||||
}
|
||||
</strong>
|
||||
`;
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -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]} </br> ${params.data[4]}`
|
||||
? `${params.data[4]} (${params.data[3]})`
|
||||
: params.data[3];
|
||||
|
||||
return `<p>${title}</p>
|
||||
${xAxisLabel}: ${xAxisFormatter(params.data[0])} <br/>
|
||||
${yAxisLabel}: ${yAxisFormatter(params.data[1])} <br/>
|
||||
${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) {
|
||||
|
||||
@@ -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<CallbackDataParams, 'name' | 'value' | 'percent' | 'data'>;
|
||||
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),
|
||||
|
||||
@@ -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<string, string[]>();
|
||||
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);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 `
|
||||
<div>
|
||||
<div>${colnames[0]}: <b>${formattedX}</b></div>
|
||||
<div>${colnames[1]}: <b>${formattedY}</b></div>
|
||||
<div>${colnames[2]}: <b>${formattedValue}</b></div>
|
||||
${
|
||||
showPercentage
|
||||
? `<div>% (${suffix}): <b>${valueFormatter(
|
||||
percentage,
|
||||
)}%</b></div>`
|
||||
: ''
|
||||
}
|
||||
</div>`;
|
||||
const title = `${formattedX} (${formattedY})`;
|
||||
const row = [colnames[2], formattedValue];
|
||||
if (showPercentage) {
|
||||
row.push(`${percentFormatter(percentage)} (${suffix})`);
|
||||
}
|
||||
return tooltipHtml([row], title);
|
||||
},
|
||||
},
|
||||
visualMap: {
|
||||
|
||||
@@ -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<string> = [`${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(`<span style="${contentStyle}">${content}</span>`);
|
||||
if (showTotal && value.observation !== undefined) {
|
||||
row.push(percentFormatter.format(value.observation / (total || 1)));
|
||||
}
|
||||
rows.push(row);
|
||||
});
|
||||
return rows.join('<br />');
|
||||
if (showTotal) {
|
||||
rows.push([
|
||||
'Total',
|
||||
formatter.format(total),
|
||||
percentFormatter.format(1),
|
||||
]);
|
||||
}
|
||||
return tooltipHtml(
|
||||
rows,
|
||||
tooltipFormatter(xValue),
|
||||
keys.findIndex(key => key === focusedSeries),
|
||||
);
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
|
||||
@@ -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<CallbackDataParams, 'name' | 'value' | 'percent'>;
|
||||
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),
|
||||
|
||||
@@ -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 = [
|
||||
`<div style="
|
||||
font-size: ${theme.typography.sizes.m}px;
|
||||
color: ${theme.colors.grayscale.base}"
|
||||
>`,
|
||||
`<div style="font-weight: ${theme.typography.weights.bold}">
|
||||
${(node.name || NULL_STRING)
|
||||
.toString()
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>')}
|
||||
</div>`,
|
||||
`<div">
|
||||
${absolutePercentage} of total
|
||||
</div>`,
|
||||
];
|
||||
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(`
|
||||
<div>
|
||||
${conditionalPercentage} of ${parentNode.name}
|
||||
</div>`);
|
||||
rows.push([t('% of parent'), conditionalPercentage]);
|
||||
}
|
||||
result.push(
|
||||
`<div>
|
||||
${metricLabel}: ${formattedValue}${
|
||||
colorByCategory
|
||||
? ''
|
||||
: `, ${secondaryMetricLabel}: ${formattedSecondaryValue}`
|
||||
}
|
||||
</div>`,
|
||||
colorByCategory
|
||||
? ''
|
||||
: `<div>${metricLabel}/${secondaryMetricLabel}: ${compareValuePercentage}</div>`,
|
||||
);
|
||||
result.push('</div>');
|
||||
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: [
|
||||
|
||||
@@ -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<string, ForecastValue> =
|
||||
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(`<span style="${contentStyle}">${content}</span>`);
|
||||
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('<br />');
|
||||
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: {
|
||||
|
||||
@@ -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 [
|
||||
`<div>${treePath.join(' ▸ ')}</div>`,
|
||||
value ? `${metricLabel}: ${value}` : '',
|
||||
].join('');
|
||||
const row = value ? [metricLabel, String(value)] : [];
|
||||
return tooltipHtml([row], treePath.join(' ▸ '));
|
||||
}
|
||||
|
||||
export default function transformProps(
|
||||
|
||||
@@ -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 [
|
||||
`<div>${treePath.join(' ▸ ')}</div>`,
|
||||
`${metricLabel}: ${formattedValue}`,
|
||||
formattedPercent ? ` (${formattedPercent})` : '',
|
||||
].join('');
|
||||
const row = [metricLabel, formattedValue];
|
||||
if (formattedPercent) {
|
||||
row.push(formattedPercent);
|
||||
}
|
||||
return tooltipHtml([row], treePath.join(' ▸ '));
|
||||
}
|
||||
|
||||
export default function transformProps(
|
||||
|
||||
@@ -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) => `
|
||||
<div>
|
||||
<span style="
|
||||
font-size:${theme.typography.sizes.m}px;
|
||||
color:${theme.colors.grayscale.base};
|
||||
font-weight:${theme.typography.weights.normal};
|
||||
margin-left:${theme.gridUnit * 0.5}px;"
|
||||
>
|
||||
${name}:
|
||||
</span>
|
||||
<span style="
|
||||
float:right;
|
||||
margin-left:${theme.gridUnit * 5}px;
|
||||
font-size:${theme.typography.sizes.m}px;
|
||||
color:${theme.colors.grayscale.base};
|
||||
font-weight:${theme.typography.weights.bold}"
|
||||
>
|
||||
${value}
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
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,
|
||||
|
||||
@@ -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)}`;
|
||||
}): 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 (forecastTrend) {
|
||||
if (isObservation) row += ', ';
|
||||
row += `ŷ = ${formatter(forecastTrend)}`;
|
||||
}
|
||||
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(
|
||||
|
||||
@@ -24,6 +24,7 @@ import { Refs } from '../types';
|
||||
export function getDefaultTooltip(refs: Refs) {
|
||||
return {
|
||||
appendToBody: true,
|
||||
borderColor: 'transparent',
|
||||
position: (
|
||||
canvasMousePos: [number, number],
|
||||
params: CallbackDataParams,
|
||||
|
||||
@@ -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(
|
||||
`<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%`,
|
||||
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(
|
||||
`<p>bubble title</p>
|
||||
x-axis-label: $10,000.00 <br/>
|
||||
y-axis-label: $25,000.00 <br/>
|
||||
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%');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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%');
|
||||
).toEqual(['My Label', '1.23k', '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%)');
|
||||
expect(
|
||||
formatFunnelLabel({
|
||||
parseParams({
|
||||
params: { ...params, name: '<NULL>' },
|
||||
numberFormatter,
|
||||
labelType: EchartsFunnelLabelTypeType.Key,
|
||||
percentCalculationType: PercentCalcType.Total,
|
||||
}),
|
||||
).toEqual('<NULL>');
|
||||
).toEqual(['<NULL>', '1.23k', '12.34%']);
|
||||
expect(
|
||||
formatFunnelLabel({
|
||||
parseParams({
|
||||
params: { ...params, name: '<NULL>' },
|
||||
numberFormatter,
|
||||
labelType: EchartsFunnelLabelTypeType.Key,
|
||||
percentCalculationType: PercentCalcType.Total,
|
||||
sanitizeName: true,
|
||||
}),
|
||||
).toEqual('<NULL>');
|
||||
).toEqual(['<NULL>', '1.23k', '12.34%']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 },
|
||||
},
|
||||
],
|
||||
|
||||
@@ -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: '<NULL>' },
|
||||
numberFormatter,
|
||||
labelType: EchartsPieLabelType.Key,
|
||||
}),
|
||||
).toEqual('<NULL>');
|
||||
).toEqual(['<NULL>', '1.23k', '12.34%']);
|
||||
expect(
|
||||
formatPieLabel({
|
||||
parseParams({
|
||||
params: { ...params, name: '<NULL>' },
|
||||
numberFormatter,
|
||||
labelType: EchartsPieLabelType.Key,
|
||||
sanitizeName: true,
|
||||
}),
|
||||
).toEqual('<NULL>');
|
||||
).toEqual(['<NULL>', '1.23k', '12.34%']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -234,7 +234,7 @@ test('formatForecastTooltipSeries should apply format to value', () => {
|
||||
observation: 10.1,
|
||||
formatter,
|
||||
}),
|
||||
).toEqual('<img>abc: 10');
|
||||
).toEqual(['<img>abc', '10']);
|
||||
});
|
||||
|
||||
test('formatForecastTooltipSeries should show falsy value', () => {
|
||||
@@ -245,7 +245,7 @@ test('formatForecastTooltipSeries should show falsy value', () => {
|
||||
observation: 0,
|
||||
formatter,
|
||||
}),
|
||||
).toEqual('<img>abc: 0');
|
||||
).toEqual(['<img>abc', '0']);
|
||||
});
|
||||
|
||||
test('formatForecastTooltipSeries should format full forecast', () => {
|
||||
@@ -259,7 +259,7 @@ test('formatForecastTooltipSeries should format full forecast', () => {
|
||||
forecastUpper: 7.1,
|
||||
formatter,
|
||||
}),
|
||||
).toEqual('<img>qwerty: 10, ŷ = 20 (5, 12)');
|
||||
).toEqual(['<img>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('<img>qwerty: ŷ = 20 (5, 12)');
|
||||
).toEqual(['<img>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('<img>qwerty: 10 (6, 13)');
|
||||
).toEqual(['<img>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('<img>qwerty: (7, 15)');
|
||||
).toEqual(['<img>qwerty', '(7, 15)']);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user