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

msyavuz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new bbb2279644 fix(theming): Superset theme configurations correctly 
applying to charts (#34218)
bbb2279644 is described below

commit bbb227964430ea0b2a01bbe29787d338cfbc9cca
Author: Gabriel Torres Ruiz <[email protected]>
AuthorDate: Fri Jul 18 10:25:48 2025 -0300

    fix(theming): Superset theme configurations correctly applying to charts 
(#34218)
---
 superset-frontend/src/theme/ThemeController.ts     | 61 +++++++++-------------
 .../src/theme/tests/ThemeController.test.ts        | 12 ++---
 2 files changed, 31 insertions(+), 42 deletions(-)

diff --git a/superset-frontend/src/theme/ThemeController.ts 
b/superset-frontend/src/theme/ThemeController.ts
index d3672092a3..b5c327393b 100644
--- a/superset-frontend/src/theme/ThemeController.ts
+++ b/superset-frontend/src/theme/ThemeController.ts
@@ -101,13 +101,14 @@ export class ThemeController {
     const {
       storage = new LocalStorageAdapter(),
       modeStorageKey = STORAGE_KEYS.THEME_MODE,
-      themeObject: fallbackThemeObject = supersetThemeObject,
+      themeObject = supersetThemeObject,
       defaultTheme = (supersetThemeObject.theme as AnyThemeConfig) ?? {},
       onChange = null,
     } = options;
 
     this.storage = storage;
     this.modeStorageKey = modeStorageKey;
+    this.themeObject = themeObject;
 
     // Initialize bootstrap data and themes
     const {
@@ -139,16 +140,14 @@ export class ThemeController {
 
     // Initialize theme and mode
     this.currentMode = this.determineInitialMode();
-    const { theme, themeObject } =
-      this.createInitialThemeObject(fallbackThemeObject);
-
-    this.themeObject = themeObject;
+    const initialTheme =
+      this.getThemeForMode(this.currentMode) || this.defaultTheme;
 
     // Setup change callback
     if (onChange) this.onChangeCallbacks.add(onChange);
 
     // Apply initial theme and persist mode
-    this.applyTheme(theme);
+    this.applyTheme(initialTheme);
     this.persistMode();
   }
 
@@ -158,7 +157,12 @@ export class ThemeController {
    * Cleans up listeners and references. Should be called when the controller 
is no longer needed.
    */
   public destroy(): void {
-    this.mediaQuery.removeEventListener('change', 
this.handleSystemThemeChange);
+    if (this.mediaQuery)
+      this.mediaQuery.removeEventListener(
+        'change',
+        this.handleSystemThemeChange,
+      );
+
     this.onChangeCallbacks.clear();
   }
 
@@ -329,8 +333,12 @@ export class ThemeController {
    * Initializes media query listeners if OS preference is allowed
    */
   private initializeMediaQueryListener(): void {
-    this.mediaQuery = window.matchMedia(MEDIA_QUERY_DARK_SCHEME);
-    this.mediaQuery.addEventListener('change', this.handleSystemThemeChange);
+    try {
+      this.mediaQuery = window.matchMedia(MEDIA_QUERY_DARK_SCHEME);
+      this.mediaQuery.addEventListener('change', this.handleSystemThemeChange);
+    } catch (error) {
+      console.warn('Failed to initialize media query listener:', error);
+    }
   }
 
   /**
@@ -347,9 +355,9 @@ export class ThemeController {
       settings: themeSettings,
     } = theme;
 
-    const hasValidDefault: boolean = this.hasKeys(defaultTheme);
-    const hasValidDark: boolean = this.hasKeys(darkTheme);
-    const hasValidSettings: boolean = this.hasKeys(themeSettings);
+    const hasValidDefault: boolean = this.isNonEmptyObject(defaultTheme);
+    const hasValidDark: boolean = this.isNonEmptyObject(darkTheme);
+    const hasValidSettings: boolean = this.isNonEmptyObject(themeSettings);
 
     return {
       bootstrapDefaultTheme: hasValidDefault ? defaultTheme : null,
@@ -360,9 +368,11 @@ export class ThemeController {
   }
 
   /**
-   * Checks if an object has keys (not empty).
+   * Checks if an object is non-empty (has at least one property).
    */
-  private hasKeys(obj: Record<string, any> | undefined | null): boolean {
+  private isNonEmptyObject(
+    obj: Record<string, any> | undefined | null,
+  ): boolean {
     return Boolean(
       obj && typeof obj === 'object' && Object.keys(obj).length > 0,
     );
@@ -417,28 +427,7 @@ export class ThemeController {
         ? this.darkTheme || this.defaultTheme
         : this.defaultTheme;
 
-    return normalizeThemeConfig(selectedTheme);
-  }
-
-  /**
-   * Creates the initial theme object.
-   * This sets the theme based on the current mode and ensures it has the 
correct algorithm.
-   * @param defaultThemeObject - The fallback theme object to use if no theme 
is set
-   * @returns An object containing the theme and the themeObject
-   */
-  private createInitialThemeObject(defaultThemeObject: Theme): {
-    theme: AnyThemeConfig;
-    themeObject: Theme;
-  } {
-    let theme: AnyThemeConfig | null = this.getThemeForMode(this.currentMode);
-    theme = theme || (defaultThemeObject.theme as AnyThemeConfig);
-
-    const normalizedTheme = this.normalizeTheme(theme);
-
-    return {
-      theme: normalizedTheme,
-      themeObject: Theme.fromConfig(normalizedTheme),
-    };
+    return selectedTheme;
   }
 
   /**
diff --git a/superset-frontend/src/theme/tests/ThemeController.test.ts 
b/superset-frontend/src/theme/tests/ThemeController.test.ts
index 66ff00bd27..cc242e849d 100644
--- a/superset-frontend/src/theme/tests/ThemeController.test.ts
+++ b/superset-frontend/src/theme/tests/ThemeController.test.ts
@@ -230,13 +230,13 @@ describe('ThemeController', () => {
     expect(controller.getTheme()).toBe(mockThemeObject);
   });
 
-  it('should use BootsrapData themes when available', () => {
+  it('should use BootstrapData themes when available', () => {
     controller = new ThemeController({
       themeObject: mockThemeObject,
     });
 
-    expect(mockThemeFromConfig).toHaveBeenCalledTimes(1);
-    expect(mockThemeFromConfig).toHaveBeenCalledWith(
+    expect(mockSetConfig).toHaveBeenCalledTimes(1);
+    expect(mockSetConfig).toHaveBeenCalledWith(
       expect.objectContaining({
         token: expect.objectContaining({
           colorBgBase: '#ededed',
@@ -246,7 +246,7 @@ describe('ThemeController', () => {
     );
   });
 
-  it('should fallback to Superset default theme when BootsrapData themes are 
empty', () => {
+  it('should fallback to Superset default theme when BootstrapData themes are 
empty', () => {
     mockGetBootstrapData.mockReturnValue(
       createMockBootstrapData({
         default: {},
@@ -267,8 +267,8 @@ describe('ThemeController', () => {
       defaultTheme: fallbackTheme,
     });
 
-    expect(mockThemeFromConfig).toHaveBeenCalledTimes(1);
-    expect(mockThemeFromConfig).toHaveBeenCalledWith(
+    expect(mockSetConfig).toHaveBeenCalledTimes(1);
+    expect(mockSetConfig).toHaveBeenCalledWith(
       expect.objectContaining({
         ...fallbackTheme,
         algorithm: antdThemeImport.defaultAlgorithm,

Reply via email to