Skip to content

Commit 3a020c3

Browse files
authored
Storybook theme switcher (#97201)
Allow switching the EUI theme in the Storybook menu bar.
1 parent 00c133b commit 3a020c3

File tree

7 files changed

+246
-11
lines changed

7 files changed

+246
-11
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@
472472
"@storybook/addon-knobs": "^6.1.20",
473473
"@storybook/addon-storyshots": "^6.1.20",
474474
"@storybook/addons": "^6.1.20",
475+
"@storybook/api": "^6.1.20",
475476
"@storybook/components": "^6.1.20",
476477
"@storybook/core": "^6.1.20",
477478
"@storybook/core-events": "^6.1.20",

packages/kbn-storybook/lib/register.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import { addons } from '@storybook/addons';
1010
import { create } from '@storybook/theming';
11+
import { registerThemeSwitcherAddon } from './register_theme_switcher_addon';
1112

1213
// This configures the "Manager", or main outer view of Storybook. It is an
1314
// addon that's loaded by the `managerEntries` part of the preset in ../preset.js.
@@ -22,3 +23,5 @@ addons.setConfig({
2223
panelPosition: 'bottom',
2324
isToolshown: true,
2425
});
26+
27+
registerThemeSwitcherAddon();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import { addons, types } from '@storybook/addons';
10+
import { ThemeSwitcher } from './theme_switcher';
11+
12+
export function registerThemeSwitcherAddon() {
13+
addons.register('kibana/eui-theme-switcher', (api) => {
14+
const channel = api.getChannel();
15+
16+
channel.on('globalsUpdated', ({ globals }) => {
17+
const previewFrame = document.getElementById(
18+
'storybook-preview-iframe'
19+
) as HTMLIFrameElement | null;
20+
const stylesheet = previewFrame?.contentDocument?.getElementById(
21+
'eui-theme-css'
22+
) as HTMLLinkElement | null;
23+
24+
if (stylesheet) {
25+
stylesheet.href = `kbn-ui-shared-deps.${globals.euiTheme}.css`;
26+
}
27+
});
28+
29+
addons.add('kibana/eui-theme-switcher', {
30+
title: 'EUI Theme Switcher',
31+
type: types.TOOL,
32+
match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
33+
render: ThemeSwitcher,
34+
});
35+
});
36+
}

packages/kbn-storybook/lib/templates/index.ejs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<script src="kbn-ui-shared-deps.@elastic.js"></script>
2222
<script src="kbn-ui-shared-deps.js"></script>
2323
<link href="kbn-ui-shared-deps.css" rel="stylesheet" />
24-
<link href="kbn-ui-shared-deps.v7.light.css" rel="stylesheet" />
24+
<link id="eui-theme-css" href="kbn-ui-shared-deps.v8.light.css" rel="stylesheet" />
2525
<!-- -->
2626

2727
<% if (typeof headHtmlSnippet !== 'undefined') { %> <%= headHtmlSnippet %> <% } %> <%
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import React from 'react';
10+
import { Icons, IconButton, TooltipLinkList, WithTooltip } from '@storybook/components';
11+
import { useGlobals } from '@storybook/api';
12+
import { Link } from '@storybook/components/dist/tooltip/TooltipLinkList';
13+
14+
const defaultTheme = 'v8.light';
15+
16+
export function ThemeSwitcher() {
17+
const [globals, updateGlobals] = useGlobals();
18+
const selectedTheme = globals.euiTheme;
19+
20+
if (!selectedTheme || selectedTheme === defaultTheme) {
21+
updateGlobals({ euiTheme: defaultTheme });
22+
}
23+
24+
function Menu({ onHide }: { onHide: () => void }) {
25+
const links: Link[] = [
26+
{
27+
id: 'v8.light',
28+
title: 'Amsterdam: Light',
29+
},
30+
{
31+
id: 'v8.dark',
32+
title: 'Amsterdam: Dark',
33+
},
34+
{ id: 'v7.light', title: 'Light' },
35+
{ id: 'v7.dark', title: 'Dark' },
36+
].map((link) => ({
37+
...link,
38+
onClick: (_event, item) => {
39+
if (item.id !== selectedTheme) {
40+
updateGlobals({ euiTheme: item.id });
41+
}
42+
onHide();
43+
},
44+
active: selectedTheme === link.id,
45+
}));
46+
47+
return <TooltipLinkList links={links} />;
48+
}
49+
50+
return (
51+
<WithTooltip
52+
placement="top"
53+
trigger="click"
54+
closeOnClick
55+
tooltip={({ onHide }) => <Menu onHide={onHide} />}
56+
>
57+
<IconButton key="eui-theme" title="Change the EUI theme">
58+
<Icons icon={selectedTheme?.includes('dark') ? 'heart' : 'hearthollow'} />
59+
</IconButton>
60+
</WithTooltip>
61+
);
62+
}

packages/kbn-storybook/tsconfig.json

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,7 @@
88
"declarationMap": true,
99
"sourceMap": true,
1010
"sourceRoot": "../../../../packages/kbn-storybook",
11-
"types": [
12-
"node"
13-
]
11+
"types": ["node"]
1412
},
15-
"include": [
16-
"*.ts",
17-
"lib/*.ts"
18-
]
13+
"include": ["*.ts", "lib/**/*.ts", "lib/**/*.tsx", "../../typings/index.d.ts"]
1914
}

0 commit comments

Comments
 (0)