Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -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"
}
16 changes: 16 additions & 0 deletions packages/react-cap-theme/.storybook/docs-theme-fill.css
Original file line number Diff line number Diff line change
@@ -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;
}
61 changes: 31 additions & 30 deletions packages/react-cap-theme/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -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<keyof Theme & keyof CAPTokens, string> = {
...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 => (
<FluentProvider
theme={capTheme as any}
customStyleHooks_unstable={CAP_STYLE_HOOKS}
>
<Story />
</FluentProvider>
),
(Story: StoryFn, context: StoryContext): JSXElement => {
const themeId = (context.globals.theme as ThemeId) ?? DEFAULT_THEME;
const { theme, styleHooks } = THEMES[themeId] ?? THEMES[DEFAULT_THEME];

return (
<FluentProvider theme={theme} customStyleHooks_unstable={styleHooks}>
<Story />
</FluentProvider>
);
},
],
tags: ['autodocs'],
};
Expand Down
62 changes: 62 additions & 0 deletions packages/react-cap-theme/.storybook/themes.ts
Original file line number Diff line number Diff line change
@@ -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<ThemeId, ThemeConfig> = {
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,
Comment on lines +48 to +58

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

With this configuration when switching from fluent{Light/Dark} to cap{Light/Dark} theme, stories crashes.

Fluent{Light/Dark} entries do not set any custom style hooks, so Fluent components fall back to internal noop style hooks (zero React hooks called inside). When the theme switcher selects CAP{Light/Dark}, it flips that prop (customStyleHooks_unstable) on the same FluentProvider, so the same runs more hooks under CAP than under Fluent (and vice versa). React reconciles the same instance with a different hook count and crashes ("Rendered more/fewer hooks than expected") → white screen in the canvas.

Note: on the Docs page the components still render — each example remounts inside Storybook's per-example error boundary, so the crash is swallowed visually — but the error is still thrown and logged in the browser DevTools console. The Default (component canvas) stories have no such boundary, so they crash completely with a white screen.

Fix:
Drop the fluentLight / fluentDark entries and keep only the CAP ones:

Suggested change
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,
capLight: {
title: 'CAP Light',
theme: capLightTheme,
styleHooks: CAP_STYLE_HOOKS,
},
capDark: {
title: 'CAP Dark',
theme: capDarkTheme,
styleHooks: CAP_STYLE_HOOKS,

Rationale: The tokens in capLightTokens and capDarkTokens that extend webLightTheme / webDarkTheme are already part of Fluent UI core's token set in recent versions. They only need to be patched in here because the contrib package is pinned to an older @fluentui/react-components. Once that dependency is bumped, these capLightTokens / capDarkTokens overrides become redundant and can be deleted — capLightTheme would collapse to webLightTheme (same for dark), so the stories would render identically with no manual token patching needed.

},
};

export const DEFAULT_THEME: ThemeId = 'capLight';
Loading