Skip to content

Commit

Permalink
[FEATURE] Theming: Support for default theme and fallback themes
Browse files Browse the repository at this point in the history
Adds support for a default theme and calculates a valid fallback theme.

Two main scenarios are tackled:

1. No "theme" configuration is set
Previously, the default theme was set to "base".
In case no theme configuration is given, this results in a broken UI
with just the bare minimum of visual support.
With this change we establish a predetermined default theme, set to the
latest available supported theme.

2. An invalid standard theme is set
Invalid standard themes are:
  a) themes that have been deprecated and removed
  b) themes that are not yet supported in the current version

For this main branch change, the latest default theme is "sap_horizon".

JIRA: CPOUI5FRAMEWORK-112
JIRA: CPOUI5FRAMEWORK-500
Change-Id: I301b79539221b4b0db7b9b1fd3f4bc9647ad1e9e
  • Loading branch information
Thodd committed Aug 8, 2023
1 parent 9c0116b commit 01e9de7
Show file tree
Hide file tree
Showing 6 changed files with 342 additions and 67 deletions.
28 changes: 15 additions & 13 deletions src/sap.ui.core/src/sap/ui/core/Theming.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ sap.ui.define([
"sap/base/Eventing",
"sap/base/Log",
"sap/base/i18n/Localization",
"sap/base/util/deepEqual"
"sap/base/util/deepEqual",
"sap/ui/core/theming/ThemeHelper"
], function(
assert,
BaseConfig,
BaseEvent,
Eventing,
Log,
Localization,
deepEqual
deepEqual,
ThemeHelper
) {
"use strict";

Expand Down Expand Up @@ -53,10 +55,12 @@ sap.ui.define([
external: true
});

// empty string is a valid value wrt. the <String> type
// this is a semantic check if we need to default to a valid theme
// Empty string is a valid value wrt. the <String> type.
// An empty string is equivalent to "no theme given" here.
// We apply the default, but also automatically detect the dark mode.
if (sTheme === "") {
sTheme = "base";
const mDefaultThemeInfo = ThemeHelper.getDefaultThemeInfo();
sTheme = `${mDefaultThemeInfo.DEFAULT_THEME}${mDefaultThemeInfo.DARK_MODE ? "_dark" : ""}`;
}

// It's only possible to provide a themeroot via theme parameter using
Expand All @@ -71,7 +75,11 @@ sap.ui.define([
Theming.setThemeRoot(sTheme, sThemeRoot);
}
}
return normalizeTheme(sTheme, Theming.getThemeRoot(sTheme));

// validate theme and fallback to the fixed default, in case the configured theme is not valid
sTheme = ThemeHelper.validateAndFallbackTheme(sTheme, Theming.getThemeRoot(sTheme));

return sTheme;
},

/**
Expand All @@ -85,6 +93,7 @@ sap.ui.define([
if (sTheme.indexOf("@") !== -1) {
throw new TypeError("Providing a theme root as part of the theme parameter is not allowed.");
}

var bFireChange = !mChanges;
mChanges = mChanges || {};
var sOldTheme = Theming.getTheme();
Expand Down Expand Up @@ -446,13 +455,6 @@ sap.ui.define([
}
}

function normalizeTheme(sTheme, sThemeBaseUrl) {
if ( sTheme && sThemeBaseUrl == null && sTheme.match(/^sap_corbu$/i) ) {
return "sap_fiori_3";
}
return sTheme;
}

Eventing.apply(Theming);
return Theming;
});
100 changes: 100 additions & 0 deletions src/sap.ui.core/src/sap/ui/core/theming/ThemeHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,51 @@ sap.ui.define([
// TODO reset map onThemeChanged
var mLibThemeMetadata = {};


// Theme defaulting
const DEFAULT_THEME = "sap_horizon";

// dark mode detection
const bDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches;

// Theme Fallback
const rThemePattern = /^([a-zA-Z0-9_]*)(_(hcb|hcw|dark))$/g;

/**
* The list of all known themes incl. their variants.
* Any SAP theme outside this list will receive a fallback to the predefined default theme.
*
* Note: This list needs to be updated on each release and/or removal of a theme.
*/
const aKnownThemes = [
// horizon
"sap_horizon",
"sap_horizon_dark",
"sap_horizon_hcb",
"sap_horizon_hcw",

// fiori_3
"sap_fiori_3",
"sap_fiori_3_dark",
"sap_fiori_3_hcb",
"sap_fiori_3_hcw",

// belize
"sap_belize",
"sap_belize_plus",
"sap_belize_hcb",
"sap_belize_hcw",

// bluecrystal (deprecated)
"sap_bluecrystal",

// hcb (deprecated) - the standard HCB theme, newer themes have a dedicated HCB/HCW variant
"sap_hcb"
];

// cache for already calculated theme fallbacks
const mThemeFallbacks = {};

/**
*
* @since 1.92.0
Expand Down Expand Up @@ -154,5 +199,60 @@ sap.ui.define([
return !!aCssRules && aCssRules.length > 0;
};

/**
* Validates the given theme and changes it to the predefined standard fallback theme if needed.
*
* An SAP standard theme is considered invalid when it is either:
* - not available anymore (deprecated & removed)
* - not yet available (meaning: released in future versions)
*
* Invalid themes will be defaulted to the predetermined standard default theme.
*
* Themes for which a theme root exists are expected to be served from their given origin
* and will not be adapted.
*
* @param {string} sTheme the theme to be validated
* @param {string|null} sThemeRoot the theme root url for the given theme
* @returns {string} the validated and transformed theme name
*/
ThemeHelper.validateAndFallbackTheme = function(sTheme, sThemeRoot) {
// check cache for already determined fallback
// only do this for themes from the default location (potential SAP standard themes)
if (sThemeRoot == null && mThemeFallbacks[sTheme]) {
return mThemeFallbacks[sTheme];
}

let sNewTheme = sTheme;

// We only fallback for a very specific set of themes:
// * no theme-root is given (themes from a different endpoint (i.e. theming-service) are excluded) and
// * the given theme is a standard SAP theme ('sap_' prefix)
// * not supported in this version
if (sThemeRoot == null && sTheme.startsWith("sap_") && aKnownThemes.indexOf(sTheme) == -1) {
// extract the theme variant if given: "_hcb", "_hcw", "_dark"
const aThemeMatch = rThemePattern.exec(sTheme) || [];
const sVariant = aThemeMatch[2]; //match includes an underscore

if (sVariant) {
sNewTheme = `${DEFAULT_THEME}${sVariant}`;
} else {
sNewTheme = DEFAULT_THEME;
}

mThemeFallbacks[sTheme] = sNewTheme;

Log.warning(`The configured theme '${sTheme}' is not yet or no longer supported in this version. The valid fallback theme is '${sNewTheme}'.`, "Theming");
}

return sNewTheme;
};

ThemeHelper.getDefaultThemeInfo = function() {
return {
DEFAULT_THEME: DEFAULT_THEME,
DARK_MODE: bDarkMode
};
};

return ThemeHelper;
});

0 comments on commit 01e9de7

Please sign in to comment.