From 419d7609ad9ce151f8f544c891d55e3a395c13d1 Mon Sep 17 00:00:00 2001 From: Makoto Morimoto Date: Mon, 4 Dec 2023 03:09:43 -0800 Subject: [PATCH] feat(docs): Adding global direction switch to dynamically change between ltr and rtl in storybook (#29954) * feat(docs): Adding global direction switch to dynamically change between ltr and rtl in storybook. * Updating API. * Moving dir switch to the same line as theme picker. --- .../src/DocsComponents/DirSwitch.stories.tsx | 50 +++++++++++++++++++ .../DocsComponents/FluentDocsPage.stories.tsx | 16 ++++-- .../etc/react-storybook-addon.api.md | 5 ++ .../react-storybook-addon/src/constants.ts | 3 +- .../src/decorators/withFluentProvider.tsx | 5 +- .../react-storybook-addon/src/hooks.ts | 3 +- .../react-storybook-addon/src/index.ts | 2 +- 7 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 apps/public-docsite-v9/src/DocsComponents/DirSwitch.stories.tsx diff --git a/apps/public-docsite-v9/src/DocsComponents/DirSwitch.stories.tsx b/apps/public-docsite-v9/src/DocsComponents/DirSwitch.stories.tsx new file mode 100644 index 0000000000000..8c5a303d9dddd --- /dev/null +++ b/apps/public-docsite-v9/src/DocsComponents/DirSwitch.stories.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import { makeStyles, Label, Switch, useId, typographyStyles } from '@fluentui/react-components'; +import type { SwitchProps } from '@fluentui/react-components'; +import { DIR_ID } from '@fluentui/react-storybook-addon'; +import addons from '@storybook/addons'; + +const useStyles = makeStyles({ + container: { + alignItems: 'center', + display: 'flex', + justifyContent: 'start', + }, + label: { + ...typographyStyles.subtitle2, + }, +}); + +/** + * Dir switch used in the react-components docs header + */ +export const DirSwitch: React.FC<{ dir?: 'ltr' | 'rtl' }> = ({ dir }) => { + const switchId = useId('dir-switch'); + + const styles = useStyles(); + + const [currentDir, setCurrentDir] = React.useState(dir); + const checked = currentDir === 'rtl'; + + const setGlobalDir = (newDir: 'ltr' | 'rtl'): void => { + addons.getChannel().emit('updateGlobals', { globals: { [DIR_ID]: newDir } }); + }; + + const onChange = React.useCallback>((_, data) => { + const newDir = data.checked ? 'rtl' : 'ltr'; + setGlobalDir(newDir); + setCurrentDir(newDir); + }, []); + + return ( +
+ + + +
+ ); +}; diff --git a/apps/public-docsite-v9/src/DocsComponents/FluentDocsPage.stories.tsx b/apps/public-docsite-v9/src/DocsComponents/FluentDocsPage.stories.tsx index 2451890d474e7..dbab08e8835e5 100644 --- a/apps/public-docsite-v9/src/DocsComponents/FluentDocsPage.stories.tsx +++ b/apps/public-docsite-v9/src/DocsComponents/FluentDocsPage.stories.tsx @@ -11,9 +11,11 @@ import { Stories, } from '@storybook/addon-docs'; import { makeStyles, shorthands } from '@griffel/react'; -import { Toc, nameToHash } from './Toc.stories'; -import { THEME_ID, themes } from '@fluentui/react-storybook-addon'; +import { tokens } from '@fluentui/react-components'; +import { DIR_ID, THEME_ID, themes } from '@fluentui/react-storybook-addon'; +import { DirSwitch } from './DirSwitch.stories'; import { ThemePicker } from './ThemePicker.stories'; +import { Toc, nameToHash } from './Toc.stories'; const useStyles = makeStyles({ divider: { @@ -38,11 +40,16 @@ const useStyles = makeStyles({ width: '200px', flexGrow: 1, }, + globalTogglesContainer: { + columnGap: tokens.spacingHorizontalXXXL, + display: 'flex', + }, }); export const FluentDocsPage = () => { const context = React.useContext(DocsContext); + const dir = context.parameters?.dir ?? context.globals?.[DIR_ID] ?? 'ltr'; const selectedTheme = themes.find(theme => theme.id === context.globals![THEME_ID]); const stories = context.componentStories(); const primaryStory = stories[0]; @@ -65,7 +72,10 @@ export const FluentDocsPage = () => {
- +
+ + +

diff --git a/packages/react-components/react-storybook-addon/etc/react-storybook-addon.api.md b/packages/react-components/react-storybook-addon/etc/react-storybook-addon.api.md index 64534c1212fec..952e79d996d90 100644 --- a/packages/react-components/react-storybook-addon/etc/react-storybook-addon.api.md +++ b/packages/react-components/react-storybook-addon/etc/react-storybook-addon.api.md @@ -10,8 +10,13 @@ import { Parameters as Parameters_2 } from '@storybook/addons'; import { StoryContext } from '@storybook/addons'; import type { Theme } from '@fluentui/react-theme'; +// @public (undocumented) +export const DIR_ID: "storybook_fluentui-react-addon_dir"; + // @public export interface FluentGlobals extends Args { + // (undocumented) + [DIR_ID]?: 'ltr' | 'rtl'; // (undocumented) [STRICT_MODE_ID]?: boolean; // (undocumented) diff --git a/packages/react-components/react-storybook-addon/src/constants.ts b/packages/react-components/react-storybook-addon/src/constants.ts index 130aa23658512..277499ae0c169 100644 --- a/packages/react-components/react-storybook-addon/src/constants.ts +++ b/packages/react-components/react-storybook-addon/src/constants.ts @@ -1,4 +1,5 @@ export const ADDON_ID = 'storybook_fluentui-react-addon'; -export const THEME_ID = `${ADDON_ID}_theme` as const; +export const DIR_ID = `${ADDON_ID}_dir` as const; export const STRICT_MODE_ID = `${ADDON_ID}_strict-mode` as const; +export const THEME_ID = `${ADDON_ID}_theme` as const; diff --git a/packages/react-components/react-storybook-addon/src/decorators/withFluentProvider.tsx b/packages/react-components/react-storybook-addon/src/decorators/withFluentProvider.tsx index 8d513eb8961f6..a13d68508160d 100644 --- a/packages/react-components/react-storybook-addon/src/decorators/withFluentProvider.tsx +++ b/packages/react-components/react-storybook-addon/src/decorators/withFluentProvider.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { FluentProvider } from '@fluentui/react-provider'; import { Theme } from '@fluentui/react-theme'; import { themes, defaultTheme, ThemeIds } from '../theme'; -import { THEME_ID } from '../constants'; +import { DIR_ID, THEME_ID } from '../constants'; import { FluentGlobals, FluentStoryContext } from '../hooks'; const findTheme = (themeId?: ThemeIds) => { @@ -25,12 +25,13 @@ export const withFluentProvider = (StoryFn: () => JSX.Element, context: FluentSt const { mode } = parameters; const isVrTest = mode === 'vr-test'; + const dir = parameters.dir ?? globals[DIR_ID] ?? 'ltr'; const globalTheme = getActiveFluentTheme(globals); const paramTheme = findTheme(parameters.fluentTheme); const { theme } = paramTheme ?? globalTheme; return ( - + {isVrTest ? StoryFn() : {StoryFn()}} ); diff --git a/packages/react-components/react-storybook-addon/src/hooks.ts b/packages/react-components/react-storybook-addon/src/hooks.ts index 4d5c9efc32112..bfdd1b644c9ae 100644 --- a/packages/react-components/react-storybook-addon/src/hooks.ts +++ b/packages/react-components/react-storybook-addon/src/hooks.ts @@ -1,7 +1,7 @@ import { useGlobals as useStorybookGlobals, Args as StorybookArgs } from '@storybook/api'; import { StoryContext as StorybookContext, Parameters } from '@storybook/addons'; -import { STRICT_MODE_ID, THEME_ID } from './constants'; +import { DIR_ID, STRICT_MODE_ID, THEME_ID } from './constants'; import type { ThemeIds } from './theme'; export interface FluentStoryContext extends StorybookContext { @@ -13,6 +13,7 @@ export interface FluentStoryContext extends StorybookContext { * Extends the storybook globals object to include fluent specific properties */ export interface FluentGlobals extends StorybookArgs { + [DIR_ID]?: 'ltr' | 'rtl'; [THEME_ID]?: ThemeIds; [STRICT_MODE_ID]?: boolean; } diff --git a/packages/react-components/react-storybook-addon/src/index.ts b/packages/react-components/react-storybook-addon/src/index.ts index 7e02b8967b3ea..cfd28195b43e8 100644 --- a/packages/react-components/react-storybook-addon/src/index.ts +++ b/packages/react-components/react-storybook-addon/src/index.ts @@ -1,5 +1,5 @@ export type { FluentGlobals, FluentParameters, FluentStoryContext } from './hooks'; export type { ThemeIds } from './theme'; export { themes } from './theme'; -export { THEME_ID } from './constants'; +export { DIR_ID, THEME_ID } from './constants'; export { parameters } from './hooks';