tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

preview.mjs (5688B)


      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 { setCustomElementsManifest } from "@storybook/web-components";
      6 import { withActions } from "@storybook/addon-actions/decorator";
      7 import { css, html } from "lit.all.mjs";
      8 import { MozLitElement } from "toolkit/content/widgets/lit-utils.mjs";
      9 import customElementsManifest from "../custom-elements.json";
     10 import { insertFTLIfNeeded, connectFluent } from "./fluent-utils.mjs";
     11 import chromeMap from "./chrome-map.js";
     12 import DocsContainer from "./DocsContainer.mjs";
     13 
     14 // Base Fluent set up.
     15 connectFluent();
     16 
     17 // Any fluent imports should go through MozXULElement.insertFTLIfNeeded.
     18 window.MozXULElement = {
     19  insertFTLIfNeeded,
     20 };
     21 
     22 // Used to set prefs in unprivileged contexts.
     23 window.RPMSetPref = () => {
     24  /* NOOP */
     25 };
     26 window.RPMGetFormatURLPref = () => {
     27  /* NOOP */
     28 };
     29 
     30 /**
     31 * Function to automatically import reusable components into all stories. This
     32 * helps ensure that components composed of multiple `moz-` elements will render
     33 * correctly, since these elements would otherwise be lazily imported.
     34 */
     35 function importReusableComponents() {
     36  let sourceMap = chromeMap[2];
     37  let mozElements = new Set();
     38  for (let key of Object.keys(sourceMap)) {
     39    if (
     40      key.startsWith("dist/bin/chrome/toolkit/content/global/elements/moz-") &&
     41      key.endsWith(".mjs")
     42    ) {
     43      mozElements.add(key.split("/").pop().replace(".mjs", ""));
     44    }
     45  }
     46  mozElements.forEach(elementName => {
     47    // eslint-disable-next-line no-unsanitized/method
     48    import(`toolkit/content/widgets/${elementName}/${elementName}.mjs`);
     49  });
     50 
     51  // Manually import the two components that don't follow our naming conventions.
     52  import("toolkit/content/widgets/panel-list/panel-list.js");
     53  import("toolkit/content/widgets/named-deck.js");
     54 }
     55 importReusableComponents();
     56 
     57 /**
     58 * Wrapper component used to decorate all of our stories by providing access to
     59 * `in-content/common.css` without leaking styles that conflict Storybook's CSS.
     60 *
     61 * More information on decorators can be found at:
     62 * https://storybook.js.org/docs/web-components/writing-stories/decorators
     63 *
     64 * @property {Function} story
     65 *  Storybook uses this internally to render stories. We call `story` in our
     66 *  render function so that the story contents have the same shadow root as
     67 *  `with-common-styles` and styles from `in-content/common` get applied.
     68 * @property {object} context
     69 *  Another Storybook provided property containing additional data stories use
     70 *  to render. If we don't make this a reactive property Lit seems to optimize
     71 *  away any re-rendering of components inside `with-common-styles`.
     72 */
     73 class WithCommonStyles extends MozLitElement {
     74  static styles = css`
     75    :host {
     76      display: block;
     77      height: 100%;
     78      padding: 1rem;
     79      box-sizing: border-box;
     80    }
     81 
     82    :host,
     83    :root {
     84      font: message-box;
     85      font-size: var(--font-size-root);
     86      appearance: none;
     87      background-color: var(--background-color-canvas);
     88      color: var(--text-color);
     89      -moz-box-layout: flex;
     90    }
     91 
     92    :host {
     93      font-size: var(--font-size-root);
     94    }
     95 
     96    :host([theme="light"]) {
     97      color-scheme: light;
     98    }
     99 
    100    :host([theme="dark"]) {
    101      color-scheme: dark;
    102    }
    103  `;
    104 
    105  static properties = {
    106    story: { type: Function },
    107    context: { type: Object },
    108  };
    109 
    110  connectedCallback() {
    111    super.connectedCallback();
    112    this.classList.add("anonymous-content-host");
    113  }
    114 
    115  storyContent() {
    116    if (this.story) {
    117      return this.story();
    118    }
    119    return html` <slot></slot> `;
    120  }
    121 
    122  render() {
    123    return html`
    124      <link
    125        rel="stylesheet"
    126        href="chrome://global/skin/in-content/common.css"
    127      />
    128      ${this.storyContent()}
    129    `;
    130  }
    131 }
    132 customElements.define("with-common-styles", WithCommonStyles);
    133 
    134 // ---- theme helpers used by the decorator (hoisted once) ----
    135 const mql = window.matchMedia("(prefers-color-scheme: dark)");
    136 const resolveTheme = (selected /* 'light'|'dark'|'system' */) => {
    137  if (selected === "dark") {
    138    return "dark";
    139  }
    140  if (selected === "light") {
    141    return "light";
    142  }
    143  return mql.matches ? "dark" : "light"; // system
    144 };
    145 
    146 // Wrap all stories in `with-common-styles`.
    147 export default {
    148  decorators: [
    149    (story, context) => {
    150      const selectedTheme = context?.globals?.theme ?? "system";
    151      const resolvedTheme = resolveTheme(selectedTheme);
    152 
    153      return html`
    154        <with-common-styles
    155          .story=${story}
    156          .context=${context}
    157          theme=${resolvedTheme}
    158        ></with-common-styles>
    159      `;
    160    },
    161    withActions,
    162  ],
    163  parameters: {
    164    docs: {
    165      toc: {
    166        disable: false,
    167        headingSelector: "h2, h3",
    168        ignoreSelector: "h2.text-truncated-ellipsis, .toc-ignore",
    169        title: "On this page",
    170      },
    171      container: DocsContainer, // updates /docs iframe
    172    },
    173    options: { showPanel: true },
    174  },
    175  globalTypes: {
    176    theme: {
    177      description: "Global theme",
    178      defaultValue: "system",
    179      toolbar: {
    180        title: "Theme toggle",
    181        items: [
    182          {
    183            value: "light",
    184            title: "Light",
    185            icon: "circlehollow",
    186          },
    187          {
    188            value: "dark",
    189            title: "Dark",
    190            icon: "circle",
    191          },
    192          {
    193            value: "system",
    194            title: "System",
    195            icon: "mirror",
    196          },
    197        ],
    198        dynamicTitle: true,
    199      },
    200    },
    201  },
    202 };
    203 
    204 // Enable props tables documentation.
    205 setCustomElementsManifest(customElementsManifest);