diff --git a/change/@fluentui-contrib-react-cap-theme-3c82bedc-0a9f-45e0-9b6d-187286f83dd3.json b/change/@fluentui-contrib-react-cap-theme-3c82bedc-0a9f-45e0-9b6d-187286f83dd3.json new file mode 100644 index 00000000..c3c65a69 --- /dev/null +++ b/change/@fluentui-contrib-react-cap-theme-3c82bedc-0a9f-45e0-9b6d-187286f83dd3.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "add storybook theme switcher toolbar", + "packageName": "@fluentui-contrib/react-cap-theme", + "email": "Oleksandr.Katrukhin@microsoft.com", + "dependentChangeType": "none" +} diff --git a/packages/react-cap-theme/.storybook/docs-theme-fill.css b/packages/react-cap-theme/.storybook/docs-theme-fill.css new file mode 100644 index 00000000..0405703f --- /dev/null +++ b/packages/react-cap-theme/.storybook/docs-theme-fill.css @@ -0,0 +1,16 @@ +/* Let the themed FluentProvider fill the docs preview card instead of Storybook's default white frame. */ +#storybook-docs .sbdocs-preview { + background: transparent; + overflow: hidden; +} + +#storybook-docs .innerZoomElementWrapper { + padding: 0; +} + +#storybook-docs .docs-story .fui-FluentProvider { + width: 100%; + box-sizing: border-box; + padding: 30px; + border-radius: 9px; +} diff --git a/packages/react-cap-theme/.storybook/preview.tsx b/packages/react-cap-theme/.storybook/preview.tsx index b864c707..859c8b9e 100644 --- a/packages/react-cap-theme/.storybook/preview.tsx +++ b/packages/react-cap-theme/.storybook/preview.tsx @@ -1,41 +1,42 @@ import * as React from 'react'; import rootPreview from '../../../.storybook/preview'; -import { - FluentProvider, - Theme, - webLightTheme, -} from '@fluentui/react-components'; -import { CAP_STYLE_HOOKS } from '../src/index'; +import { FluentProvider } from '@fluentui/react-components'; import type { JSXElement } from '@fluentui/react-utilities'; -import type { Preview, StoryFn } from '@storybook/react'; -import { CAPTokens } from '../src/components/tokens/types'; - -const capTheme: Record = { - ...webLightTheme, - borderRadius2XLarge: '12px', - borderRadius3XLarge: '16px', - borderRadius4XLarge: '24px', - colorNeutralStroke4: '#ebebeb', - colorNeutralStroke4Hover: '#e0e0e0', - colorNeutralStroke4Pressed: '#d6d6d6', - colorNeutralStroke4Selected: '#ebebeb', - colorNeutralForeground5: '#616161', - colorNeutralForeground5Hover: '#242424', - colorNeutralForeground5Pressed: '#242424', -}; +import type { Preview, StoryContext, StoryFn } from '@storybook/react'; +import { DEFAULT_THEME, THEMES, type ThemeId } from './themes'; +import './docs-theme-fill.css'; const preview: Preview = { ...rootPreview, + globalTypes: { + theme: { + description: 'Theme used to render the components', + toolbar: { + title: 'Theme', + icon: 'paintbrush', + items: (Object.keys(THEMES) as ThemeId[]).map((value) => ({ + value, + title: THEMES[value].title, + })), + dynamicTitle: true, + }, + }, + }, + initialGlobals: { + theme: DEFAULT_THEME, + }, decorators: [ - (Story: StoryFn): JSXElement => ( - - - - ), + (Story: StoryFn, context: StoryContext): JSXElement => { + const themeId = (context.globals.theme as ThemeId) ?? DEFAULT_THEME; + const { theme, styleHooks } = THEMES[themeId] ?? THEMES[DEFAULT_THEME]; + + return ( + + + + ); + }, ], tags: ['autodocs'], }; diff --git a/packages/react-cap-theme/.storybook/themes.ts b/packages/react-cap-theme/.storybook/themes.ts new file mode 100644 index 00000000..47c2e685 --- /dev/null +++ b/packages/react-cap-theme/.storybook/themes.ts @@ -0,0 +1,62 @@ +import { + type Theme, + webDarkTheme, + webLightTheme, +} from '@fluentui/react-components'; +import type { FluentProviderProps } from '@fluentui/react-components'; +import { CAP_STYLE_HOOKS } from '../src/index'; +import type { CAPTokens } from '../src/components/tokens/types'; + +const capLightTokens: CAPTokens = { + borderRadius2XLarge: '12px', + borderRadius3XLarge: '16px', + borderRadius4XLarge: '24px', + colorNeutralStroke4: '#ebebeb', + colorNeutralStroke4Hover: '#e0e0e0', + colorNeutralStroke4Pressed: '#d6d6d6', + colorNeutralStroke4Selected: '#ebebeb', + colorNeutralForeground5: '#616161', + colorNeutralForeground5Hover: '#242424', + colorNeutralForeground5Pressed: '#242424', +}; + +const capDarkTokens: CAPTokens = { + borderRadius2XLarge: '12px', + borderRadius3XLarge: '16px', + borderRadius4XLarge: '24px', + colorNeutralStroke4: '#3d3d3d', + colorNeutralStroke4Hover: '#444444', + colorNeutralStroke4Pressed: '#4a4a4a', + colorNeutralStroke4Selected: '#3d3d3d', + colorNeutralForeground5: '#adadad', + colorNeutralForeground5Hover: '#ffffff', + colorNeutralForeground5Pressed: '#ffffff', +}; + +const capLightTheme: Theme = { ...webLightTheme, ...capLightTokens }; +const capDarkTheme: Theme = { ...webDarkTheme, ...capDarkTokens }; + +export type ThemeId = 'fluentLight' | 'fluentDark' | 'capLight' | 'capDark'; + +type ThemeConfig = { + title: string; + theme: Theme; + styleHooks?: FluentProviderProps['customStyleHooks_unstable']; +}; + +export const THEMES: Record = { + fluentLight: { title: 'Fluent UI Light', theme: webLightTheme }, + fluentDark: { title: 'Fluent UI Dark', theme: webDarkTheme }, + capLight: { + title: 'CAP Light', + theme: capLightTheme, + styleHooks: CAP_STYLE_HOOKS, + }, + capDark: { + title: 'CAP Dark', + theme: capDarkTheme, + styleHooks: CAP_STYLE_HOOKS, + }, +}; + +export const DEFAULT_THEME: ThemeId = 'capLight';