Skip to content
Merged
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
58 changes: 39 additions & 19 deletions 2nd-gen/packages/swc/.storybook/decorators/language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,31 @@ 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(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 {
const langAttr = lang ? String(lang) : 'en-US';
function applyLanguageAndFontKit(
lang: string | false,
textDirection: 'ltr' | 'rtl'
): void {
const langAttr = lang || 'en-US';
const root = document.documentElement;

// Set the language on the document root and track if it has changed
Expand All @@ -33,7 +48,8 @@ function applyLanguageAndFontKit(lang: string | false, isRTL: boolean): void {
root.setAttribute('lang', langAttr);
hasChanged = true;
}
root.setAttribute('dir', isRTL ? 'rtl' : 'ltr');

root.setAttribute('dir', textDirection);

// If the fonts are actively loading, do not re-trigger the load
if (window.FontsLoading === true) {
Expand Down Expand Up @@ -173,35 +189,39 @@ 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';
const resolvedTextDirection = resolveTextDirection(lang, textDirection);

attachLanguageChangeListener();

useEffect(() => {
applyLanguageAndFontKit(lang, isRTL);
}, [lang, id, viewMode]);
applyLanguageAndFontKit(lang, resolvedTextDirection);
}, [lang, resolvedTextDirection, id, viewMode]);

return StoryFn(context);
},
Expand Down
18 changes: 18 additions & 0 deletions 2nd-gen/packages/swc/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,29 @@ const preview = {
dynamicTitle: true,
},
},
textDirection: {
name: 'Direction',
description:
'Text direction for stories. Auto follows language; LTR/RTL overrides it.',
defaultValue: 'auto',
type: 'string',
toolbar: {
title: 'Direction',
icon: 'transfer',
items: [
{ value: 'auto', title: 'Auto' },
{ value: 'ltr', title: 'LTR' },
{ value: 'rtl', title: 'RTL' },
],
dynamicTitle: true,
},
},
},
initialGlobals: {
theme: 'light',
scale: 'medium',
lang: 'en-US',
textDirection: 'auto',
},
decorators: [
withContext,
Expand Down
Loading