Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic support for experimental theme switching #199748

Merged
merged 26 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8d4b0f7
feat: add basic support for experimental theme switching
tkajtoch Nov 12, 2024
0bb04e8
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 12, 2024
e510604
update tests and mocks to account for the new `name` property
tkajtoch Nov 12, 2024
b521f53
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 12, 2024
c8d090c
fix failing bootstrap_renderer tests due to now incorrect UI settings…
tkajtoch Nov 12, 2024
6e85833
add `theme:name` to stack telemetry collectors
tkajtoch Nov 12, 2024
e385ad2
add theme utilities to core-ui-settings-common and refactor theme loa…
tkajtoch Nov 13, 2024
591948f
eslint fix
tkajtoch Nov 13, 2024
e1ae606
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 13, 2024
591bc4a
fix test assertions
tkajtoch Nov 13, 2024
7b518a3
Merge branch 'main' into feat/eui-theming
clintandrewhall Nov 13, 2024
ac0c7b6
fix `theme_loader` to only include the fallback `default` switch clau…
tkajtoch Nov 13, 2024
c9bc309
Update packages/core/rendering/core-rendering-server-internal/src/boo…
tkajtoch Nov 13, 2024
08efdfb
Update packages/core/rendering/core-rendering-server-internal/src/boo…
tkajtoch Nov 13, 2024
544b270
add `@kbn/react-kibana-context-theme` to shared deps
tkajtoch Nov 14, 2024
deab885
increase aiops optimizer limits slightly
tkajtoch Nov 14, 2024
38364d2
Revert "add `@kbn/react-kibana-context-theme` to shared deps"
tkajtoch Nov 14, 2024
a4391ca
Revert "Revert "add `@kbn/react-kibana-context-theme` to shared deps""
tkajtoch Nov 14, 2024
9d8c8f6
update type of `@kbn/react-kibana-context-theme` to `shared-common` s…
tkajtoch Nov 14, 2024
84b20c7
update type of `@kbn/react-kibana-context-root` and `@kbn/react-kiban…
tkajtoch Nov 14, 2024
d721304
Merge remote-tracking branch 'upstream/main' into feat/eui-theming
tkajtoch Nov 14, 2024
e69f313
[CI] Auto-commit changed files from 'make api-docs'
kibanamachine Nov 14, 2024
7b0a622
Merge branch 'main' into feat/eui-theming
elasticmachine Nov 14, 2024
069b07e
Merge branch 'main' into feat/eui-theming
elasticmachine Nov 14, 2024
7882da6
Merge branch 'main' into feat/eui-theming
mistic Nov 15, 2024
4b791f0
Merge branch 'main' into feat/eui-theming
elasticmachine Nov 18, 2024
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
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',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so is v8 still the default theme?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, v8 is the theme tag of what kibana ships with right now, and we call it Amsterdam. Up to this point themes were versioned by kibana majors and not named, that's why we have the v8 theme (and before that we had the v7 theme) but that's not what we want to do moving forward.

The updated approach is to differentiate themes by their name resulting in theme tags like borealislight and borealisdark for the new Borealis theme. For compatibility reasons we decided to keep the theme tags for the current Amsterdam theme the same, so v8light and v8dark, and that's why that specific mapping of amsterdam theme name to v8 is necessary.

darkMode: false,
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import { createHash } from 'crypto';
import { PackageInfo } from '@kbn/config';
import { ThemeVersion } from '@kbn/ui-shared-deps-npm';
import type { KibanaRequest, HttpAuth } from '@kbn/core-http-server';
import { type DarkModeValue, parseDarkModeValue } from '@kbn/core-ui-settings-common';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-server';
Expand Down Expand Up @@ -59,7 +58,7 @@ export const bootstrapRendererFactory: BootstrapRendererFactory = ({

return async function bootstrapRenderer({ uiSettingsClient, request, isAnonymousPage = false }) {
let darkMode: DarkModeValue = false;
const themeVersion: ThemeVersion = 'v8';
let themeName: string = 'amsterdam';

try {
const authenticated = isAuthenticated(request);
Expand All @@ -72,6 +71,8 @@ export const bootstrapRendererFactory: BootstrapRendererFactory = ({
} else {
darkMode = parseDarkModeValue(await uiSettingsClient.get('theme:darkMode'));
}

themeName = await uiSettingsClient.get('theme:name');
}
} catch (e) {
// just use the default values in case of connectivity issues with ES
Expand All @@ -83,7 +84,7 @@ export const bootstrapRendererFactory: BootstrapRendererFactory = ({
}

const themeTag = getThemeTag({
themeVersion,
name: !themeName || themeName === 'amsterdam' ? 'v8' : themeName,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theme tags should follow the <name><color_mode> format, but in case of the current Amsterdam theme they're called v8light and v8dark instead. Let's keep it this way for compatibility.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the themeName UI setting is amsterdam we're retuning 'v8' and if it's 'v8' we'll also return 'v8'. Maybe I'm not understanding what this is supposed to do, but seems like maybe the logic should be !== 'amsterdam'?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the themeName UI setting is amsterdam we're retuning 'v8' and if it's 'v8' we'll also return 'v8'

Yeah, that's technically correct. We're not expecting v8 to be a value of themeName but even if it was it's not going to break anything since it will result in a valid theme tag.

This logic exists only to map amsterdam to v8 - we're decoupling theme names from kibana versioning (like v7 and v8), but there's no real value renaming the current theme to amsterdam and all places where v8 could be used.

darkMode,
});
const bundlesHref = getBundlesHref(baseHref);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ describe('getThemeTag', () => {
it('returns the correct value for version:v8 and darkMode:false', () => {
tkajtoch marked this conversation as resolved.
Show resolved Hide resolved
expect(
getThemeTag({
themeVersion: 'v8',
name: 'v8',
darkMode: false,
})
).toEqual('v8light');
});
it('returns the correct value for version:v8 and darkMode:true', () => {
tkajtoch marked this conversation as resolved.
Show resolved Hide resolved
expect(
getThemeTag({
themeVersion: 'v8',
name: 'v8',
darkMode: true,
})
).toEqual('v8dark');
Expand Down
Loading