This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch feat/bignumber-alignment-control
in repository https://gitbox.apache.org/repos/asf/superset.git

commit d9b2d8dc0c0be065fc19dbdae0dbbed8ccbf2009
Author: Evan Rusackas <[email protected]>
AuthorDate: Tue Sep 22 18:59:23 2026 -0700

    feat(bignumber): add an alignment control for Big Number charts
    
    Superset 5 centered the Big Number value and subheader; the Emotion
    CSS-in-JS migration hardcoded align-items: flex-start on the shared
    BigNumberViz component, and there's been no way back since (#37674).
    
    Adds a header_alignment control (Left/Center/Right, default Left to
    match current behavior) to both BigNumberTotal and BigNumberWithTrendline,
    threaded through transformProps into BigNumberVizProps. BigNumberViz
    applies it as align-items on the outer container and text-container, and
    text-align on each text line (metric name, kicker, header, subheader,
    subtitle), so multi-line wrapped text aligns internally too.
    
    Scoped to alignment only: doesn't touch axis/grid config, context menu
    wiring, conditional formatting, or aggregation logic.
    
    Co-Authored-By: Evan Rusackas <[email protected]>
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
---
 .../BigNumber/BigNumberTotal/controlPanel.test.ts  | 10 ++++
 .../src/BigNumber/BigNumberTotal/controlPanel.ts   |  2 +
 .../BigNumberTotal/transformProps.test.ts          |  2 +
 .../src/BigNumber/BigNumberTotal/transformProps.ts |  2 +
 .../src/BigNumber/BigNumberViz.test.tsx            | 67 ++++++++++++++++++++++
 .../src/BigNumber/BigNumberViz.tsx                 | 21 ++++++-
 .../BigNumberWithTrendline/controlPanel.tsx        |  2 +
 .../BigNumberWithTrendline/transformProps.test.ts  |  2 +
 .../BigNumberWithTrendline/transformProps.ts       |  2 +
 .../src/BigNumber/sharedControls.ts                | 19 ++++++
 .../plugin-chart-echarts/src/BigNumber/types.ts    |  4 ++
 11 files changed, 130 insertions(+), 3 deletions(-)

diff --git 
a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.test.ts
 
b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.test.ts
index 7d8e74156d4..a71d3520a48 100644
--- 
a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.test.ts
+++ 
b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.test.ts
@@ -72,6 +72,16 @@ describe('BigNumber Total Control Panel Config', () => {
       row.some((control: any) => control.name === 'conditional_formatting'),
     );
     expect(conditionalFormattingRow).toBeTruthy();
+
+    // Check header_alignment control exists in one of the rows
+    const headerAlignmentRow = thirdSection.find(row =>
+      row.some((control: any) => control.name === 'header_alignment'),
+    );
+    expect(headerAlignmentRow).toBeTruthy();
+    expect(headerAlignmentRow![0]).toMatchObject({
+      name: 'header_alignment',
+      config: expect.objectContaining({ default: 'left' }),
+    });
   });
 
   test('should have y_axis_format override with correct label', () => {
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 d926f06de01..906b01e91c9 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
@@ -32,6 +32,7 @@ import {
   subtitleControl,
   showMetricNameControl,
   metricNameFontSizeWithVisibility,
+  headerAlignmentControl,
 } from '../sharedControls';
 
 export default {
@@ -124,6 +125,7 @@ export default {
             },
           },
         ],
+        [headerAlignmentControl],
       ],
     },
   ],
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 00522b9d0a8..54f5898c949 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
@@ -54,6 +54,7 @@ describe('BigNumberTotal transformProps', () => {
     yAxisFormat: 'SMART_NUMBER',
     conditionalFormatting: [{ color: 'red', op: '>', value: 0 }],
     currencyFormat: { symbol: '$', symbolPosition: 'prefix' },
+    headerAlignment: 'center',
   };
 
   const baseDatasource = {
@@ -86,6 +87,7 @@ describe('BigNumberTotal transformProps', () => {
     expect(result.subtitle).toBe(baseFormData.subheader);
     expect(result.onContextMenu).toBe(onContextMenu);
     expect(result.refs).toEqual({});
+    expect(result.headerAlignment).toBe('center');
     // headerFormatter should be set even if there's no data
     expect(typeof result.headerFormatter).toBe('function');
     // colorThresholdFormatters fallback to empty array when 
getColorFormatters returns falsy
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 3c7c78ecc70..2d7df441c31 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
@@ -63,6 +63,7 @@ export default function transformProps(
     currencyFormat,
     subheader,
     subheaderFontSize,
+    headerAlignment,
   } = formData;
   const refs: Refs = {};
   const {
@@ -143,5 +144,6 @@ export default function transformProps(
     metricName: originalLabel,
     showMetricName,
     metricNameFontSize,
+    headerAlignment,
   };
 }
diff --git 
a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.test.tsx
 
b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.test.tsx
index dd7a495ee89..5279056a7f5 100644
--- 
a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.test.tsx
+++ 
b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.test.tsx
@@ -117,3 +117,70 @@ describe('BigNumberViz context menu', () => {
     expect(ancestorHandler).not.toHaveBeenCalled();
   });
 });
+
+describe('BigNumberViz alignment', () => {
+  test('defaults to left alignment', () => {
+    const { container } = render(
+      <BigNumberVis
+        width={200}
+        height={100}
+        bigNumber={42}
+        headerFormatter={getNumberFormatter()}
+        headerFontSize={0.3}
+        subheaderFontSize={0.125}
+        subtitleFontSize={0.125}
+        subtitle=""
+        refs={{}}
+      />,
+    );
+
+    const textContainer = container.querySelector('.text-container');
+    expect(textContainer).toHaveStyle({ alignItems: 'flex-start' });
+    const headerLine = container.querySelector('.header-line');
+    expect(headerLine).toHaveStyle({ textAlign: 'left' });
+  });
+
+  test('centers content when headerAlignment is "center"', () => {
+    const { container } = render(
+      <BigNumberVis
+        width={200}
+        height={100}
+        bigNumber={42}
+        headerFormatter={getNumberFormatter()}
+        headerFontSize={0.3}
+        subheaderFontSize={0.125}
+        subtitleFontSize={0.125}
+        subtitle=""
+        refs={{}}
+        headerAlignment="center"
+      />,
+    );
+
+    const textContainer = container.querySelector('.text-container');
+    expect(textContainer).toHaveStyle({ alignItems: 'center' });
+    const headerLine = container.querySelector('.header-line');
+    expect(headerLine).toHaveStyle({ textAlign: 'center' });
+  });
+
+  test('right-aligns content when headerAlignment is "right"', () => {
+    const { container } = render(
+      <BigNumberVis
+        width={200}
+        height={100}
+        bigNumber={42}
+        headerFormatter={getNumberFormatter()}
+        headerFontSize={0.3}
+        subheaderFontSize={0.125}
+        subtitleFontSize={0.125}
+        subtitle=""
+        refs={{}}
+        headerAlignment="right"
+      />,
+    );
+
+    const textContainer = container.querySelector('.text-container');
+    expect(textContainer).toHaveStyle({ alignItems: 'flex-end' });
+    const headerLine = container.querySelector('.header-line');
+    expect(headerLine).toHaveStyle({ textAlign: 'right' });
+  });
+});
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 ea4e89cd0ee..e16e7efc149 100644
--- 
a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx
+++ 
b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx
@@ -28,12 +28,18 @@ import {
 } from '@superset-ui/core';
 import { styled, useTheme } from '@apache-superset/core/theme';
 import Echart from '../components/Echart';
-import { BigNumberVizProps } from './types';
+import { BigNumberVizProps, HeaderAlignment } from './types';
 import { PROPORTION } from './constants';
 import { EventHandlers } from '../types';
 
 const defaultNumberFormatter = getNumberFormatter();
 
+const ALIGN_ITEMS: Record<HeaderAlignment, string> = {
+  left: 'flex-start',
+  center: 'center',
+  right: 'flex-end',
+};
+
 function BigNumberVis({
   className = '',
   headerFormatter = defaultNumberFormatter,
@@ -47,9 +53,11 @@ function BigNumberVis({
   subheader = '',
   subheaderFontSize = PROPORTION.SUBHEADER,
   subtitleFontSize = PROPORTION.SUBHEADER,
+  headerAlignment = 'left',
   ...props
 }: BigNumberVizProps) {
   const theme = useTheme();
+  const alignItems = ALIGN_ITEMS[headerAlignment];
 
   // Convert state to hooks
   const [elementsRendered, setElementsRendered] = useState(false);
@@ -132,6 +140,7 @@ function BigNumberVis({
         style={{
           fontSize,
           height: 'auto',
+          textAlign: headerAlignment,
         }}
       >
         {text}
@@ -170,6 +179,7 @@ function BigNumberVis({
         style={{
           fontSize,
           height: 'auto',
+          textAlign: headerAlignment,
         }}
       >
         {text}
@@ -239,6 +249,7 @@ function BigNumberVis({
           fontSize,
           height: 'auto',
           color: numberColor,
+          textAlign: headerAlignment,
         }}
         onContextMenu={handleContextMenu}
       >
@@ -275,6 +286,7 @@ function BigNumberVis({
           style={{
             fontSize,
             height: maxHeight,
+            textAlign: headerAlignment,
           }}
         >
           {text}
@@ -321,6 +333,7 @@ function BigNumberVis({
             style={{
               fontSize: `${fontSize}px`,
               height: maxHeight,
+              textAlign: headerAlignment,
             }}
           >
             {text}
@@ -425,11 +438,12 @@ function BigNumberVis({
     const overflow = shouldApplyOverflow(allTextHeight);
 
     return (
-      <div className={componentClassName}>
+      <div className={componentClassName} style={{ alignItems }}>
         <div
           className="text-container"
           style={{
             height: allTextHeight,
+            alignItems,
             ...(overflow
               ? {
                   display: 'block',
@@ -472,6 +486,7 @@ function BigNumberVis({
       className={componentClassName}
       style={{
         height,
+        alignItems,
         ...(overflow
           ? {
               display: 'block',
@@ -483,7 +498,7 @@ function BigNumberVis({
           : {}),
       }}
     >
-      <div className="text-container">
+      <div className="text-container" style={{ alignItems }}>
         {renderFallbackWarning()}
         {renderMetricName((metricNameFontSize || 0) * height)}
         {renderKicker((kickerFontSize || 0) * height)}
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 d9d5b85a1cc..762fe6ad94c 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
@@ -35,6 +35,7 @@ import {
   subtitleControl,
   showMetricNameControl,
   metricNameFontSizeWithVisibility,
+  headerAlignmentControl,
 } from '../sharedControls';
 
 const config: ControlPanelConfig = {
@@ -235,6 +236,7 @@ const config: ControlPanelConfig = {
             },
           },
         ],
+        [headerAlignmentControl],
       ],
     },
     {
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 6a9752d57b9..8e7816994a9 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
@@ -113,6 +113,7 @@ describe('BigNumberWithTrendline transformProps', () => {
     compareSuffix: 'WoW',
     colorPicker: { r: 0, g: 0, b: 0 },
     currencyFormat: { symbol: '$', symbolPosition: 'prefix' },
+    headerAlignment: 'right',
   };
 
   const baseDatasource = {
@@ -141,6 +142,7 @@ describe('BigNumberWithTrendline transformProps', () => {
     );
     expect(result.bigNumber).toBeNull();
     expect(result.subtitle).toBe('subtitle message');
+    expect(result.headerAlignment).toBe('right');
   });
 
   test('should calculate subheader as percent change with suffix', () => {
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 fa57d2a8b71..9b4a4640e74 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
@@ -114,6 +114,7 @@ export default function transformProps(
     showXAxisMinMaxLabels = false,
     showYAxis = false,
     showYAxisMinMaxLabels = false,
+    headerAlignment,
   } = formData;
   const granularity = extractTimegrain(rawFormData);
   const {
@@ -416,5 +417,6 @@ export default function transformProps(
     onContextMenu,
     xValueFormatter: formatTime,
     refs,
+    headerAlignment,
   };
 }
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 5c7647fa576..44c2f1d7267 100644
--- 
a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/sharedControls.ts
+++ 
b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/sharedControls.ts
@@ -113,3 +113,22 @@ export const metricNameFontSizeWithVisibility: 
CustomControlItem = {
     resetOnHide: false,
   },
 };
+
+export const headerAlignmentControl: CustomControlItem = {
+  name: 'header_alignment',
+  config: {
+    type: 'SelectControl',
+    label: t('Alignment'),
+    renderTrigger: true,
+    clearable: false,
+    default: 'left',
+    options: [
+      { label: t('Left'), value: 'left' },
+      { label: t('Center'), value: 'center' },
+      { label: t('Right'), value: 'right' },
+    ],
+    description: t(
+      'Horizontal alignment of the metric value and supporting text',
+    ),
+  },
+};
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 d466a1781c8..d069b79219e 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/types.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/types.ts
@@ -34,10 +34,13 @@ export interface BigNumberDatum {
   [key: string]: number | string | null;
 }
 
+export type HeaderAlignment = 'left' | 'center' | 'right';
+
 export type BigNumberTotalFormData = QueryFormData & {
   metric?: QueryFormMetric;
   yAxisFormat?: string;
   forceTimestampFormatting?: boolean;
+  headerAlignment?: HeaderAlignment;
 };
 
 export type BigNumberWithTrendlineFormData = BigNumberTotalFormData & {
@@ -107,4 +110,5 @@ export type BigNumberVizProps = {
   formData?: BigNumberWithTrendlineFormData;
   refs: Refs;
   colorThresholdFormatters?: ColorFormatters;
+  headerAlignment?: HeaderAlignment;
 };

Reply via email to