From a928f8cd9e2e8461a3d3d8f650c1eb00c8cc04cd Mon Sep 17 00:00:00 2001 From: Fardin Mustaque <105560328+fardin-developer@users.noreply.github.com> Date: Wed, 7 May 2025 20:26:02 +0530 Subject: [PATCH] feat: add metric name for big number chart types #33013 (#33099) Co-authored-by: Fardin Mustaque <fardinmustaque@Fardins-Mac-mini.local> --- .../BigNumberPeriodOverPeriod/PopKPI.tsx | 23 ++- .../BigNumberPeriodOverPeriod/controlPanel.ts | 4 + .../transformProps.ts | 18 +- .../BigNumberPeriodOverPeriod/types.ts | 2 + .../BigNumberPeriodOverPeriod/utils.ts | 25 ++- .../BigNumber/BigNumberTotal/controlPanel.ts | 4 + .../BigNumberTotal/transformProps.test.ts | 1 + .../BigNumberTotal/transformProps.ts | 12 +- .../src/BigNumber/BigNumberViz.tsx | 179 ++++++++++++++++-- .../BigNumberWithTrendline/controlPanel.tsx | 4 + .../transformProps.test.ts | 1 + .../BigNumberWithTrendline/transformProps.ts | 9 +- .../src/BigNumber/sharedControls.ts | 176 ++++++++--------- .../src/BigNumber/types.ts | 4 + .../src/BigNumber/utils.ts | 44 +++++ 15 files changed, 382 insertions(+), 124 deletions(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/PopKPI.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/PopKPI.tsx index 854d224f20..6037fd6b06 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/PopKPI.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/PopKPI.tsx @@ -36,13 +36,25 @@ import { } from './types'; import { useOverflowDetection } from './useOverflowDetection'; +const MetricNameText = styled.div<{ metricNameFontSize?: number }>` + ${({ theme, metricNameFontSize }) => ` + font-family: ${theme.typography.families.sansSerif}; + font-weight: ${theme.typography.weights.normal}; + font-size: ${metricNameFontSize || theme.typography.sizes.s * 2}px; + text-align: center; + margin-bottom: ${theme.gridUnit * 3}px; + `} +`; + const NumbersContainer = styled.div` display: flex; justify-content: center; align-items: center; flex-direction: column; width: 100%; + height: 100%; overflow: auto; + padding: 12px; `; const ComparisonValue = styled.div<PopKPIComparisonValueStyleProps>` @@ -73,6 +85,8 @@ export default function PopKPI(props: PopKPIProps) { prevNumber, valueDifference, percentDifferenceFormattedString, + metricName, + metricNameFontSize, headerFontSize, subheaderFontSize, comparisonColorEnabled, @@ -84,8 +98,8 @@ export default function PopKPI(props: PopKPIProps) { subtitle, subtitleFontSize, dashboardTimeRange, + showMetricName, } = props; - const [comparisonRange, setComparisonRange] = useState<string>(''); useEffect(() => { @@ -260,9 +274,16 @@ export default function PopKPI(props: PopKPIProps) { width: fit-content; margin: auto; align-items: flex-start; + overflow: auto; ` } > + {showMetricName && metricName && ( + <MetricNameText metricNameFontSize={metricNameFontSize}> + {metricName} + </MetricNameText> + )} + <div css={bigValueContainerStyles}> {bigNumber} {percentDifferenceNumber !== 0 && ( diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/controlPanel.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/controlPanel.ts index 63c126216b..57ac9dfdd8 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/controlPanel.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/controlPanel.ts @@ -28,6 +28,8 @@ import { subheaderFontSize, subtitleControl, subtitleFontSize, + showMetricNameControl, + metricNameFontSizeWithVisibility, } from '../sharedControls'; import { ColorSchemeEnum } from './types'; @@ -70,6 +72,8 @@ const config: ControlPanelConfig = { ], [subtitleControl], [subtitleFontSize], + [showMetricNameControl], + [metricNameFontSizeWithVisibility], [ { ...subheaderFontSize, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/transformProps.ts index a40f18b24c..70189f70fa 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/transformProps.ts @@ -26,7 +26,13 @@ import { SimpleAdhocFilter, ensureIsArray, } from '@superset-ui/core'; -import { getComparisonFontSize, getHeaderFontSize } from './utils'; +import { + getComparisonFontSize, + getHeaderFontSize, + getMetricNameFontSize, +} from './utils'; + +import { getOriginalLabel } from '../utils'; dayjs.extend(utc); @@ -83,6 +89,7 @@ export default function transformProps(chartProps: ChartProps) { headerFontSize, headerText, metric, + metricNameFontSize, yAxisFormat, currencyFormat, subheaderFontSize, @@ -91,11 +98,14 @@ export default function transformProps(chartProps: ChartProps) { percentDifferenceFormat, subtitle = '', subtitleFontSize, - columnConfig, + columnConfig = {}, } = formData; const { data: dataA = [] } = queriesData[0]; const data = dataA; const metricName = metric ? getMetricLabel(metric) : ''; + const metrics = chartProps.datasource?.metrics || []; + const originalLabel = getOriginalLabel(metric, metrics); + const showMetricName = chartProps.rawFormData?.show_metric_name ?? false; const timeComparison = ensureIsArray(chartProps.rawFormData?.time_compare)[0]; const startDateOffset = chartProps.rawFormData?.start_date_offset; const currentTimeRangeFilter = chartProps.rawFormData?.adhoc_filters?.filter( @@ -179,7 +189,7 @@ export default function transformProps(chartProps: ChartProps) { width, height, data, - metricName, + metricName: originalLabel, bigNumber, prevNumber, valueDifference, @@ -187,6 +197,8 @@ export default function transformProps(chartProps: ChartProps) { boldText, subtitle, subtitleFontSize, + showMetricName, + metricNameFontSize: getMetricNameFontSize(metricNameFontSize), headerFontSize: getHeaderFontSize(headerFontSize), subheaderFontSize: getComparisonFontSize(subheaderFontSize), headerText, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/types.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/types.ts index bd12d1a154..f4fa903e85 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/types.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/types.ts @@ -61,6 +61,8 @@ export type PopKPIProps = PopKPIStylesProps & data: TimeseriesDataRecord[]; metrics: Metric[]; metricName: string; + metricNameFontSize?: number; + showMetricName: boolean; bigNumber: string; prevNumber: string; subtitle?: string; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/utils.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/utils.ts index 4fb45ab88a..9b65859329 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/utils.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/utils.ts @@ -16,10 +16,19 @@ * specific language governing permissions and limitations * under the License. */ -import { headerFontSize, subheaderFontSize } from '../sharedControls'; +import { + headerFontSize, + subheaderFontSize, + metricNameFontSize, +} from '../sharedControls'; const headerFontSizes = [16, 20, 30, 48, 60]; -const comparisonFontSizes = [16, 20, 26, 32, 40]; +const sharedFontSizes = [16, 20, 26, 32, 40]; + +const metricNameProportionValues = + metricNameFontSize.config.options.map( + (option: { label: string; value: number }) => option.value, + ) ?? []; const headerProportionValues = headerFontSize.config.options.map( @@ -40,6 +49,10 @@ const getFontSizeMapping = ( return acc; }, {}); +const metricNameFontSizesMapping = getFontSizeMapping( + metricNameProportionValues, + sharedFontSizes, +); const headerFontSizesMapping = getFontSizeMapping( headerProportionValues, headerFontSizes, @@ -47,13 +60,17 @@ const headerFontSizesMapping = getFontSizeMapping( const comparisonFontSizesMapping = getFontSizeMapping( subheaderProportionValues, - comparisonFontSizes, + sharedFontSizes, ); +export const getMetricNameFontSize = (proportionValue: number) => + metricNameFontSizesMapping[proportionValue] ?? + sharedFontSizes[sharedFontSizes.length - 1]; + export const getHeaderFontSize = (proportionValue: number) => headerFontSizesMapping[proportionValue] ?? headerFontSizes[headerFontSizes.length - 1]; export const getComparisonFontSize = (proportionValue: number) => comparisonFontSizesMapping[proportionValue] ?? - comparisonFontSizes[comparisonFontSizes.length - 1]; + sharedFontSizes[sharedFontSizes.length - 1]; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts index f9b53ccaac..33a2ba89df 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts @@ -28,6 +28,8 @@ import { headerFontSize, subtitleFontSize, subtitleControl, + showMetricNameControl, + metricNameFontSizeWithVisibility, } from '../sharedControls'; export default { @@ -44,6 +46,8 @@ export default { [headerFontSize], [subtitleControl], [subtitleFontSize], + [showMetricNameControl], + [metricNameFontSizeWithVisibility], ['y_axis_format'], ['currency_format'], [ diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.test.ts index e34e352683..aea95effeb 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.test.ts @@ -36,6 +36,7 @@ jest.mock('@superset-ui/core', () => ({ jest.mock('../utils', () => ({ getDateFormatter: jest.fn(() => (v: any) => `${v}pm`), parseMetricValue: jest.fn(val => Number(val)), + getOriginalLabel: jest.fn((metric, metrics) => metric), })); describe('BigNumberTotal transformProps', () => { diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.ts index c0f9b2baeb..fabac86ef0 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.ts @@ -29,7 +29,7 @@ import { getValueFormatter, } from '@superset-ui/core'; import { BigNumberTotalChartProps, BigNumberVizProps } from '../types'; -import { getDateFormatter, parseMetricValue } from '../utils'; +import { getDateFormatter, getOriginalLabel, parseMetricValue } from '../utils'; import { Refs } from '../../types'; export default function transformProps( @@ -45,6 +45,7 @@ export default function transformProps( datasource: { currencyFormats = {}, columnFormats = {} }, } = chartProps; const { + metricNameFontSize, headerFontSize, metric = 'value', subtitle, @@ -58,9 +59,12 @@ export default function transformProps( subheaderFontSize, } = formData; const refs: Refs = {}; - const { data = [], coltypes = [] } = queriesData[0]; + const { data = [], coltypes = [] } = queriesData[0] || {}; const granularity = extractTimegrain(rawFormData as QueryFormData); + const metrics = chartProps.datasource?.metrics || []; + const originalLabel = getOriginalLabel(metric, metrics); const metricName = getMetricLabel(metric); + const showMetricName = chartProps.rawFormData?.show_metric_name ?? false; const formattedSubtitle = subtitle?.trim() ? subtitle : subheader || ''; const formattedSubtitleFontSize = subtitle?.trim() ? (subtitleFontSize ?? 1) @@ -103,7 +107,6 @@ export default function transformProps( const colorThresholdFormatters = getColorFormatters(conditionalFormatting, data, false) ?? defaultColorFormatters; - return { width, height, @@ -116,5 +119,8 @@ export default function transformProps( onContextMenu, refs, colorThresholdFormatters, + metricName: originalLabel, + showMetricName, + metricNameFontSize, }; } diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx index 79876f2d6c..e422ec6402 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { PureComponent, MouseEvent } from 'react'; +import { PureComponent, MouseEvent, createRef } from 'react'; import { t, getNumberFormatter, @@ -35,6 +35,7 @@ const defaultNumberFormatter = getNumberFormatter(); const PROPORTION = { // text size: proportion of the chart container sans trendline + METRIC_NAME: 0.125, KICKER: 0.1, HEADER: 0.3, SUBHEADER: 0.125, @@ -42,13 +43,20 @@ const PROPORTION = { TRENDLINE: 0.3, }; -class BigNumberVis extends PureComponent<BigNumberVizProps> { +type BigNumberVisState = { + elementsRendered: boolean; + recalculateTrigger: boolean; +}; + +class BigNumberVis extends PureComponent<BigNumberVizProps, BigNumberVisState> { static defaultProps = { className: '', headerFormatter: defaultNumberFormatter, formatTime: getTimeFormatter(SMART_DATE_VERBOSE_ID), headerFontSize: PROPORTION.HEADER, kickerFontSize: PROPORTION.KICKER, + metricNameFontSize: PROPORTION.METRIC_NAME, + showMetricName: true, mainColor: BRAND_COLOR, showTimestamp: false, showTrendLine: false, @@ -58,6 +66,40 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> { timeRangeFixed: false, }; + // Create refs for each component to measure heights + metricNameRef = createRef<HTMLDivElement>(); + + kickerRef = createRef<HTMLDivElement>(); + + headerRef = createRef<HTMLDivElement>(); + + subheaderRef = createRef<HTMLDivElement>(); + + subtitleRef = createRef<HTMLDivElement>(); + + state = { + elementsRendered: false, + recalculateTrigger: false, + }; + + componentDidMount() { + // Wait for elements to render and then calculate heights + setTimeout(() => { + this.setState({ elementsRendered: true }); + }, 0); + } + + componentDidUpdate(prevProps: BigNumberVizProps) { + if ( + prevProps.height !== this.props.height || + prevProps.showTrendLine !== this.props.showTrendLine + ) { + this.setState(prevState => ({ + recalculateTrigger: !prevState.recalculateTrigger, + })); + } + } + getClassName() { const { className, showTrendLine, bigNumberFallback } = this.props; const names = `superset-legacy-chart-big-number ${className} ${ @@ -92,6 +134,37 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> { ); } + renderMetricName(maxHeight: number) { + const { metricName, width, showMetricName } = this.props; + if (!showMetricName || !metricName) return null; + + const text = metricName; + + const container = this.createTemporaryContainer(); + document.body.append(container); + const fontSize = computeMaxFontSize({ + text, + maxWidth: width, + maxHeight, + className: 'metric-name', + container, + }); + container.remove(); + + return ( + <div + ref={this.metricNameRef} + className="metric-name" + style={{ + fontSize, + height: 'auto', + }} + > + {text} + </div> + ); + } + renderKicker(maxHeight: number) { const { timestamp, showTimestamp, formatTime, width } = this.props; if ( @@ -118,6 +191,7 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> { return ( <div + ref={this.kickerRef} className="kicker" style={{ fontSize, @@ -173,6 +247,7 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> { return ( <div + ref={this.headerRef} className="header-line" style={{ display: 'flex', @@ -211,6 +286,7 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> { return ( <div + ref={this.subheaderRef} className="subheader-line" style={{ fontSize, @@ -256,6 +332,7 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> { return ( <> <div + ref={this.subtitleRef} className="subtitle-line subheader-line" style={{ fontSize: `${fontSize}px`, @@ -316,6 +393,35 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> { ); } + getTotalElementsHeight() { + const marginPerElement = 8; // theme.gridUnit = 4, so margin-bottom = 8px + + const refs = [ + this.metricNameRef, + this.kickerRef, + this.headerRef, + this.subheaderRef, + this.subtitleRef, + ]; + + // Filter refs to only those with a current element + const visibleRefs = refs.filter(ref => ref.current); + + const totalHeight = visibleRefs.reduce((sum, ref, index) => { + const height = ref.current?.offsetHeight || 0; + const margin = index < visibleRefs.length - 1 ? marginPerElement : 0; + return sum + height + margin; + }, 0); + + return totalHeight; + } + + shouldApplyOverflow(availableHeight: number) { + if (!this.state.elementsRendered) return false; + const totalHeight = this.getTotalElementsHeight(); + return totalHeight > availableHeight; + } + render() { const { showTrendLine, @@ -323,6 +429,7 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> { kickerFontSize, headerFontSize, subtitleFontSize, + metricNameFontSize, subheaderFontSize, } = this.props; const className = this.getClassName(); @@ -330,11 +437,31 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> { if (showTrendLine) { const chartHeight = Math.floor(PROPORTION.TRENDLINE * height); const allTextHeight = height - chartHeight; + const shouldApplyOverflow = this.shouldApplyOverflow(allTextHeight); return ( <div className={className}> - <div className="text-container" style={{ height: allTextHeight }}> + <div + className="text-container" + style={{ + height: allTextHeight, + ...(shouldApplyOverflow + ? { + display: 'block', + boxSizing: 'border-box', + overflowX: 'hidden', + overflowY: 'auto', + width: '100%', + } + : {}), + }} + > {this.renderFallbackWarning()} + {this.renderMetricName( + Math.ceil( + (metricNameFontSize || 0) * (1 - PROPORTION.TRENDLINE) * height, + ), + )} {this.renderKicker( Math.ceil( (kickerFontSize || 0) * (1 - PROPORTION.TRENDLINE) * height, @@ -356,16 +483,33 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> { </div> ); } - + const shouldApplyOverflow = this.shouldApplyOverflow(height); return ( - <div className={className} style={{ height }}> - {this.renderFallbackWarning()} - {this.renderKicker((kickerFontSize || 0) * height)} - {this.renderHeader(Math.ceil(headerFontSize * height))} - {this.rendermetricComparisonSummary( - Math.ceil(subheaderFontSize * height), - )} - {this.renderSubtitle(Math.ceil(subtitleFontSize * height))} + <div + className={className} + style={{ + height, + ...(shouldApplyOverflow + ? { + display: 'block', + boxSizing: 'border-box', + overflowX: 'hidden', + overflowY: 'auto', + width: '100%', + } + : {}), + }} + > + <div className="text-container"> + {this.renderFallbackWarning()} + {this.renderMetricName((metricNameFontSize || 0) * height)} + {this.renderKicker((kickerFontSize || 0) * height)} + {this.renderHeader(Math.ceil(headerFontSize * height))} + {this.rendermetricComparisonSummary( + Math.ceil(subheaderFontSize * height), + )} + {this.renderSubtitle(Math.ceil(subtitleFontSize * height))} + </div> </div> ); } @@ -400,7 +544,12 @@ export default styled(BigNumberVis)` .kicker { line-height: 1em; - padding-bottom: 2em; + margin-bottom: ${theme.gridUnit * 2}px; + } + + .metric-name { + line-height: 1em; + margin-bottom: ${theme.gridUnit * 2}px; } .header-line { @@ -416,12 +565,12 @@ export default styled(BigNumberVis)` .subheader-line { line-height: 1em; - padding-bottom: 0; + margin-bottom: ${theme.gridUnit * 2}px; } .subtitle-line { line-height: 1em; - padding-bottom: 0; + margin-bottom: ${theme.gridUnit * 2}px; } &.is-fallback-value { diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx index 7f04a2efeb..153c76e421 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx @@ -31,6 +31,8 @@ import { subheaderFontSize, subtitleFontSize, subtitleControl, + showMetricNameControl, + metricNameFontSizeWithVisibility, } from '../sharedControls'; const config: ControlPanelConfig = { @@ -141,6 +143,8 @@ const config: ControlPanelConfig = { [subheaderFontSize], [subtitleControl], [subtitleFontSize], + [showMetricNameControl], + [metricNameFontSizeWithVisibility], ['y_axis_format'], ['currency_format'], [ diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.test.ts index 069b25722d..c07a8b9477 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.test.ts @@ -39,6 +39,7 @@ jest.mock('@superset-ui/core', () => ({ jest.mock('../utils', () => ({ getDateFormatter: jest.fn(() => (v: any) => `${v}pm`), parseMetricValue: jest.fn(val => Number(val)), + getOriginalLabel: jest.fn((metric, metrics) => metric), })); jest.mock('../../utils/tooltip', () => ({ 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 3d933208fd..79c5b992b3 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 @@ -35,7 +35,7 @@ import { BigNumberWithTrendlineChartProps, TimeSeriesDatum, } from '../types'; -import { getDateFormatter, parseMetricValue } from '../utils'; +import { getDateFormatter, parseMetricValue, getOriginalLabel } from '../utils'; import { getDefaultTooltip } from '../../utils/tooltip'; import { Refs } from '../../types'; @@ -62,6 +62,7 @@ export default function transformProps( compareLag: compareLag_, compareSuffix = '', timeFormat, + metricNameFontSize, headerFontSize, metric = 'value', showTimestamp, @@ -96,6 +97,9 @@ export default function transformProps( const aggregatedData = hasAggregatedData ? aggregatedQueryData.data[0] : null; const refs: Refs = {}; const metricName = getMetricLabel(metric); + const metrics = chartProps.datasource?.metrics || []; + const originalLabel = getOriginalLabel(metric, metrics); + const showMetricName = chartProps.rawFormData?.show_metric_name ?? false; const compareLag = Number(compareLag_) || 0; let formattedSubheader = subheader; @@ -303,6 +307,9 @@ export default function transformProps( headerFormatter, formatTime, formData, + metricName: originalLabel, + showMetricName, + metricNameFontSize, headerFontSize, subtitleFontSize, subtitle, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/sharedControls.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/sharedControls.ts index 09766ed4bf..9610497382 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/sharedControls.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/sharedControls.ts @@ -21,113 +21,95 @@ import { t } from '@superset-ui/core'; import { CustomControlItem } from '@superset-ui/chart-controls'; -export const headerFontSize: CustomControlItem = { - name: 'header_font_size', - config: { - type: 'SelectControl', - label: t('Big Number Font Size'), - renderTrigger: true, - clearable: false, - default: 0.4, - // Values represent the percentage of space a header should take - options: [ - { - label: t('Tiny'), - value: 0.2, - }, - { - label: t('Small'), - value: 0.3, - }, - { - label: t('Normal'), - value: 0.4, - }, - { - label: t('Large'), - value: 0.5, - }, - { - label: t('Huge'), - value: 0.6, - }, - ], - }, -}; +const FONT_SIZE_OPTIONS_SMALL = [ + { label: t('Tiny'), value: 0.125 }, + { label: t('Small'), value: 0.15 }, + { label: t('Normal'), value: 0.2 }, + { label: t('Large'), value: 0.3 }, + { label: t('Huge'), value: 0.4 }, +]; + +const FONT_SIZE_OPTIONS_LARGE = [ + { label: t('Tiny'), value: 0.2 }, + { label: t('Small'), value: 0.3 }, + { label: t('Normal'), value: 0.4 }, + { label: t('Large'), value: 0.5 }, + { label: t('Huge'), value: 0.6 }, +]; + +function makeFontSizeControl( + name: string, + label: string, + defaultValue: number, + options: { label: string; value: number }[], +): CustomControlItem { + return { + name, + config: { + type: 'SelectControl', + label: t(label), + renderTrigger: true, + clearable: false, + default: defaultValue, + options, + }, + }; +} -export const subtitleFontSize: CustomControlItem = { - name: 'subtitle_font_size', +export const headerFontSize = makeFontSizeControl( + 'header_font_size', + 'Big Number Font Size', + 0.4, + FONT_SIZE_OPTIONS_LARGE, +); + +export const subtitleFontSize = makeFontSizeControl( + 'subtitle_font_size', + 'Subtitle Font Size', + 0.15, + FONT_SIZE_OPTIONS_SMALL, +); + +export const subheaderFontSize = makeFontSizeControl( + 'subheader_font_size', + 'Subheader Font Size', + 0.15, + FONT_SIZE_OPTIONS_SMALL, +); + +export const metricNameFontSize = makeFontSizeControl( + 'metric_name_font_size', + 'Metric Name Font Size', + 0.15, + FONT_SIZE_OPTIONS_SMALL, +); + +export const subtitleControl: CustomControlItem = { + name: 'subtitle', config: { - type: 'SelectControl', - label: t('Subtitle Font Size'), + type: 'TextControl', + label: t('Subtitle'), renderTrigger: true, - clearable: false, - default: 0.15, - // Values represent the percentage of space a subtitle should take - options: [ - { - label: t('Tiny'), - value: 0.125, - }, - { - label: t('Small'), - value: 0.15, - }, - { - label: t('Normal'), - value: 0.2, - }, - { - label: t('Large'), - value: 0.3, - }, - { - label: t('Huge'), - value: 0.4, - }, - ], + description: t('Description text that shows up below your Big Number'), }, }; -export const subheaderFontSize: CustomControlItem = { - name: 'subheader_font_size', + +export const showMetricNameControl: CustomControlItem = { + name: 'show_metric_name', config: { - type: 'SelectControl', - label: t('Subheader Font Size'), + type: 'CheckboxControl', + label: t('Show Metric Name'), renderTrigger: true, - clearable: false, - default: 0.15, - // Values represent the percentage of space a subheader should take - options: [ - { - label: t('Tiny'), - value: 0.125, - }, - { - label: t('Small'), - value: 0.15, - }, - { - label: t('Normal'), - value: 0.2, - }, - { - label: t('Large'), - value: 0.3, - }, - { - label: t('Huge'), - value: 0.4, - }, - ], + default: false, + description: t('Whether to display the metric name'), }, }; -export const subtitleControl: CustomControlItem = { - name: 'subtitle', +export const metricNameFontSizeWithVisibility: CustomControlItem = { + ...metricNameFontSize, config: { - type: 'TextControl', - label: t('Subtitle'), - renderTrigger: true, - description: t('Description text that shows up below your Big Number'), + ...metricNameFontSize.config, + visibility: ({ controls }) => controls?.show_metric_name?.value === true, + resetOnHide: false, }, }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/types.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/types.ts index 44abef0c78..d2bec7d68e 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/types.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/types.ts @@ -75,6 +75,10 @@ export type BigNumberVizProps = { bigNumberFallback?: TimeSeriesDatum; headerFormatter: ValueFormatter | TimeFormatter; formatTime?: TimeFormatter; + metricName?: string; + friendlyMetricName?: string; + metricNameFontSize?: number; + showMetricName?: boolean; headerFontSize: number; kickerFontSize?: number; subheader?: string; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/utils.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/utils.ts index a25ec74321..ecad4d9ed9 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/utils.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/utils.ts @@ -22,6 +22,10 @@ import utc from 'dayjs/plugin/utc'; import { getTimeFormatter, getTimeFormatterForGranularity, + isAdhocMetricSimple, + isSavedMetric, + Metric, + QueryFormMetric, SMART_DATE_ID, TimeGranularity, } from '@superset-ui/core'; @@ -47,3 +51,43 @@ export const getDateFormatter = ( timeFormat === SMART_DATE_ID ? getTimeFormatterForGranularity(granularity) : getTimeFormatter(timeFormat ?? fallbackFormat); + +export function getOriginalLabel( + metric: QueryFormMetric, + metrics: Metric[] = [], +): string { + const metricLabel = typeof metric === 'string' ? metric : metric.label || ''; + + if (isSavedMetric(metric)) { + const metricEntry = metrics.find(m => m.metric_name === metric); + return ( + metricEntry?.verbose_name || + metricEntry?.metric_name || + metric || + 'Unknown Metric' + ); + } + + if (isAdhocMetricSimple(metric)) { + const column = metric.column || {}; + const columnName = column.column_name || 'unknown_column'; + const verboseName = column.verbose_name || columnName; + const aggregate = metric.aggregate || 'UNKNOWN'; + return metric.hasCustomLabel && metric.label + ? metric.label + : `${aggregate}(${verboseName})`; + } + + if ( + typeof metric === 'object' && + 'expressionType' in metric && + metric.expressionType === 'SQL' && + 'sqlExpression' in metric + ) { + return metric.hasCustomLabel && metric.label + ? metric.label + : metricLabel || 'Custom Metric'; + } + + return metricLabel || 'Unknown Metric'; +} -- GitLab