├── .eslintignore ├── .eslintrc.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .storybook ├── local-preset.js ├── main.ts ├── preview-head.html └── preview.ts ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── image.gif ├── manager.js ├── package-lock.json ├── package.json ├── preview.js ├── screenshot.png ├── scripts ├── eject-typescript.js └── prepublish-checks.js ├── src ├── Panel.tsx ├── components │ └── PanelContent.tsx ├── constants.ts ├── index.ts ├── manager.ts ├── preview.ts ├── stories │ ├── Button.stories.ts │ ├── Button.tsx │ ├── Header.stories.ts │ ├── Header.tsx │ ├── Introduction.mdx │ ├── Page.stories.ts │ ├── Page.tsx │ ├── assets │ │ ├── code-brackets.svg │ │ ├── colors.svg │ │ ├── comments.svg │ │ ├── direction.svg │ │ ├── flow.svg │ │ ├── plugin.svg │ │ ├── repo.svg │ │ └── stackalt.svg │ ├── button.css │ ├── header.css │ └── page.css ├── types.ts └── withHTML.ts ├── tsconfig.json ├── tsup.config.ts └── vite.config.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | root: true 2 | plugins: 3 | - import 4 | - react 5 | extends: 6 | - eslint:recommended 7 | - plugin:import/recommended 8 | - plugin:react/recommended 9 | parserOptions: 10 | ecmaVersion: 2021 11 | ecmaFeatures: 12 | jsx: true 13 | sourceType: module 14 | settings: 15 | react: 16 | version: "17" 17 | env: 18 | browser: true 19 | es6: true 20 | node: true 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | storybook-static/ 4 | build-storybook.log 5 | .DS_Store 6 | .env -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | CHANGELOG.md -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.storybook/local-preset.js: -------------------------------------------------------------------------------- 1 | /** 2 | * to load the built addon in this test Storybook 3 | */ 4 | function previewAnnotations(entry = []) { 5 | return [...entry, require.resolve("../dist/preview.js")]; 6 | } 7 | 8 | function managerEntries(entry = []) { 9 | return [...entry, require.resolve("../dist/manager.js")]; 10 | } 11 | 12 | module.exports = { 13 | managerEntries, 14 | previewAnnotations, 15 | }; 16 | -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from "@storybook/react-vite"; 2 | const config: StorybookConfig = { 3 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 4 | addons: [ 5 | "@storybook/addon-links", 6 | "@storybook/addon-essentials", 7 | "@storybook/addon-interactions", 8 | "./local-preset.js", 9 | ], 10 | framework: { 11 | name: "@storybook/react-vite", 12 | options: {}, 13 | }, 14 | docs: { 15 | autodocs: "tag", 16 | }, 17 | }; 18 | export default config; 19 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import type { Preview } from "@storybook/react"; 2 | 3 | const preview: Preview = { 4 | parameters: { 5 | backgrounds: { 6 | default: "light", 7 | }, 8 | actions: { argTypesRegex: "^on[A-Z].*" }, 9 | controls: { 10 | matchers: { 11 | color: /(background|color)$/i, 12 | date: /Date$/, 13 | }, 14 | }, 15 | }, 16 | }; 17 | 18 | export default preview; 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### [6.1.1](https://github.com/whitespace-se/storybook-addon-html/compare/v6.1.0...v6.1.1) (2024-05-11) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * update syntax-highlighter language option and correct type reference ([#126](https://github.com/whitespace-se/storybook-addon-html/issues/126)) ([a653292](https://github.com/whitespace-se/storybook-addon-html/commit/a653292b8bac979d51ca7b48c956b6fe783b9b4f)) 7 | 8 | ## [6.1.0](https://github.com/whitespace-se/storybook-addon-html/compare/v6.0.5...v6.1.0) (2024-05-07) 9 | 10 | 11 | ### Features 12 | 13 | * add `paramKey` property to panel object. ([#120](https://github.com/whitespace-se/storybook-addon-html/issues/120)) ([1506cad](https://github.com/whitespace-se/storybook-addon-html/commit/1506cad8482a03959e585c7cd8dbe86c4dd16fde)) 14 | 15 | ### [6.0.5](https://github.com/whitespace-se/storybook-addon-html/compare/v6.0.4...v6.0.5) (2024-04-11) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * Remove unused preset file ([2a8299d](https://github.com/whitespace-se/storybook-addon-html/commit/2a8299d196fa3e19954bc30e9875aee230640cd1)) 21 | 22 | ### [6.0.4](https://github.com/whitespace-se/storybook-addon-html/compare/v6.0.3...v6.0.4) (2024-04-02) 23 | 24 | ### [6.0.3](https://github.com/whitespace-se/storybook-addon-html/compare/v6.0.2...v6.0.3) (2024-04-02) 25 | 26 | 27 | ### Bug Fixes 28 | 29 | * Replace all instances of "my addon" ([693c60e](https://github.com/whitespace-se/storybook-addon-html/commit/693c60eb28c802c330d0c184aa3fd53c0a4295d5)) 30 | 31 | ### [6.0.2](https://github.com/whitespace-se/storybook-addon-html/compare/v6.0.1...v6.0.2) (2024-03-25) 32 | 33 | 34 | ### Bug Fixes 35 | 36 | * Remove reference to preset ([f7997e8](https://github.com/whitespace-se/storybook-addon-html/commit/f7997e82f10aec49a5e716584c0c03cd44a0ddbe)) 37 | 38 | ### [6.0.1](https://github.com/whitespace-se/storybook-addon-html/compare/6.0.0...v6.0.1) (2024-03-25) 39 | 40 | ## [6.0.0](https://github.com/whitespace-se/storybook-addon-html/compare/v5.1.4...6.0.0) (2024-03-25) 41 | 42 | 43 | ### ⚠ BREAKING CHANGES 44 | 45 | * Rewrite in Typescript with support for Storybook 8 and Prettier 3 based on latest addon kit 46 | 47 | ### Code Refactoring 48 | 49 | * Rewrite in Typescript with support for Storybook 8 and Prettier 3 based on latest addon kit ([4963646](https://github.com/whitespace-se/storybook-addon-html/commit/4963646a1b08eeb19cda90c36d036c11921060c0)) 50 | 51 | ### [5.1.4](https://github.com/whitespace-se/storybook-addon-html/compare/v5.1.3...v5.1.4) (2023-03-13) 52 | 53 | 54 | ### Bug Fixes 55 | 56 | * Incompatibility with Storybook 7.0 ([4751ec7](https://github.com/whitespace-se/storybook-addon-html/commit/4751ec73ff55cd9fb7a7a4e452ae8524e42797be)) 57 | 58 | ### [5.1.3](https://github.com/whitespace-se/storybook-addon-html/compare/v5.1.2...v5.1.3) (2023-03-10) 59 | 60 | ### [5.1.2](https://github.com/whitespace-se/storybook-addon-html/compare/v5.1.1...v5.1.2) (2023-03-10) 61 | 62 | 63 | ### Bug Fixes 64 | 65 | * Replace removed hljs theme ([d7204aa](https://github.com/whitespace-se/storybook-addon-html/commit/d7204aa57f66e102ea19719786c29a7115d97a8e)) 66 | 67 | ### [5.1.1](https://github.com/whitespace-se/storybook-addon-html/compare/v5.1.0...v5.1.1) (2022-12-15) 68 | 69 | 70 | ### Bug Fixes 71 | 72 | * Outdated peer dep ([81e74f6](https://github.com/whitespace-se/storybook-addon-html/commit/81e74f658b8e60e691a6631061960fc130a187d0)) 73 | 74 | ## [5.1.0](https://github.com/whitespace-se/storybook-addon-html/compare/v5.0.3...v5.1.0) (2022-12-09) 75 | 76 | 77 | ### Features 78 | 79 | * Add transform option for custom manipulation ([e7a35b8](https://github.com/whitespace-se/storybook-addon-html/commit/e7a35b87e9afc8d377460ee131c9501a14810b97)) 80 | 81 | 82 | ### Bug Fixes 83 | 84 | * Match multiline comments ([c23952b](https://github.com/whitespace-se/storybook-addon-html/commit/c23952b75f0e471fd5c50d1a94a5540a9c6c7ad2)) 85 | 86 | 87 | ### Reverts 88 | 89 | * Revert "ci: Remove autoit/conventional-commits" ([74628b2](https://github.com/whitespace-se/storybook-addon-html/commit/74628b2b3be2abe3bce0d7137e63a9ca1aea87a6)) 90 | 91 | ### [5.0.3](https://github.com/whitespace-se/storybook-addon-html/compare/v5.0.2...v5.0.3) (2022-11-11) 92 | 93 | ### [5.0.2](https://github.com/whitespace-se/storybook-addon-html/compare/v5.0.1...v5.0.2) (2022-11-11) 94 | 95 | 96 | ### Features 97 | 98 | * Add `removeComments` option ([5b4a6e6](https://github.com/whitespace-se/storybook-addon-html/commit/5b4a6e6cef0c53b528903eefd3bebd23963e4aa1)), closes [#73](https://github.com/whitespace-se/storybook-addon-html/issues/73) [#64](https://github.com/whitespace-se/storybook-addon-html/issues/64) 99 | 100 | ### [5.0.1](https://github.com/whitespace-se/storybook-addon-html/compare/v5.0.0...v5.0.1) (2022-11-11) 101 | 102 | 103 | ### ⚠ BREAKING CHANGES 104 | 105 | * Complete rewrite based on the official addon kit structure 106 | 107 | ### Code Refactoring 108 | 109 | * Complete rewrite based on the official addon kit structure ([836d4b9](https://github.com/whitespace-se/storybook-addon-html/commit/836d4b97bdbe52a321210e7d612c53b4f3c0c771)) 110 | 111 | ## [5.0.0](https://github.com/whitespace-se/storybook-addon-html/compare/v4.2.0...v5.0.0) (2021-03-26) 112 | 113 | ## [4.2.0](https://github.com/whitespace-se/storybook-addon-html/compare/v4.1.0...v4.2.0) (2021-01-07) 114 | 115 | 116 | ### Features 117 | 118 | * allow empty comments to be removed ([5cf46c0](https://github.com/whitespace-se/storybook-addon-html/commit/5cf46c0a1ff31ae4cf4e5b9a000436368e8df77c)) 119 | * allow overriding `showLineNumbers` and `wrapLines` for the highlighter ([121ffb6](https://github.com/whitespace-se/storybook-addon-html/commit/121ffb6260f5f1567d612a8f616d300dec53ed66)), closes [#21](https://github.com/whitespace-se/storybook-addon-html/issues/21) 120 | 121 | ## [4.1.0](https://github.com/whitespace-se/storybook-addon-html/compare/v4.0.2...v4.1.0) (2021-01-07) 122 | 123 | ### [4.0.2](https://github.com/whitespace-se/storybook-addon-html/compare/v4.0.1...v4.0.2) (2020-12-14) 124 | 125 | ### [4.0.1](https://github.com/whitespace-se/storybook-addon-html/compare/v4.0.0...v4.0.1) (2020-12-11) 126 | 127 | 128 | ### Bug Fixes 129 | 130 | * show error message when trying to import framework-specific module ([1ef9e1b](https://github.com/whitespace-se/storybook-addon-html/commit/1ef9e1b68f7757558de8d25e47b50dcc8a96b5d7)), closes [#31](https://github.com/whitespace-se/storybook-addon-html/issues/31) 131 | 132 | ## [4.0.0](https://github.com/whitespace-se/storybook-addon-html/compare/v3.0.0...v4.0.0) (2020-12-10) 133 | 134 | 135 | ### ⚠ BREAKING CHANGES 136 | 137 | * add preset and support for att frameworks 138 | 139 | ### Features 140 | 141 | * add preset and support for att frameworks ([51a33c8](https://github.com/whitespace-se/storybook-addon-html/commit/51a33c8b23870dd47ac899b98fb92427329ab971)) 142 | 143 | 144 | ### Bug Fixes 145 | 146 | * assign peer deps ([ae0dabc](https://github.com/whitespace-se/storybook-addon-html/commit/ae0dabc8f316612a9dfa4fe50062667b90643416)) 147 | * **examples/react:** simplify Whitespace logo ([4884b04](https://github.com/whitespace-se/storybook-addon-html/commit/4884b0490c081104d896db7e7afdcf92fe893ffb)) 148 | * re-add support for options via parameters ([ed19b93](https://github.com/whitespace-se/storybook-addon-html/commit/ed19b93ec6c7f3776e5e16866c7a87694d6ec758)) 149 | 150 | ## [3.0.0](https://github.com/whitespace-se/storybook-addon-html/compare/v2.0.1...v3.0.0) (2020-12-09) 151 | 152 | ### [2.0.1](https://github.com/whitespace-se/storybook-addon-html/compare/v2.0.0...v2.0.1) (2020-09-09) 153 | 154 | ## [2.0.0](https://github.com/whitespace-se/storybook-addon-html/compare/v1.2.2...v2.0.0) (2020-08-21) 155 | 156 | 157 | ### ⚠ BREAKING CHANGES 158 | 159 | * **deps:** Minimum supported Storybook version is now 6.X.X 160 | 161 | ### Bug Fixes 162 | 163 | * ensure code is shown on first render ([0f033b8](https://github.com/whitespace-se/storybook-addon-html/commit/0f033b8c77178d18e700cd5f105b1fa336a65d51)) 164 | * pin “Copy” button to bottom of panel ([ed0c7d4](https://github.com/whitespace-se/storybook-addon-html/commit/ed0c7d43b236427fb66995256972a31807a98d7b)) 165 | 166 | 167 | ### Miscellaneous Chores 168 | 169 | * **deps:** upgrade to support Storybook 6 ([2b049c9](https://github.com/whitespace-se/storybook-addon-html/commit/2b049c9de87216cedd399876a7bcb38770ef9e40)) 170 | 171 | ### [1.2.2](https://github.com/whitespace-se/storybook-addon-html/compare/v1.2.0...v1.2.2) (2020-05-09) 172 | 173 | ## 1.2.0 (2020-01-07) 174 | 175 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Repository Structure 4 | 5 | This repository makes use of [Yarn Workspaces][yarn-workspaces] to install the 6 | dependencies for both the library itself _and_ the example applications found in 7 | the `examples` directory. 8 | 9 | You can find the source code for the library within the `addon` directory. 10 | 11 | Example applications -- which are useful for verifying changes -- can be found 12 | within `examples`. 13 | 14 | ## Getting Started 15 | 16 | Once you have the repository cloned, you'll want to install the dependencies 17 | using 18 | 19 | ``` 20 | yarn install 21 | ``` 22 | 23 | This will take care of setting up everything you need to get started! 24 | 25 | ## Building the library 26 | 27 | The library source is found within `addon`; referenced sub-directories or 28 | commands in this section are relative to that directory. 29 | 30 | The source-code for the library can be found within the `src` sub-directory. 31 | Babel is used to compile the output to the adjacent `out` directory, which is 32 | _not_ part of version control. This can be done by running 33 | 34 | ``` 35 | yarn build 36 | ``` 37 | 38 | Additionally, files exist within the root of the library that re-export code 39 | from the `out` directory. This allows for nice clean imports for users of the 40 | library without them needing to know about the `out` directory. 41 | 42 | ## Running Example Apps 43 | 44 | You can run `yarn start` from within any of the example applications to start 45 | Storybook with the library added to it. This can be helpful for checking that 46 | changes to the library look correct! 47 | 48 | ## Publishing Releases 49 | 50 | TBD! 51 | 52 | [yarn-workspaces]: https://classic.yarnpkg.com/en/docs/workspaces/ 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU Affero General Public License 2 | 3 | Copyright (c) 2022 Whitespace. 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as 7 | published by the Free Software Foundation, either version 3 of the 8 | License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see http://www.gnu.org/licenses/. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Storybook Addon HTML 2 | 3 | This addon for Storybook adds a tab that displays the compiled HTML for each 4 | story. 5 | 6 | ![Animated preview](https://raw.githubusercontent.com/whitespace-se/storybook-addon-html/master/image.gif) 7 | 8 | ## Requirements 9 | 10 | Version 6 of this addon requires Storybook 8 and Prettier 3. If you are still 11 | using Storybook 7, you can use version 5. 12 | 13 | As of version 7 of this addon, the `react-syntax-highlighter` dependency is no 14 | longer required. The addon will use the default syntax highlighter of Storybook, 15 | which also formats the code with Prettier. 16 | 17 | ## Getting Started 18 | 19 | Install the addon and its dependencies. 20 | 21 | With NPM: 22 | 23 | ```sh 24 | npm i --save-dev @whitespace/storybook-addon-html 25 | ``` 26 | 27 | With Yarn: 28 | 29 | ```sh 30 | yarn add -D @whitespace/storybook-addon-html 31 | ``` 32 | 33 | With PNPM: 34 | 35 | ```sh 36 | pnpm add -D @whitespace/storybook-addon-html 37 | ``` 38 | 39 | ### Register addon 40 | 41 | ```js 42 | // .storybook/main.js 43 | 44 | module.exports = { 45 | // ... 46 | addons: [ 47 | "@whitespace/storybook-addon-html", 48 | // ... 49 | ], 50 | }; 51 | ``` 52 | 53 | ## Usage 54 | 55 | You can override the wrapper element selector used to grab the component HTML. 56 | 57 | ```js 58 | export const parameters = { 59 | html: { 60 | root: "#my-custom-wrapper", // default: #root 61 | }, 62 | }; 63 | ``` 64 | 65 | Some frameworks put comments inside the HTML. If you want to remove these you 66 | can use the `removeComments` parameter. Set it to `true` to remove all comments 67 | or set it to a regular expression that matches the content of the comments you 68 | want to remove. 69 | 70 | ```js 71 | export const parameters = { 72 | html: { 73 | removeComments: /^\s*remove me\s*$/, // default: false 74 | }, 75 | }; 76 | ``` 77 | 78 | You can also use the `removeEmptyComments` parameter to remove only empty 79 | comments like `` and ``. 80 | 81 | ```js 82 | export const parameters = { 83 | html: { 84 | removeEmptyComments: true, // default: false 85 | }, 86 | }; 87 | ``` 88 | 89 | You can override the `showLineNumbers` and `wrapLines` settings for the syntax 90 | highlighter by using the `highlighter` parameter: 91 | 92 | ```js 93 | export const parameters = { 94 | html: { 95 | highlighter: { 96 | showLineNumbers: true, // default: false 97 | wrapLines: false, // default: true 98 | }, 99 | }, 100 | }; 101 | ``` 102 | 103 | Another way of hiding unwanted code is to define the `transform` option. It 104 | allows you to perform any change to the output code, e.g. removing attributes 105 | injected by frameworks. 106 | 107 | ```js 108 | html: { 109 | transform: (code) => { 110 | // Remove attributes `_nghost` and `ng-reflect` injected by Angular: 111 | return code.replace(/(?:_nghost|ng-reflect).*?="[\S\s]*?"/g, ""); 112 | }; 113 | } 114 | ``` 115 | 116 | You can disable the HTML panel by setting the `disable` parameter to true. 117 | This will hide and disable the HTML addon in your stories. 118 | 119 | ```js 120 | html: { 121 | disable: true, // default: false 122 | } 123 | ``` 124 | -------------------------------------------------------------------------------- /image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitespace-se/storybook-addon-html/6d9e79f3f5378f9bf25ca157da58fe4ed2d5fbec/image.gif -------------------------------------------------------------------------------- /manager.js: -------------------------------------------------------------------------------- 1 | import "./dist/manager"; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@whitespace/storybook-addon-html", 3 | "version": "7.0.0", 4 | "description": "A Storybook addon that extracts and displays compiled syntax-highlighted HTML", 5 | "keywords": [ 6 | "storybook-addon", 7 | "html", 8 | "code", 9 | "popular", 10 | "storybook-addons" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/whitespace-se/storybook-addon-html" 15 | }, 16 | "type": "module", 17 | "author": "Whitespace ", 18 | "license": "AGPL-3.0-or-later", 19 | "main": "dist/cjs/index.js", 20 | "module": "dist/esm/index.js", 21 | "types": "dist/ts/index.d.ts", 22 | "exports": { 23 | ".": { 24 | "types": "./dist/index.d.ts", 25 | "import": "./dist/index.js", 26 | "require": "./dist/index.cjs" 27 | }, 28 | "./preview": { 29 | "types": "./dist/index.d.ts", 30 | "import": "./dist/preview.js", 31 | "require": "./dist/preview.cjs" 32 | }, 33 | "./manager": "./dist/manager.js", 34 | "./package.json": "./package.json" 35 | }, 36 | "files": [ 37 | "dist/**/*", 38 | "README.md", 39 | "*.js", 40 | "*.d.ts" 41 | ], 42 | "scripts": { 43 | "build": "tsup", 44 | "build:watch": "npm run build -- --watch", 45 | "test": "echo \"Error: no test specified\" && exit 1", 46 | "start": "run-p build:watch 'storybook --quiet'", 47 | "prerelease": "zx scripts/prepublish-checks.js", 48 | "release": "npm run build && auto shipit", 49 | "eject-ts": "zx scripts/eject-typescript.js", 50 | "storybook": "storybook dev -p 6006", 51 | "build-storybook": "storybook build", 52 | "format": "run-s format:*", 53 | "format:eslint": "eslint --fix '**/*.{js,jsx}' || true", 54 | "format:prettier": "prettier --write '**/*.{js,jsx,json,md,mdx,yml,html,css}'", 55 | "lint": "eslint '**/*.{js,jsx}'" 56 | }, 57 | "dependencies": {}, 58 | "devDependencies": { 59 | "@storybook/addon-essentials": "^8.6.0", 60 | "@storybook/addon-interactions": "^8.6.0", 61 | "@storybook/addon-links": "^8.6.0", 62 | "@storybook/blocks": "^8.6.0", 63 | "@storybook/react": "^8.6.0", 64 | "@storybook/react-vite": "^8.6.0", 65 | "@storybook/test": "^8.6.0", 66 | "@storybook/types": "^8.6.0", 67 | "@types/node": "^18.15.0", 68 | "@types/react": "^18.2.65", 69 | "@types/react-dom": "^18.2.21", 70 | "@vitejs/plugin-react": "^4.2.1", 71 | "auto": "^11.1.1", 72 | "boxen": "^7.1.1", 73 | "dedent": "^1.5.1", 74 | "eslint": "^8.57.0", 75 | "eslint-plugin-import": "^2.29.1", 76 | "eslint-plugin-react": "^7.34.1", 77 | "npm-run-all": "^4.1.5", 78 | "prettier": "^3.0.0", 79 | "prompts": "^2.4.2", 80 | "react": "^18.2.0", 81 | "react-dom": "^18.2.0", 82 | "storybook": "^8.6.0", 83 | "tsup": "^8.0.2", 84 | "typescript": "^5.4.2", 85 | "vite": "^5.1.6", 86 | "zx": "^7.2.3" 87 | }, 88 | "peerDependencies": { 89 | "storybook": "^8.2.0" 90 | }, 91 | "publishConfig": { 92 | "access": "public" 93 | }, 94 | "bundler": { 95 | "exportEntries": [ 96 | "src/index.ts" 97 | ], 98 | "managerEntries": [ 99 | "src/manager.ts" 100 | ], 101 | "previewEntries": [ 102 | "src/preview.ts" 103 | ], 104 | "nodeEntries": [ 105 | "src/preset.ts" 106 | ] 107 | }, 108 | "storybook": { 109 | "displayName": "HTML Preview", 110 | "supportedFrameworks": [ 111 | "react", 112 | "vue", 113 | "angular", 114 | "web-components", 115 | "ember", 116 | "html", 117 | "svelte", 118 | "preact", 119 | "react-native" 120 | ], 121 | "icon": "https://avatars1.githubusercontent.com/u/10450088?s=400&u=b2a95469b7d2addf63366a93a17f0bd06f9894cc&v=4" 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /preview.js: -------------------------------------------------------------------------------- 1 | export * from "./dist/preview"; 2 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitespace-se/storybook-addon-html/6d9e79f3f5378f9bf25ca157da58fe4ed2d5fbec/screenshot.png -------------------------------------------------------------------------------- /scripts/eject-typescript.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | 3 | // Copy TS files and delete src 4 | await $`cp -r ./src ./srcTS`; 5 | await $`rm -rf ./src`; 6 | await $`mkdir ./src`; 7 | 8 | // Install Babel and TS preset 9 | console.log(chalk.green` 10 | 11 | 🔃 Installing dependencies... 12 | 13 | `); 14 | await $`npm install --save-dev @babel/cli @babel/preset-typescript --ignore-scripts`; 15 | 16 | // Convert TS code to JS 17 | await $`babel --no-babelrc --presets @babel/preset-typescript ./srcTS -d ./src --extensions \".js,.jsx,.ts,.tsx\" --ignore "./srcTS/typings.d.ts"`; 18 | 19 | // Format the newly created .js files 20 | console.log(chalk.green` 21 | 22 | 💅 Format the newly created .js files... 23 | 24 | `); 25 | await $`prettier --write ./src`; 26 | 27 | // Add in minimal files required for the TS build setup 28 | console.log(chalk.green` 29 | 30 | ➕ Add minimal files required for the TS build setup 31 | 32 | `); 33 | await $`prettier --write ./src`; 34 | await $`touch ./src/dummy.ts`; 35 | await $`printf "export {};" >> ./src/dummy.ts`; 36 | 37 | await $`touch ./src/typings.d.ts`; 38 | await $`printf 'declare module "global";' >> ./src/typings.d.ts`; 39 | 40 | // Clean up 41 | await $`rm -rf ./srcTS`; 42 | console.log(chalk.green` 43 | 44 | 🧹 Clean up... 45 | 46 | `); 47 | await $`npm uninstall @babel/cli @babel/preset-typescript --ignore-scripts`; 48 | 49 | console.log( 50 | chalk.green.bold` 51 | TypeScript Ejection complete!`, 52 | chalk.green` 53 | Addon code converted with JS. The TypeScript build setup is still available in case you want to adopt TypeScript in the future. 54 | `, 55 | ); 56 | -------------------------------------------------------------------------------- /scripts/prepublish-checks.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | 3 | import boxen from "boxen"; 4 | import dedent from "dedent"; 5 | import { readFile } from "node:fs/promises"; 6 | import { globalPackages as globalManagerPackages } from "storybook/internal/manager/globals"; 7 | import { globalPackages as globalPreviewPackages } from "storybook/internal/preview/globals"; 8 | 9 | const packageJson = await readFile("./package.json", "utf8").then(JSON.parse); 10 | 11 | const name = packageJson.name; 12 | const displayName = packageJson.storybook.displayName; 13 | 14 | let exitCode = 0; 15 | $.verbose = false; 16 | 17 | /** 18 | * Check that meta data has been updated 19 | */ 20 | if (name.includes("addon-kit") || displayName.includes("Addon Kit")) { 21 | console.error( 22 | boxen( 23 | dedent` 24 | ${chalk.red.bold("Missing metadata")} 25 | 26 | ${chalk.red(dedent`Your package name and/or displayName includes default values from the Addon Kit. 27 | The addon gallery filters out all such addons. 28 | 29 | Please configure appropriate metadata before publishing your addon. For more info, see: 30 | https://storybook.js.org/docs/react/addons/addon-catalog#addon-metadata`)}`, 31 | { padding: 1, borderColor: "red" }, 32 | ), 33 | ); 34 | 35 | exitCode = 1; 36 | } 37 | 38 | /** 39 | * Check that README has been updated 40 | */ 41 | const readmeTestStrings = 42 | "# Storybook Addon Kit|Click the \\*\\*Use this template\\*\\* button to get started.|https://user-images.githubusercontent.com/42671/106809879-35b32000-663a-11eb-9cdc-89f178b5273f.gif"; 43 | 44 | if ((await $`cat README.md | grep -E ${readmeTestStrings}`.exitCode) == 0) { 45 | console.error( 46 | boxen( 47 | dedent` 48 | ${chalk.red.bold("README not updated")} 49 | 50 | ${chalk.red(dedent`You are using the default README.md file that comes with the addon kit. 51 | Please update it to provide info on what your addon does and how to use it.`)} 52 | `, 53 | { padding: 1, borderColor: "red" }, 54 | ), 55 | ); 56 | 57 | exitCode = 1; 58 | } 59 | 60 | /** 61 | * Check that globalized packages are not incorrectly listed as peer dependencies 62 | */ 63 | const peerDependencies = Object.keys(packageJson.peerDependencies || {}); 64 | const globalPackages = [...globalManagerPackages, ...globalPreviewPackages]; 65 | peerDependencies.forEach((dependency) => { 66 | if (globalPackages.includes(dependency)) { 67 | console.error( 68 | boxen( 69 | dedent` 70 | ${chalk.red.bold("Unnecessary peer dependency")} 71 | 72 | ${chalk.red(dedent`You have a peer dependency on ${chalk.bold(dependency)} which is most likely unnecessary 73 | as that is provided by Storybook directly. 74 | Check the "bundling" section in README.md for more information. 75 | If you are absolutely sure you are doing it correct, you should remove this check from scripts/prepublish-checks.js.`)} 76 | `, 77 | { padding: 1, borderColor: "red" }, 78 | ), 79 | ); 80 | 81 | exitCode = 1; 82 | } 83 | }); 84 | 85 | process.exit(exitCode); 86 | -------------------------------------------------------------------------------- /src/Panel.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | useAddonState, 4 | useChannel, 5 | useParameter, 6 | } from "storybook/internal/manager-api"; 7 | import { AddonPanel } from "storybook/internal/components"; 8 | import { ADDON_ID, EVENTS, PARAM_KEY } from "./constants"; 9 | import { PanelContent } from "./components/PanelContent"; 10 | 11 | interface PanelProps { 12 | active: boolean; 13 | } 14 | 15 | export const Panel: React.FC = (props) => { 16 | // https://storybook.js.org/docs/react/addons/addons-api#useaddonstate 17 | const [{ code }, setState] = useAddonState(ADDON_ID, { 18 | code: null, 19 | }); 20 | 21 | // https://storybook.js.org/docs/react/addons/addons-api#usechannel 22 | useChannel({ 23 | [EVENTS.CODE_UPDATE]: ({ code }) => { 24 | setState((state) => ({ ...state, code })); 25 | }, 26 | }); 27 | 28 | const parameters = useParameter(PARAM_KEY, { 29 | highlighter: { showLineNumbers: false, wrapLines: true }, 30 | }); 31 | const { 32 | highlighter: { showLineNumbers = false, wrapLines = true } = {}, 33 | } = parameters; 34 | 35 | return ( 36 | 37 | 42 | 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/components/PanelContent.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SyntaxHighlighter } from "storybook/internal/components" 3 | 4 | interface PanelContentProps { 5 | code: string; 6 | showLineNumbers?: boolean; 7 | wrapLines?: boolean; 8 | } 9 | 10 | export const PanelContent: React.FC = ({ 11 | code, 12 | showLineNumbers = false, 13 | wrapLines = false, 14 | }) => ( 15 | 23 | {code} 24 | 25 | ); 26 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const ADDON_ID = "storybook/html"; 2 | export const PANEL_ID = `${ADDON_ID}/panel`; 3 | export const PARAM_KEY = `html`; 4 | 5 | export const EVENTS = { 6 | CODE_UPDATE: `${ADDON_ID}/codeUpdate`, 7 | }; 8 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // make it work with --isolatedModules 2 | export default {}; 3 | -------------------------------------------------------------------------------- /src/manager.ts: -------------------------------------------------------------------------------- 1 | import { addons, types } from "storybook/internal/manager-api"; 2 | import { ADDON_ID, PANEL_ID, PARAM_KEY } from "./constants"; 3 | import { Panel } from "./Panel"; 4 | 5 | // Register the addon 6 | addons.register(ADDON_ID, () => { 7 | // Register the panel 8 | addons.add(PANEL_ID, { 9 | type: types.PANEL, 10 | title: "HTML", 11 | match: ({ viewMode }) => viewMode === "story", 12 | render: Panel, 13 | paramKey: PARAM_KEY, 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/preview.ts: -------------------------------------------------------------------------------- 1 | import type { ProjectAnnotations, Renderer } from "storybook/internal/types"; 2 | import { withHTML } from "./withHTML"; 3 | 4 | const preview: ProjectAnnotations = { 5 | decorators: [withHTML], 6 | }; 7 | 8 | export default preview; 9 | -------------------------------------------------------------------------------- /src/stories/Button.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | 3 | import { Button } from "./Button"; 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export 6 | const meta: Meta = { 7 | title: "Example/Button", 8 | component: Button, 9 | // More on argTypes: https://storybook.js.org/docs/react/api/argtypes 10 | argTypes: { 11 | backgroundColor: { control: "color" }, 12 | }, 13 | tags: ["autodocs"], 14 | }; 15 | 16 | export default meta; 17 | type Story = StoryObj; 18 | 19 | // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args 20 | export const Primary: Story = { 21 | // More on args: https://storybook.js.org/docs/react/writing-stories/args 22 | args: { 23 | primary: true, 24 | label: "Button", 25 | }, 26 | }; 27 | 28 | export const Secondary: Story = { 29 | args: { 30 | label: "Button", 31 | }, 32 | }; 33 | 34 | export const Large: Story = { 35 | args: { 36 | size: "large", 37 | label: "Button", 38 | }, 39 | }; 40 | 41 | export const Small: Story = { 42 | args: { 43 | size: "small", 44 | label: "Button", 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /src/stories/Button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './button.css'; 3 | 4 | interface ButtonProps { 5 | /** 6 | * Is this the principal call to action on the page? 7 | */ 8 | primary?: boolean; 9 | /** 10 | * What background color to use 11 | */ 12 | backgroundColor?: string; 13 | /** 14 | * How large should the button be? 15 | */ 16 | size?: 'small' | 'medium' | 'large'; 17 | /** 18 | * Button contents 19 | */ 20 | label: string; 21 | /** 22 | * Optional click handler 23 | */ 24 | onClick?: () => void; 25 | } 26 | 27 | /** 28 | * Primary UI component for user interaction 29 | */ 30 | export const Button = ({ 31 | primary = false, 32 | size = 'medium', 33 | backgroundColor, 34 | label, 35 | ...props 36 | }: ButtonProps) => { 37 | const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; 38 | return ( 39 | 47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /src/stories/Header.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | import { Header } from "./Header"; 3 | 4 | const meta: Meta = { 5 | title: "Example/Header", 6 | component: Header, 7 | parameters: { 8 | // More on Story layout: https://storybook.js.org/docs/react/configure/story-layout 9 | layout: "fullscreen", 10 | }, 11 | }; 12 | 13 | export default meta; 14 | type Story = StoryObj; 15 | 16 | export const LoggedIn: Story = { 17 | args: { 18 | user: { 19 | name: "Jane Doe", 20 | }, 21 | }, 22 | }; 23 | 24 | export const LoggedOut: Story = {}; 25 | -------------------------------------------------------------------------------- /src/stories/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Button } from './Button'; 4 | import './header.css'; 5 | 6 | type User = { 7 | name: string; 8 | }; 9 | 10 | interface HeaderProps { 11 | user?: User; 12 | onLogin: () => void; 13 | onLogout: () => void; 14 | onCreateAccount: () => void; 15 | } 16 | 17 | export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => ( 18 |
19 |
20 |
21 | 22 | 23 | 27 | 31 | 35 | 36 | 37 |

Acme

38 |
39 |
40 | {user ? ( 41 | <> 42 | 43 | Welcome, {user.name}! 44 | 45 |
54 |
55 |
56 | ); 57 | -------------------------------------------------------------------------------- /src/stories/Introduction.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from "@storybook/blocks"; 2 | import Code from "./assets/code-brackets.svg"; 3 | import Colors from "./assets/colors.svg"; 4 | import Comments from "./assets/comments.svg"; 5 | import Direction from "./assets/direction.svg"; 6 | import Flow from "./assets/flow.svg"; 7 | import Plugin from "./assets/plugin.svg"; 8 | import Repo from "./assets/repo.svg"; 9 | import StackAlt from "./assets/stackalt.svg"; 10 | 11 | 12 | 13 | 116 | 117 | # Welcome to Storybook 118 | 119 | Storybook helps you build UI components in isolation from your app's business logic, data, and context. 120 | That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA. 121 | 122 | Browse example stories now by navigating to them in the sidebar. 123 | View their code in the `stories` directory to learn how they work. 124 | We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages. 125 | 126 |
Configure
127 | 128 | 174 | 175 |
Learn
176 | 177 | 215 | 216 |
217 | TipEdit the Markdown in{" "} 218 | stories/Introduction.stories.mdx 219 |
220 | -------------------------------------------------------------------------------- /src/stories/Page.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import { within, userEvent } from '@storybook/test'; 3 | 4 | import { Page } from './Page'; 5 | 6 | const meta: Meta = { 7 | title: 'Example/Page', 8 | component: Page, 9 | parameters: { 10 | // More on Story layout: https://storybook.js.org/docs/react/configure/story-layout 11 | layout: 'fullscreen', 12 | }, 13 | }; 14 | 15 | export default meta; 16 | type Story = StoryObj; 17 | 18 | export const LoggedOut: Story = {}; 19 | 20 | // More on interaction testing: https://storybook.js.org/docs/react/writing-tests/interaction-testing 21 | export const LoggedIn: Story = { 22 | play: async ({ canvasElement }) => { 23 | const canvas = within(canvasElement); 24 | const loginButton = await canvas.getByRole('button', { 25 | name: /Log in/i, 26 | }); 27 | await userEvent.click(loginButton); 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /src/stories/Page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Header } from './Header'; 4 | import './page.css'; 5 | 6 | type User = { 7 | name: string; 8 | }; 9 | 10 | export const Page: React.FC = () => { 11 | const [user, setUser] = React.useState(); 12 | 13 | return ( 14 |
15 |
setUser({ name: 'Jane Doe' })} 18 | onLogout={() => setUser(undefined)} 19 | onCreateAccount={() => setUser({ name: 'Jane Doe' })} 20 | /> 21 | 22 |
23 |

Pages in Storybook

24 |

25 | We recommend building UIs with a{' '} 26 | 27 | component-driven 28 | {' '} 29 | process starting with atomic components and ending with pages. 30 |

31 |

32 | Render pages with mock data. This makes it easy to build and review page states without 33 | needing to navigate to them in your app. Here are some handy patterns for managing page 34 | data in Storybook: 35 |

36 |
    37 |
  • 38 | Use a higher-level connected component. Storybook helps you compose such data from the 39 | "args" of child component stories 40 |
  • 41 |
  • 42 | Assemble data in the page component from your services. You can mock these services out 43 | using Storybook. 44 |
  • 45 |
46 |

47 | Get a guided tutorial on component-driven development at{' '} 48 | 49 | Storybook tutorials 50 | 51 | . Read more in the{' '} 52 | 53 | docs 54 | 55 | . 56 |

57 |
58 | Tip Adjust the width of the canvas with the{' '} 59 | 60 | 61 | 66 | 67 | 68 | Viewports addon in the toolbar 69 |
70 |
71 |
72 | ); 73 | }; 74 | -------------------------------------------------------------------------------- /src/stories/assets/code-brackets.svg: -------------------------------------------------------------------------------- 1 | illustration/code-brackets -------------------------------------------------------------------------------- /src/stories/assets/colors.svg: -------------------------------------------------------------------------------- 1 | illustration/colors -------------------------------------------------------------------------------- /src/stories/assets/comments.svg: -------------------------------------------------------------------------------- 1 | illustration/comments -------------------------------------------------------------------------------- /src/stories/assets/direction.svg: -------------------------------------------------------------------------------- 1 | illustration/direction -------------------------------------------------------------------------------- /src/stories/assets/flow.svg: -------------------------------------------------------------------------------- 1 | illustration/flow -------------------------------------------------------------------------------- /src/stories/assets/plugin.svg: -------------------------------------------------------------------------------- 1 | illustration/plugin -------------------------------------------------------------------------------- /src/stories/assets/repo.svg: -------------------------------------------------------------------------------- 1 | illustration/repo -------------------------------------------------------------------------------- /src/stories/assets/stackalt.svg: -------------------------------------------------------------------------------- 1 | illustration/stackalt -------------------------------------------------------------------------------- /src/stories/button.css: -------------------------------------------------------------------------------- 1 | .storybook-button { 2 | font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | font-weight: 700; 4 | border: 0; 5 | border-radius: 3em; 6 | cursor: pointer; 7 | display: inline-block; 8 | line-height: 1; 9 | } 10 | .storybook-button--primary { 11 | color: white; 12 | background-color: #1ea7fd; 13 | } 14 | .storybook-button--secondary { 15 | color: #333; 16 | background-color: transparent; 17 | box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; 18 | } 19 | .storybook-button--small { 20 | font-size: 12px; 21 | padding: 10px 16px; 22 | } 23 | .storybook-button--medium { 24 | font-size: 14px; 25 | padding: 11px 20px; 26 | } 27 | .storybook-button--large { 28 | font-size: 16px; 29 | padding: 12px 24px; 30 | } 31 | -------------------------------------------------------------------------------- /src/stories/header.css: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 4 | padding: 15px 20px; 5 | display: flex; 6 | align-items: center; 7 | justify-content: space-between; 8 | } 9 | 10 | svg { 11 | display: inline-block; 12 | vertical-align: top; 13 | } 14 | 15 | h1 { 16 | font-weight: 700; 17 | font-size: 20px; 18 | line-height: 1; 19 | margin: 6px 0 6px 10px; 20 | display: inline-block; 21 | vertical-align: top; 22 | } 23 | 24 | button + button { 25 | margin-left: 10px; 26 | } 27 | 28 | .welcome { 29 | color: #333; 30 | font-size: 14px; 31 | margin-right: 10px; 32 | } 33 | -------------------------------------------------------------------------------- /src/stories/page.css: -------------------------------------------------------------------------------- 1 | section { 2 | font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | font-size: 14px; 4 | line-height: 24px; 5 | padding: 48px 20px; 6 | margin: 0 auto; 7 | max-width: 600px; 8 | color: #333; 9 | } 10 | 11 | section h2 { 12 | font-weight: 700; 13 | font-size: 32px; 14 | line-height: 1; 15 | margin: 0 0 4px; 16 | display: inline-block; 17 | vertical-align: top; 18 | } 19 | 20 | section p { 21 | margin: 1em 0; 22 | } 23 | 24 | section a { 25 | text-decoration: none; 26 | color: #1ea7fd; 27 | } 28 | 29 | section ul { 30 | padding-left: 30px; 31 | margin: 1em 0; 32 | } 33 | 34 | section li { 35 | margin-bottom: 8px; 36 | } 37 | 38 | section .tip { 39 | display: inline-block; 40 | border-radius: 1em; 41 | font-size: 11px; 42 | line-height: 12px; 43 | font-weight: 700; 44 | background: #e7fdd8; 45 | color: #66bf3c; 46 | padding: 4px 12px; 47 | margin-right: 10px; 48 | vertical-align: top; 49 | } 50 | 51 | section .tip-wrapper { 52 | font-size: 13px; 53 | line-height: 20px; 54 | margin-top: 40px; 55 | margin-bottom: 40px; 56 | } 57 | 58 | section .tip-wrapper svg { 59 | display: inline-block; 60 | height: 12px; 61 | width: 12px; 62 | margin-right: 4px; 63 | vertical-align: top; 64 | margin-top: 3px; 65 | } 66 | 67 | section .tip-wrapper svg path { 68 | fill: #1ea7fd; 69 | } 70 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type Parameters = { 2 | highlighter?: { 3 | showLineNumbers?: boolean; 4 | wrapLines?: boolean; 5 | }; 6 | root?: string; 7 | removeEmptyComments?: boolean; 8 | removeComments?: boolean | RegExp; 9 | transform?: (code: string) => string; 10 | }; 11 | -------------------------------------------------------------------------------- /src/withHTML.ts: -------------------------------------------------------------------------------- 1 | import { useChannel } from "storybook/internal/preview-api"; 2 | import type { 3 | Renderer, 4 | PartialStoryFn as StoryFunction, 5 | } from "storybook/internal/types"; 6 | import { EVENTS } from "./constants"; 7 | import { Parameters } from "./types"; 8 | 9 | export const withHTML = ( 10 | storyFn: StoryFunction, 11 | { 12 | parameters: { html: parameters = {} } = {}, 13 | }: { parameters?: { html?: Parameters } }, 14 | ) => { 15 | const emit = useChannel({}); 16 | 17 | setTimeout(() => { 18 | const rootSelector = parameters.root || "#storybook-root, #root"; 19 | const root = document.querySelector(rootSelector); 20 | let code: string = root ? root.innerHTML : `${rootSelector} not found.`; 21 | const { removeEmptyComments, removeComments, transform } = parameters; 22 | if (removeEmptyComments) { 23 | code = code.replace(//g, ""); 24 | } 25 | if (removeComments === true) { 26 | code = code.replace(//g, ""); 27 | } else if (removeComments instanceof RegExp) { 28 | code = code.replace(//g, (match, p1) => 29 | removeComments.test(p1) ? "" : match, 30 | ); 31 | } 32 | if (typeof transform === "function") { 33 | try { 34 | code = transform(code); 35 | } catch (e) { 36 | console.error(e); 37 | } 38 | } 39 | emit(EVENTS.CODE_UPDATE, { code, options: parameters }); 40 | }, 0); 41 | 42 | return storyFn(); 43 | }; 44 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "baseUrl": ".", 5 | "esModuleInterop": true, 6 | "experimentalDecorators": true, 7 | "incremental": false, 8 | "isolatedModules": true, 9 | "jsx": "react", 10 | "lib": ["es2020", "dom"], 11 | "module": "commonjs", 12 | "noImplicitAny": true, 13 | "rootDir": "./src", 14 | "skipLibCheck": true, 15 | "target": "ES2020" 16 | }, 17 | "include": ["src/**/*"] 18 | } 19 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, type Options } from "tsup"; 2 | import { readFile } from "node:fs/promises"; 3 | import { globalPackages as globalManagerPackages } from "storybook/internal/manager/globals"; 4 | import { globalPackages as globalPreviewPackages } from "storybook/internal/preview/globals"; 5 | 6 | // The current browsers supported by Storybook v7 7 | const BROWSER_TARGET: Options["target"] = [ 8 | "chrome100", 9 | "safari15", 10 | "firefox91", 11 | ]; 12 | const NODE_TARGET: Options["target"] = ["node18"]; 13 | 14 | type BundlerConfig = { 15 | bundler?: { 16 | exportEntries?: string[]; 17 | nodeEntries?: string[]; 18 | managerEntries?: string[]; 19 | previewEntries?: string[]; 20 | }; 21 | }; 22 | 23 | export default defineConfig(async (options) => { 24 | // reading the three types of entries from package.json, which has the following structure: 25 | // { 26 | // ... 27 | // "bundler": { 28 | // "exportEntries": ["./src/index.ts"], 29 | // "managerEntries": ["./src/manager.ts"], 30 | // "previewEntries": ["./src/preview.ts"] 31 | // "nodeEntries": ["./src/preset.ts"] 32 | // } 33 | // } 34 | const packageJson = (await readFile("./package.json", "utf8").then( 35 | JSON.parse, 36 | )) as BundlerConfig; 37 | const { 38 | bundler: { 39 | exportEntries = [], 40 | managerEntries = [], 41 | previewEntries = [], 42 | nodeEntries = [], 43 | } = {}, 44 | } = packageJson; 45 | 46 | const commonConfig: Options = { 47 | splitting: false, 48 | minify: !options.watch, 49 | treeshake: true, 50 | sourcemap: true, 51 | clean: options.watch ? false : true, 52 | }; 53 | 54 | const configs: Options[] = []; 55 | 56 | // export entries are entries meant to be manually imported by the user 57 | // they are not meant to be loaded by the manager or preview 58 | // they'll be usable in both node and browser environments, depending on which features and modules they depend on 59 | if (exportEntries.length) { 60 | configs.push({ 61 | ...commonConfig, 62 | entry: exportEntries, 63 | dts: { 64 | resolve: true, 65 | }, 66 | format: ["esm", "cjs"], 67 | target: [...BROWSER_TARGET, ...NODE_TARGET], 68 | platform: "neutral", 69 | external: [...globalManagerPackages, ...globalPreviewPackages], 70 | }); 71 | } 72 | 73 | // manager entries are entries meant to be loaded into the manager UI 74 | // they'll have manager-specific packages externalized and they won't be usable in node 75 | // they won't have types generated for them as they're usually loaded automatically by Storybook 76 | if (managerEntries.length) { 77 | configs.push({ 78 | ...commonConfig, 79 | entry: managerEntries, 80 | format: ["esm"], 81 | target: BROWSER_TARGET, 82 | platform: "browser", 83 | external: globalManagerPackages, 84 | }); 85 | } 86 | 87 | // preview entries are entries meant to be loaded into the preview iframe 88 | // they'll have preview-specific packages externalized and they won't be usable in node 89 | // they'll have types generated for them so they can be imported when setting up Portable Stories 90 | if (previewEntries.length) { 91 | configs.push({ 92 | ...commonConfig, 93 | entry: previewEntries, 94 | dts: { 95 | resolve: true, 96 | }, 97 | format: ["esm", "cjs"], 98 | target: BROWSER_TARGET, 99 | platform: "browser", 100 | external: globalPreviewPackages, 101 | }); 102 | } 103 | 104 | // node entries are entries meant to be used in node-only 105 | // this is useful for presets, which are loaded by Storybook when setting up configurations 106 | // they won't have types generated for them as they're usually loaded automatically by Storybook 107 | if (nodeEntries.length) { 108 | configs.push({ 109 | ...commonConfig, 110 | entry: nodeEntries, 111 | format: ["cjs"], 112 | target: NODE_TARGET, 113 | platform: "node", 114 | }); 115 | } 116 | 117 | return configs; 118 | }); 119 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }); 8 | --------------------------------------------------------------------------------