Skip to content

Commit

Permalink
Add basic support for experimental theme switching (#199748)
Browse files Browse the repository at this point in the history
## Summary

This PR adds basic support for theme switching to test experimental
themes internally. It defines a new `theme:name` setting which is
read-only (and thus hidden) by default, and needs
`uiSettings.experimental.themeSwitcherEnabled: true` config setting to
become visible. The implementation and the way the theme switcher
feature is enabled will likely change in the upcoming weeks when we iron
out the details, but the way it works right now is sufficient for
testing and that's what the immediate goal is.

Please note this PR includes mostly setup work, and no additional themes
have been added here. To make reviewing slightly easier, these will be
added separately.

The feature and settings should be undocumented for the time being and
disabled by default.

---

As you might notice, there's a new setting added despite already having
`theme:version`. We decided to introduce a new setting instead of
resurrecting an existing one for better naming and flexibility and to
reduce the risk of breaking things that might still use the old setting
value (hardcoded to `v8` at the moment). The current plan is to remove
`theme:version` in 9.0.

The theme_loader got refactored to only bundle active themes coming from
`KBN_OPTIMIZER_THEMES` (defaulting to `v8light` and `v8dark`) instead of
`ALL_THEMES` that would significantly increase bundle sizes even when
the experimental themes were not enabled. Considering there's a SASS to
CSS-in-JS migration happening right now, we don't need to worry about
Kibana bundles growing in size when a new theme is officially added.

### Checklist

Delete any items that are not applicable to this PR.

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)
- [ ] This will appear in the **Release Notes** and follow the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <[email protected]>
Co-authored-by: Clint Andrew Hall <[email protected]>
Co-authored-by: Rudolf Meijering <[email protected]>
Co-authored-by: Elastic Machine <[email protected]>
Co-authored-by: Tiago Costa <[email protected]>
  • Loading branch information
6 people authored Nov 18, 2024
1 parent a8fc787 commit 5b77b7e
Show file tree
Hide file tree
Showing 80 changed files with 597 additions and 289 deletions.
35 changes: 35 additions & 0 deletions packages/core/base/core-base-common/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")

SRCS = glob(
[
"**/*.ts",
"**/*.tsx",
],
exclude = [
"**/test_helpers.ts",
"**/*.config.js",
"**/*.mock.*",
"**/*.test.*",
"**/*.stories.*",
"**/__snapshots__/**",
"**/integration_tests/**",
"**/mocks/**",
"**/scripts/**",
"**/storybook/**",
"**/test_fixtures/**",
"**/test_helpers/**",
],
)

DEPS = [
"@npm//react",
"@npm//tslib",
]

js_library(
name = "core-base-common",
package_name = "@kbn/core-base-common",
srcs = ["package.json"] + SRCS,
deps = DEPS,
visibility = ["//visibility:public"],
)
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const createSetupContractMock = () => {
setupContract.getPlugins.mockReturnValue([]);
setupContract.getTheme.mockReturnValue({
darkMode: false,
name: 'amsterdam',
version: 'v8',
stylesheetPaths: {
default: ['light-1.css'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface InjectedMetadataExternalUrlPolicy {
/** @internal */
export interface InjectedMetadataTheme {
darkMode: DarkModeValue;
name: string;
version: ThemeVersion;
stylesheetPaths: {
default: string[];
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ const createPackageInfo = (parts: Partial<PackageInfo> = {}): PackageInfo => ({
...parts,
});

const getClientGetMockImplementation =
({ darkMode, name }: { darkMode?: boolean; name?: string } = {}) =>
(key: string) => {
switch (key) {
case 'theme:darkMode':
return Promise.resolve(darkMode ?? false);
case 'theme:name':
return Promise.resolve(name ?? 'amsterdam');
}
return Promise.resolve();
};

const createUiPlugins = (): UiPlugins => ({
public: new Map(),
internal: new Map(),
Expand All @@ -59,6 +71,7 @@ describe('bootstrapRenderer', () => {
getPluginsBundlePathsMock.mockReturnValue(new Map());
renderTemplateMock.mockReturnValue('__rendered__');
getJsDependencyPathsMock.mockReturnValue([]);
uiSettingsClient.get.mockImplementation(getClientGetMockImplementation());

renderer = bootstrapRendererFactory({
auth,
Expand Down Expand Up @@ -91,13 +104,17 @@ describe('bootstrapRenderer', () => {
uiSettingsClient,
});

expect(uiSettingsClient.get).toHaveBeenCalledTimes(1);
expect(uiSettingsClient.get).toHaveBeenCalledTimes(2);
expect(uiSettingsClient.get).toHaveBeenCalledWith('theme:darkMode');
expect(uiSettingsClient.get).toHaveBeenCalledWith('theme:name');
});

it('calls getThemeTag with the values from the UiSettingsClient (true/dark) when the UserSettingsService is not provided', async () => {
uiSettingsClient.get.mockResolvedValue(true);

uiSettingsClient.get.mockImplementation(
getClientGetMockImplementation({
darkMode: true,
})
);
const request = httpServerMock.createKibanaRequest();

await renderer({
Expand All @@ -107,13 +124,13 @@ describe('bootstrapRenderer', () => {

expect(getThemeTagMock).toHaveBeenCalledTimes(1);
expect(getThemeTagMock).toHaveBeenCalledWith({
themeVersion: 'v8',
name: 'v8',
darkMode: true,
});
});

it('calls getThemeTag with the values from the UiSettingsClient (false/light) when the UserSettingsService is not provided', async () => {
uiSettingsClient.get.mockResolvedValue(false);
uiSettingsClient.get.mockImplementation(getClientGetMockImplementation({}));

const request = httpServerMock.createKibanaRequest();

Expand All @@ -124,7 +141,7 @@ describe('bootstrapRenderer', () => {

expect(getThemeTagMock).toHaveBeenCalledTimes(1);
expect(getThemeTagMock).toHaveBeenCalledWith({
themeVersion: 'v8',
name: 'v8',
darkMode: false,
});
});
Expand All @@ -150,7 +167,7 @@ describe('bootstrapRenderer', () => {

expect(getThemeTagMock).toHaveBeenCalledTimes(1);
expect(getThemeTagMock).toHaveBeenCalledWith({
themeVersion: 'v8',
name: 'v8',
darkMode: true,
});
});
Expand All @@ -166,7 +183,6 @@ describe('bootstrapRenderer', () => {
userSettingsService,
});

uiSettingsClient.get.mockResolvedValue(true);
const request = httpServerMock.createKibanaRequest();

await renderer({
Expand All @@ -176,7 +192,7 @@ describe('bootstrapRenderer', () => {

expect(getThemeTagMock).toHaveBeenCalledTimes(1);
expect(getThemeTagMock).toHaveBeenCalledWith({
themeVersion: 'v8',
name: 'v8',
darkMode: false,
});
});
Expand All @@ -192,7 +208,6 @@ describe('bootstrapRenderer', () => {
userSettingsService,
});

uiSettingsClient.get.mockResolvedValue(false);
const request = httpServerMock.createKibanaRequest();

await renderer({
Expand All @@ -202,7 +217,7 @@ describe('bootstrapRenderer', () => {

expect(getThemeTagMock).toHaveBeenCalledTimes(1);
expect(getThemeTagMock).toHaveBeenCalledWith({
themeVersion: 'v8',
name: 'v8',
darkMode: false,
});
});
Expand All @@ -218,7 +233,11 @@ describe('bootstrapRenderer', () => {
userSettingsService,
});

uiSettingsClient.get.mockResolvedValue(true);
uiSettingsClient.get.mockImplementation(
getClientGetMockImplementation({
darkMode: true,
})
);
const request = httpServerMock.createKibanaRequest();

await renderer({
Expand All @@ -228,7 +247,7 @@ describe('bootstrapRenderer', () => {

expect(getThemeTagMock).toHaveBeenCalledTimes(1);
expect(getThemeTagMock).toHaveBeenCalledWith({
themeVersion: 'v8',
name: 'v8',
darkMode: true,
});
});
Expand All @@ -250,12 +269,17 @@ describe('bootstrapRenderer', () => {
uiSettingsClient,
});

expect(uiSettingsClient.get).toHaveBeenCalledTimes(1);
expect(uiSettingsClient.get).toHaveBeenCalledTimes(2);
expect(uiSettingsClient.get).toHaveBeenCalledWith('theme:darkMode');
expect(uiSettingsClient.get).toHaveBeenCalledWith('theme:name');
});

it('calls getThemeTag with the correct parameters', async () => {
uiSettingsClient.get.mockResolvedValue(true);
uiSettingsClient.get.mockImplementation(
getClientGetMockImplementation({
darkMode: true,
})
);

const request = httpServerMock.createKibanaRequest();

Expand All @@ -266,7 +290,7 @@ describe('bootstrapRenderer', () => {

expect(getThemeTagMock).toHaveBeenCalledTimes(1);
expect(getThemeTagMock).toHaveBeenCalledWith({
themeVersion: 'v8',
name: 'v8',
darkMode: true,
});
});
Expand All @@ -283,7 +307,7 @@ describe('bootstrapRenderer', () => {

expect(getThemeTagMock).toHaveBeenCalledTimes(1);
expect(getThemeTagMock).toHaveBeenCalledWith({
themeVersion: 'v8',
name: 'system',
darkMode: false,
});
});
Expand Down Expand Up @@ -318,7 +342,7 @@ describe('bootstrapRenderer', () => {

expect(getThemeTagMock).toHaveBeenCalledTimes(1);
expect(getThemeTagMock).toHaveBeenCalledWith({
themeVersion: 'v8',
name: 'v8',
darkMode: false,
});
});
Expand Down
Loading

0 comments on commit 5b77b7e

Please sign in to comment.