withPseudoLocalization.mjs (2974B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import { useEffect, useGlobals, useChannel } from "@storybook/preview-api"; 6 import { 7 DIRECTIONS, 8 DIRECTION_BY_STRATEGY, 9 UPDATE_STRATEGY_EVENT, 10 FLUENT_CHANGED, 11 } from "./constants.mjs"; 12 import { provideFluent } from "../fluent-utils.mjs"; 13 14 /** 15 * withPseudoLocalization is a Storybook decorator that handles emitting an 16 * event to update translations when a new pseudo localization strategy is 17 * applied. It also handles setting a "dir" attribute on the root element in the 18 * Storybook iframe. 19 * 20 * @param {Function} StoryFn - Provided by Storybook, used to render the story. 21 * @param {object} context - Provided by Storybook, data about the story. 22 * @returns {Function} StoryFn with a modified "dir" attr set. 23 */ 24 export const withPseudoLocalization = (StoryFn, context) => { 25 const [{ pseudoStrategy }] = useGlobals(); 26 const direction = DIRECTION_BY_STRATEGY[pseudoStrategy] || DIRECTIONS.ltr; 27 const isInDocs = context.viewMode === "docs"; 28 const emit = useChannel({}); 29 30 useEffect(() => { 31 if (pseudoStrategy) { 32 emit(UPDATE_STRATEGY_EVENT, pseudoStrategy); 33 } 34 }, [pseudoStrategy]); 35 36 useEffect(() => { 37 if (isInDocs) { 38 document.documentElement.setAttribute("dir", DIRECTIONS.ltr); 39 let storyElements = document.querySelectorAll(".docs-story"); 40 storyElements.forEach(element => element.setAttribute("dir", direction)); 41 } else { 42 document.documentElement.setAttribute("dir", direction); 43 } 44 }, [direction, isInDocs]); 45 46 return StoryFn(); 47 }; 48 49 /** 50 * withFluentStrings is a Storybook decorator that handles emitting an 51 * event to update the Fluent strings shown in the Fluent panel. 52 * 53 * @param {Function} StoryFn - Provided by Storybook, used to render the story. 54 * @param {object} context - Provided by Storybook, data about the story. 55 * @returns {Function} StoryFn unmodified. 56 */ 57 export const withFluentStrings = (StoryFn, context) => { 58 const [{ fluentStrings }, updateGlobals] = useGlobals(); 59 const emit = useChannel({}); 60 const fileName = context.component + ".ftl"; 61 let strings = []; 62 63 if (context.parameters?.fluent && fileName) { 64 if (fluentStrings.hasOwnProperty(fileName)) { 65 strings = fluentStrings[fileName]; 66 } else { 67 let resource = provideFluent(context.parameters.fluent, fileName); 68 for (let message of resource.body) { 69 strings.push([ 70 message.id, 71 [ 72 message.value, 73 ...Object.entries(message.attributes).map( 74 ([key, value]) => ` .${key} = ${value}` 75 ), 76 ].join("\n"), 77 ]); 78 } 79 updateGlobals({ 80 fluentStrings: { ...fluentStrings, [fileName]: strings }, 81 }); 82 } 83 } 84 85 emit(FLUENT_CHANGED, strings, fileName); 86 87 return StoryFn(); 88 };