Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions packages/base/cypress/specs/Boot.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,50 @@ describe("Framework boot", () => {
.invoke("hasStyle", "data-ui5-theme-properties", "@ui5/webcomponents-theming")
.should("be.true");
});

it("Tests theme loading, when registered after boot + theme props are registered for the base theme of an external theme", () => {
const baseTheme = "sap_horizon"
const customTheme = "my_custom_theme";
const customThemeStyles = `.sapThemeMetaData-Base-baseLib{background-image: url('data:text/plain;utf-8,{"Path": "Base.baseLib.${customTheme}.css_variables", "PathPattern": "/%frameworkId%/%libId%/%themeId%/%fileId%.css", "Extends": ["${baseTheme}"], "Tags": ["Fiori_3","LightColorScheme"], "FallbackThemeId": "${baseTheme}", "Engine": {"Name": "theming-engine", "Version": "16.1.10"}, "Version": { "Build": "11.32.2.20251030081249", "Source": "11.32.2"}}');}`;
const dataPropAttr = `data-ui5-component-properties-0`;

// Append meta style and config script to the head setting the custom theme
cy.window()
.then($el => {
const style = document.createElement("style");
style.innerHTML = customThemeStyles;
return $el.document.head.append(style);
})
.then($el => {
const scriptElement = document.createElement("script");
scriptElement.type = "application/json";
scriptElement.setAttribute("data-ui5-config", "true");
scriptElement.innerHTML = JSON.stringify({
"theme": customTheme,
});
return $el.document.head.append(scriptElement);
});


// Trigger boot
cy.wrap({ boot })
.invoke("boot");

// After boot, register theme properties loader for the base theme
cy.wrap({ registerThemePropertiesLoader })
.invoke("registerThemePropertiesLoader", "@ui5/webcomponents-test", baseTheme, () => {
return Promise.resolve(`:root{--_ui5_internal_var: #ccc }`);
});

// Assert - theme properties for the base theme are loaded
cy.document().should(($doc) => {
const adoptedStyleSheets = $doc.adoptedStyleSheets;
const sheet = adoptedStyleSheets.find(sh => (sh as any)._ui5StyleId === `${dataPropAttr}|@ui5/webcomponents-test`);

expect(sheet, "stylesheet should exist").to.exist;
expect(sheet!.cssRules, "cssRules should exist").to.exist;
expect(sheet!.cssRules.length, "cssRules should have at least one rule").to.be.greaterThan(0);
expect(sheet!.cssRules[0].cssText, "the custom variable should exist").to.include("--_ui5_internal_var: #ccc");
});
});
});
13 changes: 10 additions & 3 deletions packages/base/src/Boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import EventProvider from "./EventProvider.js";
import insertFontFace from "./FontFace.js";
import insertSystemCSSVars from "./SystemCSSVars.js";
import insertScrollbarStyles from "./ScrollbarStyles.js";
import { getTheme } from "./config/Theme.js";
import { getTheme, getBaseTheme } from "./config/Theme.js";
import applyTheme from "./theming/applyTheme.js";
import { registerCurrentRuntime } from "./Runtimes.js";
import { getFeature } from "./FeaturesRegistry.js";
Expand Down Expand Up @@ -88,8 +88,15 @@ const boot = async (): Promise<void> => {
* @param { string } theme
*/
const onThemeRegistered = (theme: string) => {
if (booted && theme === getTheme()) { // getTheme should only be called if "booted" is true
applyTheme(getTheme());
if (!booted) {
return;
}

const currentTheme = getTheme();
const currentBaseTheme = getBaseTheme();

if (theme === currentTheme || theme === currentBaseTheme) {
applyTheme(currentTheme);
}
};

Expand Down
21 changes: 21 additions & 0 deletions packages/base/src/config/Theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { boot, isBooted } from "../Boot.js";
import { attachConfigurationReset } from "./ConfigurationReset.js";

let curTheme: string | undefined;
let curBaseTheme: string | undefined;

attachConfigurationReset(() => {
curTheme = undefined;
Expand Down Expand Up @@ -91,11 +92,31 @@ const isLegacyThemeFamilyAsync = async () => {

const isKnownTheme = (theme: string) => SUPPORTED_THEMES.includes(theme);

/**
* Returns the base theme of external theme.
* @private
* @returns {string | undefined} the base theme name
*/
const getBaseTheme = (): string | undefined => {
return curBaseTheme;
};

/**
* Sets the base theme of the current external theme.
* @param { string | undefined } theme the name of the new base theme
* @private
*/
const setBaseTheme = (theme: string | undefined): void => {
curBaseTheme = theme;
};

export {
getTheme,
setTheme,
isTheme,
isLegacyThemeFamily,
isLegacyThemeFamilyAsync,
getDefaultTheme,
getBaseTheme,
setBaseTheme,
};
9 changes: 7 additions & 2 deletions packages/base/src/theming/applyTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import getThemeDesignerTheme from "./getThemeDesignerTheme.js";
import { fireThemeLoaded } from "./ThemeLoaded.js";
import { getFeature } from "../FeaturesRegistry.js";
import { attachCustomThemeStylesToHead, getThemeRoot } from "../config/ThemeRoot.js";
import { setBaseTheme } from "../config/Theme.js";
import type OpenUI5Support from "../features/OpenUI5Support.js";
import { DEFAULT_THEME } from "../generated/AssetParameters.js";
import { getCurrentRuntimeIndex } from "../Runtimes.js";
Expand Down Expand Up @@ -87,9 +88,13 @@ const applyTheme = async (theme: string) => {
}

// Always load component packages properties. For non-registered themes, try with the base theme, if any
const packagesTheme = isThemeRegistered(theme) ? theme : extTheme && extTheme.baseThemeName;
await loadComponentPackages(packagesTheme || DEFAULT_THEME, extTheme && extTheme.themeName === theme ? theme : undefined);
const externalThemeName = extTheme && extTheme.themeName === theme ? theme : undefined;
const baseThemeName = extTheme && extTheme.baseThemeName;
const effectiveThemeName = isThemeRegistered(theme) ? theme : (baseThemeName || DEFAULT_THEME);

await loadComponentPackages(effectiveThemeName, externalThemeName);

setBaseTheme(baseThemeName);
fireThemeLoaded(theme);
};

Expand Down
Loading