Skip to content

Commit 0dda8ea

Browse files
Alexander KatrukhinCopilot
authored andcommitted
feat(react-cap-theme): add storybook theme switcher toolbar
Add a Theme toolbar control to the react-cap-theme storybook so stories can switch between Fluent UI Light/Dark and CAP Light/Dark. The decorator applies the selected theme and CAP style hooks via FluentProvider. Also fill the docs canvas card with the active theme background. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a77fb89 commit 0dda8ea

4 files changed

Lines changed: 104 additions & 30 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "none",
3+
"comment": "add storybook theme switcher toolbar",
4+
"packageName": "@fluentui-contrib/react-cap-theme",
5+
"email": "Oleksandr.Katrukhin@microsoft.com",
6+
"dependentChangeType": "none"
7+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* Let the themed FluentProvider fill the docs preview card instead of Storybook's default white frame. */
2+
#storybook-docs .sbdocs-preview {
3+
background: transparent;
4+
overflow: hidden;
5+
}
6+
7+
#storybook-docs .innerZoomElementWrapper {
8+
padding: 0;
9+
}
10+
11+
#storybook-docs .docs-story .fui-FluentProvider {
12+
width: 100%;
13+
box-sizing: border-box;
14+
padding: 30px;
15+
border-radius: 9px;
16+
}

packages/react-cap-theme/.storybook/preview.tsx

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,42 @@
11
import * as React from 'react';
22
import rootPreview from '../../../.storybook/preview';
3-
import {
4-
FluentProvider,
5-
Theme,
6-
webLightTheme,
7-
} from '@fluentui/react-components';
8-
import { CAP_STYLE_HOOKS } from '../src/index';
3+
import { FluentProvider } from '@fluentui/react-components';
94
import type { JSXElement } from '@fluentui/react-utilities';
105

11-
import type { Preview, StoryFn } from '@storybook/react';
12-
import { CAPTokens } from '../src/components/tokens/types';
13-
14-
const capTheme: Record<keyof Theme & keyof CAPTokens, string> = {
15-
...webLightTheme,
16-
borderRadius2XLarge: '12px',
17-
borderRadius3XLarge: '16px',
18-
borderRadius4XLarge: '24px',
19-
colorNeutralStroke4: '#ebebeb',
20-
colorNeutralStroke4Hover: '#e0e0e0',
21-
colorNeutralStroke4Pressed: '#d6d6d6',
22-
colorNeutralStroke4Selected: '#ebebeb',
23-
colorNeutralForeground5: '#616161',
24-
colorNeutralForeground5Hover: '#242424',
25-
colorNeutralForeground5Pressed: '#242424',
26-
};
6+
import type { Preview, StoryContext, StoryFn } from '@storybook/react';
7+
import { DEFAULT_THEME, THEMES, type ThemeId } from './themes';
8+
import './docs-theme-fill.css';
279

2810
const preview: Preview = {
2911
...rootPreview,
12+
globalTypes: {
13+
theme: {
14+
description: 'Theme used to render the components',
15+
toolbar: {
16+
title: 'Theme',
17+
icon: 'paintbrush',
18+
items: (Object.keys(THEMES) as ThemeId[]).map((value) => ({
19+
value,
20+
title: THEMES[value].title,
21+
})),
22+
dynamicTitle: true,
23+
},
24+
},
25+
},
26+
initialGlobals: {
27+
theme: DEFAULT_THEME,
28+
},
3029
decorators: [
31-
(Story: StoryFn): JSXElement => (
32-
<FluentProvider
33-
theme={capTheme as any}
34-
customStyleHooks_unstable={CAP_STYLE_HOOKS}
35-
>
36-
<Story />
37-
</FluentProvider>
38-
),
30+
(Story: StoryFn, context: StoryContext): JSXElement => {
31+
const themeId = (context.globals.theme as ThemeId) ?? DEFAULT_THEME;
32+
const { theme, styleHooks } = THEMES[themeId] ?? THEMES[DEFAULT_THEME];
33+
34+
return (
35+
<FluentProvider theme={theme} customStyleHooks_unstable={styleHooks}>
36+
<Story />
37+
</FluentProvider>
38+
);
39+
},
3940
],
4041
tags: ['autodocs'],
4142
};
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { type Theme, webDarkTheme, webLightTheme } from '@fluentui/react-components';
2+
import type { FluentProviderProps } from '@fluentui/react-components';
3+
import { CAP_STYLE_HOOKS } from '../src/index';
4+
import type { CAPTokens } from '../src/components/tokens/types';
5+
6+
const capLightTokens: CAPTokens = {
7+
borderRadius2XLarge: '12px',
8+
borderRadius3XLarge: '16px',
9+
borderRadius4XLarge: '24px',
10+
colorNeutralStroke4: '#ebebeb',
11+
colorNeutralStroke4Hover: '#e0e0e0',
12+
colorNeutralStroke4Pressed: '#d6d6d6',
13+
colorNeutralStroke4Selected: '#ebebeb',
14+
colorNeutralForeground5: '#616161',
15+
colorNeutralForeground5Hover: '#242424',
16+
colorNeutralForeground5Pressed: '#242424',
17+
};
18+
19+
const capDarkTokens: CAPTokens = {
20+
borderRadius2XLarge: '12px',
21+
borderRadius3XLarge: '16px',
22+
borderRadius4XLarge: '24px',
23+
colorNeutralStroke4: '#3d3d3d',
24+
colorNeutralStroke4Hover: '#444444',
25+
colorNeutralStroke4Pressed: '#4a4a4a',
26+
colorNeutralStroke4Selected: '#3d3d3d',
27+
colorNeutralForeground5: '#adadad',
28+
colorNeutralForeground5Hover: '#ffffff',
29+
colorNeutralForeground5Pressed: '#ffffff',
30+
};
31+
32+
export const capLightTheme: Theme = { ...webLightTheme, ...capLightTokens };
33+
export const capDarkTheme: Theme = { ...webDarkTheme, ...capDarkTokens };
34+
35+
export type ThemeId = 'fluentLight' | 'fluentDark' | 'capLight' | 'capDark';
36+
37+
type ThemeConfig = {
38+
title: string;
39+
theme: Theme;
40+
styleHooks?: FluentProviderProps['customStyleHooks_unstable'];
41+
};
42+
43+
export const THEMES: Record<ThemeId, ThemeConfig> = {
44+
fluentLight: { title: 'Fluent UI Light', theme: webLightTheme },
45+
fluentDark: { title: 'Fluent UI Dark', theme: webDarkTheme },
46+
capLight: { title: 'CAP Light', theme: capLightTheme, styleHooks: CAP_STYLE_HOOKS },
47+
capDark: { title: 'CAP Dark', theme: capDarkTheme, styleHooks: CAP_STYLE_HOOKS },
48+
};
49+
50+
export const DEFAULT_THEME: ThemeId = 'capLight';

0 commit comments

Comments
 (0)