From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../storybook/.storybook/markdown-story-loader.js | 133 +++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 browser/components/storybook/.storybook/markdown-story-loader.js (limited to 'browser/components/storybook/.storybook/markdown-story-loader.js') diff --git a/browser/components/storybook/.storybook/markdown-story-loader.js b/browser/components/storybook/.storybook/markdown-story-loader.js new file mode 100644 index 0000000000..150db5639e --- /dev/null +++ b/browser/components/storybook/.storybook/markdown-story-loader.js @@ -0,0 +1,133 @@ +/* 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/. */ +/* eslint-env node */ + +/** + * This file contains a Webpack loader that takes markdown as its source and + * outputs a docs only MDX Storybook story. This enables us to write docs only + * pages in plain markdown by specifying a `.stories.md` extension. + * + * For more context on docs only stories, see: + * https://storybook.js.org/docs/web-components/writing-docs/mdx#documentation-only-mdx + * + * The MDX generated by the loader will then get run through the same loaders + * Storybook usually uses to transform MDX files. + */ + +const path = require("path"); +const fs = require("fs"); + +const projectRoot = path.resolve(__dirname, "../../../../"); + +/** + * Takes a file path and returns a string to use as the story title, capitalized + * and split into multiple words. The file name gets transformed into the story + * name, which will be visible in the Storybook sidebar. For example, either: + * + * /stories/hello-world.stories.md or /stories/helloWorld.md + * + * will result in a story named "Hello World". + * + * @param {string} filePath - path of the file being processed. + * @returns {string} The title of the story. + */ +function getStoryTitle(filePath) { + let fileName = path.basename(filePath, ".stories.md"); + if (fileName != "README") { + try { + let relatedFilePath = path.resolve( + "../../../", + filePath.replace(".md", ".mjs") + ); + let relatedFile = fs.readFileSync(relatedFilePath).toString(); + let relatedTitle = relatedFile.match(/title: "(.*)"/)[1]; + if (relatedTitle) { + return relatedTitle + "/README"; + } + } catch {} + } + return separateWords(fileName); +} + +/** + * Splits a string into multiple capitalized words e.g. hello-world, helloWorld, + * and hello.world all become "Hello World." + * @param {string} str - String in any case. + * @returns {string} The string split into multiple words. + */ +function separateWords(str) { + return ( + str + .match(/[A-Z]?[a-z0-9]+/g) + ?.map(text => text[0].toUpperCase() + text.substring(1)) + .join(" ") || str + ); +} + +/** + * Enables rendering code in our markdown docs by parsing the source for + * annotated code blocks and replacing them with Storybook's Canvas component. + * @param {string} source - Stringified markdown source code. + * @returns {string} Source with code blocks replaced by Canvas components. + */ +function parseStoriesFromMarkdown(source) { + let storiesRegex = /```(?:js|html) story\n(?[\s\S]*?)```/g; + // $code comes from the capture group in the regex above. It consists + // of any code in between backticks and gets run when used in a Canvas component. + return source.replace( + storiesRegex, + "$" + ); +} + +/** + * The WebpackLoader export. Takes markdown as its source and returns a docs + * only MDX story. Falls back to filing stories under "Docs" for everything + * outside of `toolkit/content/widgets`. + * + * @param {string} source - The markdown source to rewrite to MDX. + */ +module.exports = function markdownStoryLoader(source) { + // Currently we sort docs only stories under "Docs" by default. + let storyPath = "Docs"; + + // `this.resourcePath` is the path of the file being processed. + let relativePath = path + .relative(projectRoot, this.resourcePath) + .replaceAll(path.sep, "/"); + + if (relativePath.includes("toolkit/content/widgets")) { + let storyNameRegex = /(?<=\/widgets\/)(?.*?)(?=\/)/g; + let componentName = storyNameRegex.exec(relativePath)?.groups?.name; + if (componentName) { + // Get the common name for a component e.g. Toggle for moz-toggle + storyPath = + "UI Widgets/" + separateWords(componentName).replace(/^Moz/g, ""); + } + } + + let storyTitle = getStoryTitle(relativePath); + let title = storyTitle.includes("/") + ? storyTitle + : `${storyPath}/${storyTitle}`; + + // Unfortunately the indentation/spacing here seems to be important for the + // MDX parser to know what to do in the next step of the Webpack process. + let mdxSource = ` +import { Meta, Description, Canvas } from "@storybook/addon-docs"; + + + +${parseStoriesFromMarkdown(source)}`; + + return mdxSource; +}; -- cgit v1.2.3