UNPKG

orphic-cypress

Version:

Set of utilities and typescript transformers to cover storybook stories with cypress component tests

184 lines 5.76 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.segmentMdx = exports.safeKebabCase = exports.Fifo = void 0; const React = __importStar(require("react")); /** * Check if the first prop of a */ const isRawMd = (childProps) => { var _a, _b; return childProps.mdxType === "pre" && ((_b = (_a = childProps.children) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b.className) === "language-md"; }; /** * quick and dirty fifo * @private */ class Fifo { // TODO: babel was getting upset with `private` keyword constructor(limit = 50, _cache = new Map()) { this.limit = limit; this._cache = _cache; } get(key) { return this._cache.get(key); } set(key, val) { if (this._cache.size === this.limit) { this._cache.delete(this._cache.keys().next().value); } this._cache.set(key, val); return val; } } exports.Fifo = Fifo; const cache = new Fifo(); /** * simple kebab-case converter for space separated text, * returns undefined if str is undefined or null * @private */ const safeKebabCase = (str) => typeof str === "string" ? str.toLowerCase().replace(/ /g, "-") : null; exports.safeKebabCase = safeKebabCase; /** * Split up an MDX files into headers for easy use in multiple parts of documentation * or in multiple files, with some added perks. * * Currently, this breaks on any header such that a file like * ~~~md * # First Component * * Something * * ## Second Component * * ```md * # Second header description * This second component does stuff * ``` * ~~~ * becomes essentially * ```ts * { * "first-component": { * full: [<h1>First Component</h1>,<p>Something</p>], * body: [<p>Something</p>], * md: "", * }, * "second-component": { * full: [<h1>Second Component</h1>,<p>Other</p>], * body: [<code>....</code>], * md: "# Second header description\nThis second component does stuff", * }, * } * ``` * Although actually they'll be functions at those locations that also have those properties, * but is `() => full` at invocation. Note how it picks up md code blocks as raw text, suitable * for story descriptions. * * * Then you can use it like * ```ts * import mdx from "./some.mdx"; * const mdxObject = segmentMdx(mdx); * // define FirstComponent... * FirstComponent.parameters = { * docs: { * page: mdxObject['first-component'], * } * }; * // define SecondComponent... * SecondComponent.parameters = { * docs: { * story: { * description: mdxObject['second-component'].md, * } * } * }; * ``` * * And if you needed to combine them you could do something like * ```ts * docs: { * page: () => [ * ...mdxObject["first-component"].full, * ...mdxObject["second-component"].full, * ] * } * ``` * * Or, in an mdx file like so (real example): * ```md * import { Meta } from "@storybook/addon-docs"; * import readme from "../../README.md"; * import { segmentMdx } from "orphic-cypress"; * * <Meta title="MockRequests/Overview" /> * * <>{segmentMdx(readme)["intercepting-api-requests"].full}</> * * <-- more markdown --> * # Further afield * ``` * * Uses a dead simple FIFO cache of size 50 just to avoid thinking about memory consumption issues. */ const segmentMdx = (mdx, /** force skipping the cache */ force) => { const fromCache = !force && cache.get(mdx); if (fromCache) return fromCache; if (typeof mdx !== "function") return cache.set(mdx, {}); const rendered = mdx({}); let currentId = "file"; const collection = { file: { full: [], body: [], md: "" }, }; React.Children.forEach(rendered.props.children, (child) => { const childrenOfChild = child.props.children; if (/^h\d$/.test(child.props.mdxType)) { // not sure why exactly the id is sometimes already present currentId = child.props.id || (0, exports.safeKebabCase)(childrenOfChild) || "unknown"; collection[currentId] = { full: [child], body: [], md: "" }; } else if (collection[currentId]) { collection[currentId].full.push(child); collection[currentId].body.push(child); if (isRawMd(child.props)) { const rawMd = childrenOfChild.props.children; collection[currentId].md += rawMd; } } }); return cache.set(mdx, Object.fromEntries(Object.entries(collection).map(([k, v]) => [ k, Object.assign(() => v.full, v), ]))); }; exports.segmentMdx = segmentMdx; //# sourceMappingURL=segment-mdx.js.map