summaryrefslogtreecommitdiffstats
path: root/browser/components/storybook/.storybook/addon-pseudo-localization/withPseudoLocalization.mjs
blob: 9d6c62af380d0bc67778f6e549c802593071e038 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { useEffect, useGlobals, addons } from "@storybook/addons";
import {
  DIRECTIONS,
  DIRECTION_BY_STRATEGY,
  UPDATE_STRATEGY_EVENT,
  FLUENT_CHANGED,
} from "./constants.mjs";
import { provideFluent } from "../fluent-utils.mjs";

/**
 * withPseudoLocalization is a Storybook decorator that handles emitting an
 * event to update translations when a new pseudo localization strategy is
 * applied. It also handles setting a "dir" attribute on the root element in the
 * Storybook iframe.
 *
 * @param {Function} StoryFn - Provided by Storybook, used to render the story.
 * @param {Object} context - Provided by Storybook, data about the story.
 * @returns {Function} StoryFn with a modified "dir" attr set.
 */
export const withPseudoLocalization = (StoryFn, context) => {
  const [{ pseudoStrategy }] = useGlobals();
  const direction = DIRECTION_BY_STRATEGY[pseudoStrategy] || DIRECTIONS.ltr;
  const isInDocs = context.viewMode === "docs";
  const channel = addons.getChannel();

  useEffect(() => {
    if (pseudoStrategy) {
      channel.emit(UPDATE_STRATEGY_EVENT, pseudoStrategy);
    }
  }, [pseudoStrategy]);

  useEffect(() => {
    if (isInDocs) {
      document.documentElement.setAttribute("dir", DIRECTIONS.ltr);
      let storyElements = document.querySelectorAll(".docs-story");
      storyElements.forEach(element => element.setAttribute("dir", direction));
    } else {
      document.documentElement.setAttribute("dir", direction);
    }
  }, [direction, isInDocs]);

  return StoryFn();
};

/**
 * withFluentStrings is a Storybook decorator that handles emitting an
 * event to update the Fluent strings shown in the Fluent panel.
 *
 * @param {Function} StoryFn - Provided by Storybook, used to render the story.
 * @param {Object} context - Provided by Storybook, data about the story.
 * @returns {Function} StoryFn unmodified.
 */
export const withFluentStrings = (StoryFn, context) => {
  const [{ fluentStrings }, updateGlobals] = useGlobals();
  const channel = addons.getChannel();

  const fileName = context.component + ".ftl";
  let strings = [];

  if (context.parameters?.fluent && fileName) {
    if (fluentStrings.hasOwnProperty(fileName)) {
      strings = fluentStrings[fileName];
    } else {
      let resource = provideFluent(context.parameters.fluent, fileName);
      for (let message of resource.body) {
        strings.push([
          message.id,
          [
            message.value,
            ...Object.entries(message.attributes).map(
              ([key, value]) => `  .${key} = ${value}`
            ),
          ].join("\n"),
        ]);
      }
      updateGlobals({
        fluentStrings: { ...fluentStrings, [fileName]: strings },
      });
    }
  }

  channel.emit(FLUENT_CHANGED, strings);

  return StoryFn();
};