Create Storybook add-on for switching VSCode themes

This adds a Storybook add-on that allows you to switch between VSCode
theme. It follows the pattern of the [outline](https://github.com/storybookjs/storybook/tree/v6.5.12/addons/outline/src)
and [backgrounds](https://github.com/storybookjs/storybook/tree/v6.5.12/addons/backgrounds)
add-ons.

Unfortunately, it doesn't apply the CSS to just the elements it should
be applied to, but globally to the complete preview. This is a limitation
of using CSS files rather than setting inline styles on the elements. We
might be able to resolve this in the future by extracting the CSS
variables from the CSS files, but this is somewhat more involved.
This commit is contained in:
Koen Vlaswinkel
2022-10-19 11:01:24 +02:00
parent 157a5d6afd
commit a254ceaa59
12 changed files with 162 additions and 60 deletions

View File

@@ -8,7 +8,8 @@ const config: StorybookConfig = {
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions'
'@storybook/addon-interactions',
'./vscode-theme-addon/preset.ts',
],
framework: '@storybook/react',
core: {

View File

@@ -4,8 +4,6 @@ import { action } from '@storybook/addon-actions';
// Allow all stories/components to use Codicons
import '@vscode/codicons/dist/codicon.css';
import '../src/stories/vscode-theme-dark.css';
// https://storybook.js.org/docs/react/configure/overview#configure-story-rendering
export const parameters = {
// All props starting with `on` will automatically receive an action as a prop
@@ -22,13 +20,8 @@ export const parameters = {
theme: themes.dark,
},
backgrounds: {
default: 'dark',
values: [
{
name: 'dark',
value: '#1e1e1e',
},
],
// The background is injected by our theme CSS files
disable: true,
}
};

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "node",
"target": "es6",
"outDir": "out",
"lib": ["ES2021", "dom"],
"jsx": "react",
"sourceMap": true,
"rootDir": "..",
"strict": true,
"noUnusedLocals": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"experimentalDecorators": true,
"skipLibCheck": true
},
"exclude": ["node_modules"]
}

View File

@@ -0,0 +1,49 @@
import * as React from 'react';
import { FunctionComponent, useCallback } from 'react';
import { useGlobals } from '@storybook/api';
import { IconButton, Icons, WithTooltip, TooltipLinkList, Link, WithHideFn } from '@storybook/components';
import { themeNames, VSCodeTheme } from './theme';
export const ThemeSelector: FunctionComponent = () => {
const [{ vscodeTheme }, updateGlobals] = useGlobals();
const changeTheme = useCallback((theme: VSCodeTheme) => {
updateGlobals({
vscodeTheme: theme,
});
}, [updateGlobals]);
const createLinks = useCallback((onHide: () => void): Link[] => Object.values(VSCodeTheme).map((theme) => ({
id: theme,
onClick() {
changeTheme(theme);
onHide();
},
title: themeNames[theme],
value: theme,
active: vscodeTheme === theme,
})), [vscodeTheme, changeTheme]);
return (
<WithTooltip
placement="top"
trigger="click"
closeOnClick
tooltip={({ onHide }: WithHideFn) => (
<TooltipLinkList
links={createLinks(onHide)}
/>
)}
>
<IconButton
key="theme"
title="Change the theme of the preview"
active={vscodeTheme !== VSCodeTheme.Dark}
>
<Icons icon="dashboard" />
</IconButton>
</WithTooltip>
);
};

View File

@@ -0,0 +1,14 @@
import * as React from 'react';
import { addons, types } from '@storybook/addons';
import { ThemeSelector } from './ThemeSelector';
const ADDON_ID = 'vscode-theme-addon';
addons.register(ADDON_ID, () => {
addons.add(ADDON_ID, {
title: 'VSCode Themes',
type: types.TOOL,
match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
render: () => <ThemeSelector />,
});
});

View File

@@ -0,0 +1,7 @@
export function config(entry = []) {
return [...entry, require.resolve("./preview.ts")];
}
export function managerEntries(entry = []) {
return [...entry, require.resolve("./manager.tsx")];
}

View File

@@ -0,0 +1,8 @@
import { withTheme } from './withTheme';
import { VSCodeTheme } from './theme';
export const decorators = [withTheme];
export const globals = {
vscodeTheme: VSCodeTheme.Dark,
};

View File

@@ -0,0 +1,9 @@
export enum VSCodeTheme {
Dark = 'dark',
Light = 'light',
}
export const themeNames: { [key in VSCodeTheme]: string } = {
[VSCodeTheme.Dark]: 'Dark+',
[VSCodeTheme.Light]: 'Light+',
}

View File

@@ -0,0 +1,36 @@
import { useEffect, useGlobals } from '@storybook/addons';
import type { AnyFramework, PartialStoryFn as StoryFunction, StoryContext } from '@storybook/csf';
import { VSCodeTheme } from './theme';
const themeFiles: { [key in VSCodeTheme]: string } = {
[VSCodeTheme.Dark]: require('!file-loader?modules!../../src/stories/vscode-theme-dark.css').default,
[VSCodeTheme.Light]: require('!file-loader?modules!../../src/stories/vscode-theme-light.css').default,
}
export const withTheme = (
StoryFn: StoryFunction<AnyFramework>,
context: StoryContext<AnyFramework>
) => {
const [{ vscodeTheme }] = useGlobals();
useEffect(() => {
const styleSelectorId =
context.viewMode === 'docs'
? `addon-vscode-theme-docs-${context.id}`
: `addon-vscode-theme-theme`;
const theme = Object.values(VSCodeTheme).includes(vscodeTheme) ? vscodeTheme as VSCodeTheme : VSCodeTheme.Dark;
document.getElementById(styleSelectorId)?.remove();
const link = document.createElement('link');
link.id = styleSelectorId;
link.href = themeFiles[theme];
link.rel = 'stylesheet';
document.head.appendChild(link);
}, [vscodeTheme]);
return StoryFn();
};

View File

@@ -12,56 +12,8 @@ Welcome to the Storybook for **CodeQL for Visual Studio Code**! This Storybook c
### Switching themes
To switch between VSCode Dark+ and Light+ themes, you can make the following changes:
```diff
diff --git a/extensions/ql-vscode/.storybook/manager.ts b/extensions/ql-vscode/.storybook/manager.ts
--- a/extensions/ql-vscode/.storybook/manager.ts
+++ b/extensions/ql-vscode/.storybook/manager.ts
@@ -2,6 +2,6 @@ import { addons } from '@storybook/addons';
import { themes } from '@storybook/theming';
addons.setConfig({
- theme: themes.dark,
+ theme: themes.light,
enableShortcuts: false,
});
diff --git a/extensions/ql-vscode/.storybook/preview.ts b/extensions/ql-vscode/.storybook/preview.ts
--- a/extensions/ql-vscode/.storybook/preview.ts
+++ b/extensions/ql-vscode/.storybook/preview.ts
@@ -4,7 +4,7 @@ import { action } from '@storybook/addon-actions';
// Allow all stories/components to use Codicons
import '@vscode/codicons/dist/codicon.css';
-import '../src/stories/vscode-theme-dark.css';
+import '../src/stories/vscode-theme-light.css';
// https://storybook.js.org/docs/react/configure/overview#configure-story-rendering
export const parameters = {
@@ -19,14 +19,14 @@ export const parameters = {
},
- // Use a dark theme to be aligned with VSCode
+ // Use a light theme to be aligned with VSCode
docs: {
- theme: themes.dark,
+ theme: themes.light,
},
backgrounds: {
- default: 'dark',
+ default: 'light',
values: [
{
- name: 'dark',
- value: '#1e1e1e',
+ name: 'light',
+ value: '#ffffff',
},
],
}
```
You will need to restart Storybook to apply the theme change to the Storybook UI. The preview frame should update
automatically.
To switch between VSCode Dark+ and Light+ themes, use the button in the toolbar. This will not work on this document, so you'll only see
the changes applied to a different story.
### Writing stories

View File

@@ -628,3 +628,10 @@ body {
margin: 0;
padding: 0 20px;
}
/**
* This is used for setting the background on the Storybook preview.
*/
body {
background-color: var(--vscode-editor-background);
}

View File

@@ -626,3 +626,10 @@ body {
margin: 0;
padding: 0 20px;
}
/**
* This is used for setting the background on the Storybook preview.
*/
body {
background-color: var(--vscode-editor-background);
}