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