-
Notifications
You must be signed in to change notification settings - Fork 250
feat(docs): add RTL decorator #6188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,15 +15,30 @@ import { addons, makeDecorator, useEffect } from '@storybook/preview-api'; | |
| import type {} from '../storybook-env'; | ||
|
|
||
| const DEFAULT_KIT_ID = 'obc6cux'; | ||
| const RTL_LANGS = new Set(['ar', 'fa', 'he']); | ||
|
|
||
| let languageListenerAttached = false; | ||
|
|
||
| function resolveTextDirection( | ||
| lang: string | false, | ||
| textDirection: string | undefined | ||
| ): 'ltr' | 'rtl' { | ||
| if (textDirection === 'ltr' || textDirection === 'rtl') { | ||
| return textDirection; | ||
| } | ||
|
|
||
| return RTL_LANGS.has(String(lang ?? 'en-US')) ? 'rtl' : 'ltr'; | ||
| } | ||
|
|
||
| /** | ||
| * Applies the selected language (lang/dir) to the document and loads the corresponding | ||
| * Applies the selected language/dir to the document and loads the corresponding | ||
| * Adobe Fonts kit if needed. Safe to call from the decorator or from a toolbar listener | ||
| * so that font loading works even when the decorator does not re-run (e.g. on some docs pages). | ||
| */ | ||
| function applyLanguageAndFontKit(lang: string | false, isRTL: boolean): void { | ||
| function applyLanguageAndFontKit( | ||
| lang: string | false, | ||
| textDirection: 'ltr' | 'rtl' | ||
| ): void { | ||
| const langAttr = lang ? String(lang) : 'en-US'; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: This could also be updated to Also what happens if you are passed a invalid lang string? I know this is just storybook, but we might want to consider some stronger type safety.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we pass an invalid string here right now it will silently LTR. The set check is |
||
| const root = document.documentElement; | ||
|
|
||
|
|
@@ -33,7 +48,10 @@ function applyLanguageAndFontKit(lang: string | false, isRTL: boolean): void { | |
| root.setAttribute('lang', langAttr); | ||
| hasChanged = true; | ||
| } | ||
| root.setAttribute('dir', isRTL ? 'rtl' : 'ltr'); | ||
|
|
||
| // Apply both semantic and CSS direction so docs/stories and bidi behavior stay aligned. | ||
| root.setAttribute('dir', textDirection); | ||
| root.style.direction = textDirection; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated! |
||
|
|
||
| // If the fonts are actively loading, do not re-trigger the load | ||
| if (window.FontsLoading === true) { | ||
|
|
@@ -173,35 +191,41 @@ function attachLanguageChangeListener(): void { | |
| languageListenerAttached = true; | ||
| try { | ||
| const channel = addons.getChannel(); | ||
| channel.on('globalsUpdated', (payload: { globals?: { lang?: string } }) => { | ||
| const lang = payload?.globals?.lang ?? 'en-US'; | ||
| const isRTL = ['ar', 'fa', 'he'].includes(lang); | ||
| applyLanguageAndFontKit(lang, isRTL); | ||
| }); | ||
| channel.on( | ||
| 'globalsUpdated', | ||
| (payload: { globals?: { lang?: string; textDirection?: string } }) => { | ||
| const lang = payload?.globals?.lang ?? 'en-US'; | ||
| const textDirection = resolveTextDirection( | ||
| lang, | ||
| payload?.globals?.textDirection | ||
| ); | ||
| applyLanguageAndFontKit(lang, textDirection); | ||
| } | ||
| ); | ||
| } catch { | ||
| // Storybook event bus not available (e.g. in test env); decorator-only path still works. | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @type import('@storybook/csf').DecoratorFunction<import('@storybook/web-components').WebComponentsFramework> | ||
| **/ | ||
| export const withLanguageWrapper = makeDecorator({ | ||
| name: 'withLanguageWrapper', | ||
| parameterName: 'lang', | ||
| wrapper: (StoryFn, context) => { | ||
| const { globals: { lang = false } = {}, id, viewMode } = context; | ||
| const { | ||
| globals: { lang = false, textDirection = 'auto' } = {}, | ||
| id, | ||
| viewMode, | ||
| } = context; | ||
|
|
||
| // Add a textDirection property to the globals for use in the stories | ||
| // fa/Farsi is currently not included in the storybook toolbar map | ||
| const isRTL = ['ar', 'fa', 'he'].includes(lang); | ||
| context.globals.textDirection = isRTL ? 'rtl' : 'ltr'; | ||
| // Keep an explicit, resolved direction available for stories. | ||
| const resolvedTextDirection = resolveTextDirection(lang, textDirection); | ||
| context.globals.resolvedTextDirection = resolvedTextDirection; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be an anti-pattern of mutating
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right - no need for this now, so I removed it :) |
||
|
|
||
| attachLanguageChangeListener(); | ||
|
|
||
| useEffect(() => { | ||
| applyLanguageAndFontKit(lang, isRTL); | ||
| }, [lang, id, viewMode]); | ||
| applyLanguageAndFontKit(lang, resolvedTextDirection); | ||
| }, [lang, resolvedTextDirection, id, viewMode]); | ||
|
|
||
| return StoryFn(context); | ||
| }, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
??only short‑circuits onnull | undefined. Whenlang === falsethis evaluates toString(false) === 'false', which never matches RTL_LANGS. You get LTR for the right reason by accident. You can uselang || 'en-US'to mirror the convention used in applyLanguageAndFontKit (lang ? String(lang) : 'en-US')There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated!