├── .all-contributorsrc ├── .babelrc ├── .circleci └── config.yml ├── .eslintrc ├── .github └── workflows │ ├── chromatic.yml │ └── codeql-analysis.yml ├── .gitignore ├── .npmignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example ├── .storybook │ ├── .babelrc │ ├── addons.js │ └── config.js ├── components │ └── simple.js └── stories │ ├── array.js │ ├── decorator.js │ ├── deep.js │ ├── fragments.js │ ├── functions.js │ ├── index.js │ ├── simple.js │ └── withProps.js ├── netlify.toml ├── package.json ├── register.js ├── screenshot.png ├── src ├── __tests__ │ ├── __snapshots__ │ │ └── index.test.js.snap │ └── index.test.js ├── constants.ts ├── index.tsx ├── jsx.tsx ├── register.tsx ├── renderer.tsx └── types │ ├── react-element-to-jsx-string.d.ts │ └── storybook__ui.d.ts ├── storybook-jsx.png ├── storybook-jsx.sketch ├── storybook-jsx.svg ├── tsconfig.json └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "addon-jsx", 3 | "projectOwner": "storybookjs", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": [ 7 | "README.md" 8 | ], 9 | "imageSize": 100, 10 | "commit": true, 11 | "commitConvention": "none", 12 | "contributors": [ 13 | { 14 | "login": "wcastand", 15 | "name": "William", 16 | "avatar_url": "https://avatars3.githubusercontent.com/u/2178244?v=4", 17 | "profile": "https://wcastand.tech/", 18 | "contributions": [ 19 | "code", 20 | "design", 21 | "ideas", 22 | "doc" 23 | ] 24 | }, 25 | { 26 | "login": "hipstersmoothie", 27 | "name": "Andrew Lisowski", 28 | "avatar_url": "https://avatars3.githubusercontent.com/u/1192452?v=4", 29 | "profile": "http://hipstersmoothie.com", 30 | "contributions": [ 31 | "code", 32 | "doc", 33 | "infra", 34 | "maintenance" 35 | ] 36 | }, 37 | { 38 | "login": "ndelangen", 39 | "name": "Norbert de Langen", 40 | "avatar_url": "https://avatars2.githubusercontent.com/u/3070389?v=4", 41 | "profile": "https://github.com/ndelangen", 42 | "contributions": [ 43 | "code", 44 | "doc" 45 | ] 46 | }, 47 | { 48 | "login": "samouss", 49 | "name": "Samuel Vaillant", 50 | "avatar_url": "https://avatars2.githubusercontent.com/u/6513513?v=4", 51 | "profile": "https://github.com/samouss", 52 | "contributions": [ 53 | "code", 54 | "doc" 55 | ] 56 | }, 57 | { 58 | "login": "alexandrebodin", 59 | "name": "Alexandre BODIN", 60 | "avatar_url": "https://avatars2.githubusercontent.com/u/6065744?v=4", 61 | "profile": "https://twitter.com/_alexandrebodin", 62 | "contributions": [ 63 | "code" 64 | ] 65 | }, 66 | { 67 | "login": "stof", 68 | "name": "Christophe Coevoet", 69 | "avatar_url": "https://avatars0.githubusercontent.com/u/439401?v=4", 70 | "profile": "https://github.com/stof", 71 | "contributions": [ 72 | "code" 73 | ] 74 | }, 75 | { 76 | "login": "leonelgalan", 77 | "name": "Leonel Galán", 78 | "avatar_url": "https://avatars3.githubusercontent.com/u/727774?v=4", 79 | "profile": "http://www.leonelgalan.com", 80 | "contributions": [ 81 | "code" 82 | ] 83 | }, 84 | { 85 | "login": "Landerson352", 86 | "name": "Lincoln Anderson", 87 | "avatar_url": "https://avatars3.githubusercontent.com/u/5214462?v=4", 88 | "profile": "http://threefivetwo.com", 89 | "contributions": [ 90 | "code" 91 | ] 92 | }, 93 | { 94 | "login": "smollweide", 95 | "name": "Simon Mollweide", 96 | "avatar_url": "https://avatars2.githubusercontent.com/u/2912007?v=4", 97 | "profile": "https://github.com/smollweide", 98 | "contributions": [ 99 | "code" 100 | ] 101 | }, 102 | { 103 | "login": "lflpowell", 104 | "name": "lflpowell", 105 | "avatar_url": "https://avatars0.githubusercontent.com/u/37211236?v=4", 106 | "profile": "https://github.com/lflpowell", 107 | "contributions": [ 108 | "code" 109 | ] 110 | }, 111 | { 112 | "login": "expe-lbenychou", 113 | "name": "lionelbenychou", 114 | "avatar_url": "https://avatars1.githubusercontent.com/u/31204794?v=4", 115 | "profile": "https://github.com/expe-lbenychou", 116 | "contributions": [ 117 | "code" 118 | ] 119 | }, 120 | { 121 | "login": "breadadams", 122 | "name": "Brad Adams", 123 | "avatar_url": "https://avatars1.githubusercontent.com/u/5795227?v=4", 124 | "profile": "http://breadadams.com", 125 | "contributions": [ 126 | "doc" 127 | ] 128 | }, 129 | { 130 | "login": "arahansen", 131 | "name": "Andrew Hansen", 132 | "avatar_url": "https://avatars0.githubusercontent.com/u/8746094?v=4", 133 | "profile": "http://twitter.com/arahansen", 134 | "contributions": [ 135 | "code" 136 | ] 137 | }, 138 | { 139 | "login": "petermikitsh", 140 | "name": "Peter Mikitsh", 141 | "avatar_url": "https://avatars3.githubusercontent.com/u/1571918?v=4", 142 | "profile": "http://peter.mikit.sh", 143 | "contributions": [ 144 | "doc", 145 | "code" 146 | ] 147 | }, 148 | { 149 | "login": "lisamartin00", 150 | "name": "lisamartin00", 151 | "avatar_url": "https://avatars0.githubusercontent.com/u/6465955?v=4", 152 | "profile": "https://github.com/lisamartin00", 153 | "contributions": [ 154 | "code" 155 | ] 156 | }, 157 | { 158 | "login": "semihraifgurel", 159 | "name": "Semih Raif Gürel", 160 | "avatar_url": "https://avatars.githubusercontent.com/u/29544960?v=4", 161 | "profile": "https://github.com/semihraifgurel", 162 | "contributions": [ 163 | "doc" 164 | ] 165 | }, 166 | { 167 | "login": "leepowelldev", 168 | "name": "Lee Powell", 169 | "avatar_url": "https://avatars.githubusercontent.com/u/602052?v=4", 170 | "profile": "https://leepowell.dev", 171 | "contributions": [ 172 | "infra", 173 | "code" 174 | ] 175 | }, 176 | { 177 | "login": "jimmyandrade", 178 | "name": "Jimmy Andrade", 179 | "avatar_url": "https://avatars.githubusercontent.com/u/2307245?v=4", 180 | "profile": "http://jimmyandrade.com", 181 | "contributions": [ 182 | "infra" 183 | ] 184 | } 185 | ], 186 | "contributorsPerLine": 7 187 | } 188 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "shippedProposals": true 7 | } 8 | ], 9 | "@babel/preset-react", 10 | "@babel/preset-typescript" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | defaults: &defaults 4 | working_directory: ~/storybook-addon-jsx 5 | docker: 6 | - image: circleci/node:latest-browsers 7 | 8 | jobs: 9 | install: 10 | <<: *defaults 11 | steps: 12 | - checkout 13 | - restore_cache: 14 | keys: 15 | # Find a cache corresponding to this specific package.json checksum 16 | # when this file is changed, this key will fail 17 | - storybook-addon-jsx-{{ .Branch }}-{{ checksum "yarn.lock" }}-{{ checksum ".circleci/config.yml" }} 18 | - storybook-addon-jsx-{{ .Branch }}-{{ checksum "yarn.lock" }} 19 | - storybook-addon-jsx-{{ .Branch }} 20 | # Find the most recent cache used from any branch 21 | - storybook-addon-jsx-master 22 | - storybook-addon-jsx- 23 | - run: 24 | name: Install Dependencies 25 | command: yarn install --frozen-lockfile 26 | - save_cache: 27 | key: storybook-addon-jsx-{{ .Branch }}-{{ checksum "yarn.lock" }}-{{ checksum ".circleci/config.yml" }} 28 | paths: 29 | - node_modules 30 | - ~/.cache/yarn 31 | - persist_to_workspace: 32 | root: . 33 | paths: 34 | - . 35 | 36 | test: 37 | <<: *defaults 38 | steps: 39 | - attach_workspace: 40 | at: ~/storybook-addon-jsx 41 | - run: 42 | name: Test 43 | command: yarn test 44 | 45 | lint: 46 | <<: *defaults 47 | steps: 48 | - attach_workspace: 49 | at: ~/storybook-addon-jsx 50 | - run: 51 | name: Lint 52 | command: yarn lint 53 | 54 | build: 55 | <<: *defaults 56 | steps: 57 | - attach_workspace: 58 | at: ~/storybook-addon-jsx 59 | - run: 60 | name: Build 61 | command: yarn build 62 | - persist_to_workspace: 63 | root: . 64 | paths: 65 | - . 66 | 67 | release: 68 | <<: *defaults 69 | steps: 70 | - attach_workspace: 71 | at: ~/storybook-addon-jsx 72 | - run: 73 | name: Release 74 | command: yarn run release 75 | 76 | workflows: 77 | version: 2 78 | build_and_test: 79 | jobs: 80 | - install 81 | 82 | - test: 83 | requires: 84 | - install 85 | 86 | - lint: 87 | requires: 88 | - install 89 | 90 | - build: 91 | requires: 92 | - install 93 | 94 | - release: 95 | requires: 96 | - lint 97 | - test 98 | - build 99 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@design-systems", 3 | "rules": { 4 | "no-undef": 0 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.github/workflows/chromatic.yml: -------------------------------------------------------------------------------- 1 | name: 'Chromatic' 2 | on: pull_request 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v1 9 | - run: | 10 | yarn 11 | - uses: chromaui/action@v1 12 | with: 13 | appCode: qceepgaflpj 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ master ] 9 | schedule: 10 | - cron: '35 3 * * 2' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: [ 'javascript' ] 25 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 26 | # Learn more: 27 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 28 | 29 | steps: 30 | - name: Checkout repository 31 | uses: actions/checkout@v2 32 | 33 | # Initializes the CodeQL tools for scanning. 34 | - name: Initialize CodeQL 35 | uses: github/codeql-action/init@v1 36 | with: 37 | languages: ${{ matrix.language }} 38 | # If you wish to specify custom queries, you can do so here or in a config file. 39 | # By default, queries listed here will override any specified in a config file. 40 | # Prefix the list here with "+" to use these queries and those in the config file. 41 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 42 | 43 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 44 | # If this step fails, then you should remove it and run the build manually (see below) 45 | - name: Autobuild 46 | uses: github/codeql-action/autobuild@v1 47 | 48 | # ℹ️ Command-line programs to run using the OS shell. 49 | # 📚 https://git.io/JvXDl 50 | 51 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 52 | # and modify them (or add more) to build your code if your project 53 | # uses a compiled language 54 | 55 | #- run: | 56 | # make bootstrap 57 | # make release 58 | 59 | - name: Perform CodeQL Analysis 60 | uses: github/codeql-action/analyze@v1 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | *.log 4 | .idea 5 | storybook-static 6 | .env 7 | .eslintcache -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | .babelrc 3 | .vscode 4 | example -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Placez vos paramètres dans ce fichier pour remplacer les paramètres par défaut et les paramètres utilisateur. 2 | { 3 | "prettier.bracketSpacing": true, 4 | "prettier.semi": false, 5 | "prettier.singleQuote": true, 6 | "prettier.trailingComma": "all", 7 | "prettier.tabWidth": 2, 8 | "prettier.printWidth": 100, 9 | "editor.formatOnSave": true, 10 | "editor.tabSize": 2, 11 | "deepscan.enable": true 12 | } 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v7.3.14 (Tue Sep 14 2021) 2 | 3 | #### 🐛 Bug Fix 4 | 5 | - Bump tar from 4.4.15 to 4.4.19 [#160](https://github.com/storybookjs/addon-jsx/pull/160) ([@dependabot[bot]](https://github.com/dependabot[bot])) 6 | 7 | #### Authors: 1 8 | 9 | - [@dependabot[bot]](https://github.com/dependabot[bot]) 10 | 11 | --- 12 | 13 | # v7.3.13 (Tue Aug 24 2021) 14 | 15 | #### 🐛 Bug Fix 16 | 17 | - Bump tar from 4.4.13 to 4.4.15 [#157](https://github.com/storybookjs/addon-jsx/pull/157) ([@dependabot[bot]](https://github.com/dependabot[bot])) 18 | - Bump path-parse from 1.0.6 to 1.0.7 [#158](https://github.com/storybookjs/addon-jsx/pull/158) ([@dependabot[bot]](https://github.com/dependabot[bot])) 19 | 20 | #### Authors: 1 21 | 22 | - [@dependabot[bot]](https://github.com/dependabot[bot]) 23 | 24 | --- 25 | 26 | # v7.3.12 (Tue Jun 22 2021) 27 | 28 | :tada: This release contains work from a new contributor! :tada: 29 | 30 | Thank you, Jimmy Andrade ([@jimmyandrade](https://github.com/jimmyandrade)), for all your work! 31 | 32 | #### 🐛 Bug Fix 33 | 34 | - ci: run CodeQL analysis [#156](https://github.com/storybookjs/addon-jsx/pull/156) ([@jimmyandrade](https://github.com/jimmyandrade)) 35 | - Bump markdown-to-jsx from 6.10.3 to 6.11.4 [#125](https://github.com/storybookjs/addon-jsx/pull/125) ([@dependabot[bot]](https://github.com/dependabot[bot])) 36 | - Bump elliptic from 6.5.1 to 6.5.4 [#137](https://github.com/storybookjs/addon-jsx/pull/137) ([@dependabot[bot]](https://github.com/dependabot[bot])) 37 | 38 | #### Authors: 2 39 | 40 | - [@dependabot[bot]](https://github.com/dependabot[bot]) 41 | - Jimmy Andrade ([@jimmyandrade](https://github.com/jimmyandrade)) 42 | 43 | --- 44 | 45 | # v7.3.11 (Tue Jun 22 2021) 46 | 47 | #### 🐛 Bug Fix 48 | 49 | - Bump websocket-extensions from 0.1.3 to 0.1.4 [#111](https://github.com/storybookjs/addon-jsx/pull/111) ([@dependabot[bot]](https://github.com/dependabot[bot])) 50 | - Bump yargs-parser from 13.1.1 to 13.1.2 [#127](https://github.com/storybookjs/addon-jsx/pull/127) ([@dependabot[bot]](https://github.com/dependabot[bot])) 51 | - Bump nested-object-assign from 1.0.3 to 1.0.4 [#133](https://github.com/storybookjs/addon-jsx/pull/133) ([@dependabot[bot]](https://github.com/dependabot[bot])) 52 | - Bump ini from 1.3.5 to 1.3.8 [#136](https://github.com/storybookjs/addon-jsx/pull/136) ([@dependabot[bot]](https://github.com/dependabot[bot])) 53 | - Bump lodash from 4.17.15 to 4.17.21 [#143](https://github.com/storybookjs/addon-jsx/pull/143) ([@dependabot[bot]](https://github.com/dependabot[bot])) 54 | - Bump y18n from 4.0.0 to 4.0.3 [#147](https://github.com/storybookjs/addon-jsx/pull/147) ([@dependabot[bot]](https://github.com/dependabot[bot])) 55 | - Bump postcss from 7.0.18 to 7.0.36 [#155](https://github.com/storybookjs/addon-jsx/pull/155) ([@dependabot[bot]](https://github.com/dependabot[bot])) 56 | - Bump handlebars from 4.4.5 to 4.7.7 [#144](https://github.com/storybookjs/addon-jsx/pull/144) ([@dependabot[bot]](https://github.com/dependabot[bot])) 57 | - Bump ua-parser-js from 0.7.20 to 0.7.28 [#150](https://github.com/storybookjs/addon-jsx/pull/150) ([@dependabot[bot]](https://github.com/dependabot[bot])) 58 | - Bump url-parse from 1.4.7 to 1.5.1 [#151](https://github.com/storybookjs/addon-jsx/pull/151) ([@dependabot[bot]](https://github.com/dependabot[bot])) 59 | - Bump merge-deep from 3.0.2 to 3.0.3 [#154](https://github.com/storybookjs/addon-jsx/pull/154) ([@dependabot[bot]](https://github.com/dependabot[bot])) 60 | 61 | #### Authors: 1 62 | 63 | - [@dependabot[bot]](https://github.com/dependabot[bot]) 64 | 65 | --- 66 | 67 | # v7.3.10 (Thu May 13 2021) 68 | 69 | #### 🐛 Bug Fix 70 | 71 | - Bump hosted-git-info from 2.8.5 to 2.8.9 [#152](https://github.com/storybookjs/addon-jsx/pull/152) ([@dependabot[bot]](https://github.com/dependabot[bot])) 72 | 73 | #### Authors: 1 74 | 75 | - [@dependabot[bot]](https://github.com/dependabot[bot]) 76 | 77 | --- 78 | 79 | # v7.3.9 (Wed Apr 21 2021) 80 | 81 | #### 🐛 Bug Fix 82 | 83 | - Bump ssri from 6.0.1 to 6.0.2 [#148](https://github.com/storybookjs/addon-jsx/pull/148) ([@dependabot[bot]](https://github.com/dependabot[bot])) 84 | 85 | #### Authors: 1 86 | 87 | - [@dependabot[bot]](https://github.com/dependabot[bot]) 88 | 89 | --- 90 | 91 | # v7.3.8 (Sun Apr 18 2021) 92 | 93 | #### 🐛 Bug Fix 94 | 95 | - Bump gitlog from 4.0.0 to 4.0.4 [#146](https://github.com/storybookjs/addon-jsx/pull/146) ([@dependabot[bot]](https://github.com/dependabot[bot])) 96 | 97 | #### Authors: 1 98 | 99 | - [@dependabot[bot]](https://github.com/dependabot[bot]) 100 | 101 | --- 102 | 103 | # v7.3.7 (Wed Mar 31 2021) 104 | 105 | #### 🐛 Bug Fix 106 | 107 | - Support React 17 peer dependency [#142](https://github.com/storybookjs/addon-jsx/pull/142) ([@leepowelldev](https://github.com/leepowelldev)) 108 | 109 | #### Authors: 1 110 | 111 | - Lee Powell ([@leepowelldev](https://github.com/leepowelldev)) 112 | 113 | --- 114 | 115 | # v7.3.6 (Tue Feb 09 2021) 116 | 117 | #### 🐛 Bug Fix 118 | 119 | - fix copy button positioning on long jsx content [#135](https://github.com/storybookjs/addon-jsx/pull/135) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 120 | 121 | #### Authors: 1 122 | 123 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 124 | 125 | --- 126 | 127 | # v7.3.5 (Tue Feb 09 2021) 128 | 129 | :tada: This release contains work from a new contributor! :tada: 130 | 131 | Thank you, Semih Raif Gürel ([@semihraifgurel](https://github.com/semihraifgurel)), for all your work! 132 | 133 | #### 🐛 Bug Fix 134 | 135 | - Custom component name [#132](https://github.com/storybookjs/addon-jsx/pull/132) (semih@weptile.com) 136 | 137 | #### Authors: 1 138 | 139 | - Semih Raif Gürel ([@semihraifgurel](https://github.com/semihraifgurel)) 140 | 141 | --- 142 | 143 | # v7.3.4 (Thu Aug 13 2020) 144 | 145 | #### 🐛 Bug Fix 146 | 147 | - hide default props as the default [#122](https://github.com/storybookjs/addon-jsx/pull/122) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 148 | 149 | #### Authors: 1 150 | 151 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 152 | 153 | --- 154 | 155 | # v7.3.3 (Fri Jul 17 2020) 156 | 157 | #### 🐛 Bug Fix 158 | 159 | - fix skip [#119](https://github.com/storybookjs/addon-jsx/pull/119) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 160 | 161 | #### Authors: 1 162 | 163 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 164 | 165 | --- 166 | 167 | # v7.3.2 (Wed Jul 15 2020) 168 | 169 | :tada: This release contains work from a new contributor! :tada: 170 | 171 | Thank you, null[@lisamartin00](https://github.com/lisamartin00), for all your work! 172 | 173 | #### 🐛 Bug Fix 174 | 175 | - Fix unique key prop warning [#117](https://github.com/storybookjs/addon-jsx/pull/117) ([@lisamartin00](https://github.com/lisamartin00)) 176 | 177 | #### Authors: 1 178 | 179 | - [@lisamartin00](https://github.com/lisamartin00) 180 | 181 | --- 182 | 183 | # v7.3.1 (Fri Jul 10 2020) 184 | 185 | #### 🐛 Bug Fix 186 | 187 | - docs overhaul [#115](https://github.com/storybookjs/addon-jsx/pull/115) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 188 | 189 | #### Authors: 1 190 | 191 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 192 | 193 | --- 194 | 195 | # v7.3.0 (Fri Jun 05 2020) 196 | 197 | :tada: This release contains work from a new contributor! :tada: 198 | 199 | Thank you, Peter Mikitsh ([@petermikitsh](https://github.com/petermikitsh)), for all your work! 200 | 201 | #### 🚀 Enhancement 202 | 203 | - feat: disable jsx per story [#110](https://github.com/storybookjs/addon-jsx/pull/110) ([@petermikitsh](https://github.com/petermikitsh)) 204 | 205 | #### 📝 Documentation 206 | 207 | - Update auto + add all-contributors [#108](https://github.com/storybookjs/addon-jsx/pull/108) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 208 | 209 | #### Authors: 2 210 | 211 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 212 | - Peter Mikitsh ([@petermikitsh](https://github.com/petermikitsh)) 213 | 214 | --- 215 | 216 | # v7.2.3 (Tue Apr 14 2020) 217 | 218 | #### 🐛 Bug Fix 219 | 220 | - corral runaway useEffect [#104](https://github.com/storybookjs/addon-jsx/pull/104) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 221 | 222 | #### Authors: 1 223 | 224 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 225 | 226 | --- 227 | 228 | # v7.2.2 (Mon Apr 13 2020) 229 | 230 | #### 🐛 Bug Fix 231 | 232 | - fix children bug [#103](https://github.com/storybookjs/addon-jsx/pull/103) ([@hipstersmoothie](https://github.com/hipstersmoothie) [@github-actions[bot]](https://github.com/github-actions[bot])) 233 | 234 | #### Authors: 2 235 | 236 | - [@github-actions[bot]](https://github.com/github-actions[bot]) 237 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 238 | 239 | --- 240 | 241 | # v7.2.1 (Sat Apr 11 2020) 242 | 243 | #### 🐛 Bug Fix 244 | 245 | - use stack to track last component [#102](https://github.com/storybookjs/addon-jsx/pull/102) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 246 | 247 | #### Authors: 1 248 | 249 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 250 | 251 | --- 252 | 253 | # v7.2.0 (Fri Apr 10 2020) 254 | 255 | #### 🚀 Enhancement 256 | 257 | - Show type info on hover [#101](https://github.com/storybookjs/addon-jsx/pull/101) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 258 | 259 | #### Authors: 1 260 | 261 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 262 | 263 | --- 264 | 265 | # v7.1.15 (Mon Mar 16 2020) 266 | 267 | #### 🐛 Bug Fix 268 | 269 | - Bump acorn from 5.7.3 to 5.7.4 [#99](https://github.com/storybookjs/addon-jsx/pull/99) ([@dependabot[bot]](https://github.com/dependabot[bot])) 270 | 271 | #### Authors: 1 272 | 273 | - [@dependabot[bot]](https://github.com/dependabot[bot]) 274 | 275 | --- 276 | 277 | # v7.1.14 (Thu Jan 23 2020) 278 | 279 | #### 🐛 Bug Fix 280 | 281 | - feat: Upgrade react-element-to-jsx-string to latest version [#97](https://github.com/storybookjs/addon-jsx/pull/97) ([@leonelgalan](https://github.com/leonelgalan)) 282 | 283 | #### Authors: 1 284 | 285 | - Leonel Galán ([@leonelgalan](https://github.com/leonelgalan)) 286 | 287 | --- 288 | 289 | # v7.1.13 (Fri Oct 25 2019) 290 | 291 | #### 🐛 Bug Fix 292 | 293 | - [ImgBot] Optimize images [#93](https://github.com/storybookjs/addon-jsx/pull/93) ([@ImgBotApp](https://github.com/ImgBotApp)) 294 | 295 | #### Authors: 1 296 | 297 | - Imgbot ([@ImgBotApp](https://github.com/ImgBotApp)) 298 | 299 | --- 300 | 301 | # v7.1.12 (Wed Oct 23 2019) 302 | 303 | #### 🐛 Bug Fix 304 | 305 | - Use storybook syntax-highlighter [#92](https://github.com/storybookjs/addon-jsx/pull/92) ([@ndelangen](https://github.com/ndelangen) [@github-actions[bot]](https://github.com/github-actions[bot])) 306 | 307 | #### Authors: 2 308 | 309 | - Norbert de Langen ([@ndelangen](https://github.com/ndelangen)) 310 | - [@github-actions[bot]](https://github.com/github-actions[bot]) 311 | 312 | --- 313 | 314 | # v7.1.11 (Wed Oct 23 2019) 315 | 316 | #### 🐛 Bug Fix 317 | 318 | - UPGRADES [#91](https://github.com/storybookjs/addon-jsx/pull/91) ([@ndelangen](https://github.com/ndelangen) [@github-actions[bot]](https://github.com/github-actions[bot])) 319 | 320 | #### Authors: 2 321 | 322 | - Norbert de Langen ([@ndelangen](https://github.com/ndelangen)) 323 | - [@github-actions[bot]](https://github.com/github-actions[bot]) 324 | 325 | --- 326 | 327 | # v7.1.10 (Wed Oct 23 2019) 328 | 329 | #### 🐛 Bug Fix 330 | 331 | - Upgrade dependencies [#90](https://github.com/storybookjs/addon-jsx/pull/90) ([@stof](https://github.com/stof) [@ndelangen](https://github.com/ndelangen)) 332 | 333 | #### Authors: 2 334 | 335 | - Christophe Coevoet ([@stof](https://github.com/stof)) 336 | - Norbert de Langen ([@ndelangen](https://github.com/ndelangen)) 337 | 338 | --- 339 | 340 | # v7.1.9 (Wed Oct 23 2019) 341 | 342 | #### ⚠️ Pushed to master 343 | 344 | - FIX ref to incorrect config path for storybook ([@ndelangen](https://github.com/ndelangen)) 345 | 346 | #### Authors: 1 347 | 348 | - Norbert de Langen ([@ndelangen](https://github.com/ndelangen)) 349 | 350 | --- 351 | 352 | # v7.1.7 (Wed Oct 23 2019) 353 | 354 | #### ⚠️ Pushed to master 355 | 356 | - ADD required build-storybook command for action to work ([@ndelangen](https://github.com/ndelangen)) 357 | - ADD chromatic ([@ndelangen](https://github.com/ndelangen)) 358 | 359 | #### Authors: 1 360 | 361 | - Norbert de Langen ([@ndelangen](https://github.com/ndelangen)) 362 | 363 | --- 364 | 365 | # v7.1.6 (Sun Sep 01 2019) 366 | 367 | --- 368 | 369 | # v7.1.5 (Fri Jul 19 2019) 370 | 371 | --- 372 | 373 | # v7.1.3 (Fri Jul 19 2019) 374 | 375 | #### 🏠 Internal 376 | 377 | - move auto to CI [#82](https://github.com/storybookjs/addon-jsx/pull/82) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 378 | - Bump handlebars from 4.1.1 to 4.1.2 [#74](https://github.com/storybookjs/addon-jsx/pull/74) ([@dependabot[bot]](https://github.com/dependabot[bot])) 379 | 380 | #### Authors: 2 381 | 382 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 383 | - [@dependabot[bot]](https://github.com/dependabot[bot]) 384 | 385 | --- 386 | 387 | # v7.1.2 (Sat Apr 27 2019) 388 | 389 | #### 🐛 Bug Fix 390 | 391 | - replace escaped quotes with single quotes for better rendering [#69](https://github.com/storybooks/addon-jsx/pull/69) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 392 | 393 | #### 📝 Documentation 394 | 395 | - add using with IE11 docs [#71](https://github.com/storybooks/addon-jsx/pull/71) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 396 | - add storyshot testing docs [#70](https://github.com/storybooks/addon-jsx/pull/70) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 397 | 398 | #### Authors: 1 399 | 400 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 401 | 402 | --- 403 | 404 | # v7.1.1 (Sat Apr 27 2019) 405 | 406 | #### 🐛 Bug Fix 407 | 408 | - fix key issue [#67](https://github.com/storybooks/addon-jsx/pull/67) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 409 | 410 | #### 🏠 Internal 411 | 412 | - update auto [#68](https://github.com/storybooks/addon-jsx/pull/68) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 413 | 414 | #### Authors: 1 415 | 416 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 417 | 418 | --- 419 | 420 | # v7.1.0 (Fri Mar 22 2019) 421 | 422 | #### 🚀 Enhancement 423 | 424 | - Theme support [#62](https://github.com/storybooks/addon-jsx/pull/62) ([@lflpowell](https://github.com/lflpowell) [@hipstersmoothie](https://github.com/hipstersmoothie)) 425 | 426 | #### 🐛 Bug Fix 427 | 428 | - fix circle build [#63](https://github.com/storybooks/addon-jsx/pull/63) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 429 | 430 | #### 🏠 Internal 431 | 432 | - add released plugin to auto [#64](https://github.com/storybooks/addon-jsx/pull/64) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 433 | 434 | #### Authors: 2 435 | 436 | - [@lflpowell](https://github.com/lflpowell) 437 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 438 | 439 | --- 440 | 441 | # v7.0.2 (Wed Mar 20 2019) 442 | 443 | #### 🐛 Bug Fix 444 | 445 | - fix types [#61](https://github.com/storybooks/addon-jsx/pull/61) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 446 | 447 | #### 🏠 Internal 448 | 449 | - Add Fragment Tests [#57](https://github.com/storybooks/addon-jsx/pull/57) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 450 | 451 | #### Authors: 1 452 | 453 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 454 | 455 | --- 456 | 457 | # v7.0.1 (Mon Mar 18 2019) 458 | 459 | #### 🏠 Internal 460 | 461 | - add circle config [#56](https://github.com/storybooks/addon-jsx/pull/56) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 462 | - add tests for decorator [#55](https://github.com/storybooks/addon-jsx/pull/55) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 463 | - add type build [#53](https://github.com/storybooks/addon-jsx/pull/53) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 464 | 465 | #### 📝 Documentation 466 | 467 | - correct docs [#52](https://github.com/storybooks/addon-jsx/pull/52) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 468 | 469 | #### Authors: 1 470 | 471 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 472 | 473 | --- 474 | 475 | # v7.0.0 (Fri Mar 15 2019) 476 | 477 | #### 💥 Breaking Change 478 | 479 | - Works with v5 + Written in TypeScript + Decorator Support [#51](https://github.com/storybooks/addon-jsx/pull/51) ([@hipstersmoothie](https://github.com/hipstersmoothie)) 480 | 481 | #### Authors: 1 482 | 483 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) 484 | 485 | --- 486 | 487 | # v5.1.1 (Fri Mar 15 2019) 488 | 489 | #### 🐛 Bug Fix 490 | 491 | - Move `active` check out of the `kind` condition [#49](https://github.com/storybooks/addon-jsx/pull/49) ([@Landerson352](https://github.com/Landerson352)) 492 | - fix(register): pass Title directly to panel [#43](https://github.com/storybooks/addon-jsx/pull/43) (wuxx1045@umn.edu) 493 | - feat(render-jsx): add onBeforeRender option to manipulate jsx render output [#39](https://github.com/storybooks/addon-jsx/pull/39) ([@smollweide](https://github.com/smollweide)) 494 | - Update README.md [#33](https://github.com/storybooks/addon-jsx/pull/33) ([@breadadams](https://github.com/breadadams)) 495 | - Beautify html if template string is being displayed. [#29](https://github.com/storybooks/addon-jsx/pull/29) (alfredo.delgado@gmail.com) 496 | - Add support for Vue stories … [#27](https://github.com/storybooks/addon-jsx/pull/27) (ndelangen@me.com) 497 | 498 | #### ⚠️ Pushed to master 499 | 500 | - add auto ([@lisowski54@gmail.com](https://github.com/lisowski54@gmail.com)) 501 | - 5.4.0 ([@ndelangen](https://github.com/ndelangen)) 502 | - UPDATE dependencies ([@ndelangen](https://github.com/ndelangen)) 503 | - 5.3.0 ([@adelgado@chewy.com](https://github.com/adelgado@chewy.com)) 504 | - 5.2.0 ([@ndelangen](https://github.com/ndelangen)) 505 | - ADD netlify command ([@ndelangen](https://github.com/ndelangen)) 506 | - UPGRADE && format ([@ndelangen](https://github.com/ndelangen)) 507 | - Update README.md ([@ndelangen](https://github.com/ndelangen)) 508 | - Update README.md ([@ndelangen](https://github.com/ndelangen)) 509 | 510 | #### Authors: 9 511 | 512 | - Lincoln Anderson ([@Landerson352](https://github.com/Landerson352)) 513 | - wuxx1045@umn.edu 514 | - Simon Mollweide ([@smollweide](https://github.com/smollweide)) 515 | - Brad Adams ([@breadadams](https://github.com/breadadams)) 516 | - alfredo.delgado@gmail.com 517 | - ndelangen@me.com 518 | - [@lisowski54@gmail.com](https://github.com/lisowski54@gmail.com) 519 | - [@ndelangen](https://github.com/ndelangen) 520 | - [@adelgado@chewy.com](https://github.com/adelgado@chewy.com) 521 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Your Name. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 |

Storybook-addon-jsx

5 |

6 | 7 | [![Build Status](https://travis-ci.org/Kilix/storybook-addon-jsx.svg?branch=master)](https://travis-ci.org/Kilix/storybook-addon-jsx) 8 | [![Total Download](https://img.shields.io/npm/dt/storybook-addon-jsx.svg)](https://www.npmjs.com/package/storybook-addon-jsx) 9 | [![Current Version](https://img.shields.io/npm/v/storybook-addon-jsx.svg)](https://www.npmjs.com/package/storybook-addon-jsx) 10 | 11 | This Storybook addon shows you the JSX of the story. 12 | This preview works for Vue components as well. 13 | The outputted JSX will reflect any changes made to the storybok by knobs or controls. 14 | 15 | ![Storybook Addon JSX Demo](screenshot.png) 16 | 17 | ## Getting started 18 | 19 | ### Installation 20 | 21 | First install the addon from `npm`: 22 | 23 | ```sh 24 | npm i --save-dev storybook-addon-jsx 25 | # or 26 | yarn add --dev storybook-addon-jsx 27 | ``` 28 | 29 | ### Configuration 30 | 31 | For the latest storybook all you need to do is add the addon to your `.storybook/main.js`: 32 | 33 | ```js 34 | module.exports = { 35 | addons: ['storybook-addon-jsx'] 36 | }; 37 | ``` 38 | 39 | If you are using storybook@5.x or lower you will need to add the following to `.storybook/addons.js`: 40 | 41 | ```js 42 | import 'storybook-addon-jsx/register'; 43 | ``` 44 | 45 | ### Usage 46 | 47 | Import it into your stories file and then use it when you write stories: 48 | 49 | ```js 50 | import React from "react"; 51 | import { storiesOf } from "@storybook/react"; 52 | import { jsxDecorator } from "storybook-addon-jsx"; 53 | 54 | import { TestComponent } from './TestComponent': 55 | 56 | export default { 57 | title: "Components/TestComponent", 58 | decorators: [jsxDecorator], 59 | }; 60 | 61 | export const Paris = () => ( 62 | 63 | Hello 64 | 65 | ); 66 | 67 | export const Orleans = () => Hello; 68 | ``` 69 | 70 | Or to configure it globally add the `jsxDecorator` to your `.storybook/preview.js`: 71 | 72 | ```js 73 | const { addDecorator } = require('@storybook/react'); 74 | const { jsxDecorator } = require('storybook-addon-jsx'); 75 | 76 | addDecorator(jsxDecorator); 77 | ``` 78 | 79 | #### Vue 80 | 81 | You can also use this addon with `@storybook/vue`. 82 | 83 | **`.storybook/preview.js`** 84 | 85 | ```js 86 | import { configure, addDecorator } from '@storybook/vue'; 87 | import { jsxDecorator } from 'storybook-addon-jsx'; 88 | 89 | addDecorator(jsxDecorator); 90 | ``` 91 | 92 | If a Vue story defines its view with a template string then it will be displayed. 93 | 94 | ```js 95 | import { storiesOf } from '@storybook/vue'; 96 | 97 | storiesOf('Vue', module).add('template property', () => ({ 98 | template: `
` 99 | })); 100 | ``` 101 | 102 | ## Options 103 | 104 | ### JSX 105 | 106 | This addon support all options from [react-element-to-jsx-string](https://github.com/algolia/react-element-to-jsx-string) as well as the following options. 107 | 108 | - `skip` (default: 0) : Skip element in your component to display 109 | 110 | ```javascript 111 | export default { 112 | title: 'Components/TestComponent', 113 | parameters: { 114 | jsx: { skip: 1 } 115 | } 116 | }; 117 | ``` 118 | 119 | - `onBeforeRender(domString: string) => string` (default: undefined) : function that receives the dom as a string before render. 120 | 121 | ```js 122 | export default { 123 | title: 'Components/TestComponent', 124 | parameters: { 125 | jsx: { 126 | onBeforeRender: domString => { 127 | if (domString.search('dangerouslySetInnerHTML') < 0) { 128 | return ''; 129 | } 130 | 131 | try { 132 | domString = /(dangerouslySetInnerHTML={{)([^}}]*)/.exec(domString)[2]; 133 | domString = /(')([^']*)/.exec(domString)[2]; 134 | } catch (err) {} 135 | 136 | return domString; 137 | } 138 | } 139 | } 140 | }; 141 | ``` 142 | 143 | - `displayName` (default: 0) : You can manually name the components that use useMemo or useRef. 144 | 145 | ```javascript 146 | export default { 147 | title: 'Components/TestComponent', 148 | parameters: { 149 | jsx: { 150 | displayName: () => 'CustomName' 151 | } 152 | } 153 | }; 154 | ``` 155 | 156 | ### Disable JSX Addon 157 | 158 | If enabled globally, the JSX addon can be disabled on individual stories: 159 | 160 | ```jsx 161 | export const Simple = () =>
Hello
; 162 | 163 | Simple.story = { 164 | parameters: { 165 | jsx: { 166 | disable: true 167 | } 168 | } 169 | }; 170 | ``` 171 | 172 | ### Vue Options 173 | 174 | - `enableBeautify` (default: true) : Beautify the template string 175 | - All HTML options from [js-beautify](https://github.com/beautify-web/js-beautify#css--html) 176 | 177 | ## Global Options 178 | 179 | To configure global options for this plugin, add the following to your `config.js`. 180 | 181 | ```js 182 | import { addParameters } from '@storybook/react'; 183 | 184 | addParameters({ 185 | jsx: { 186 | // your options 187 | } 188 | }); 189 | ``` 190 | 191 | ## Function Props 192 | 193 | If you provide a funtion to one of your props `storybook-addon-jsx` will display that functions `toString` result. 194 | This is usaully very ugly. 195 | To override this include the following util function that will print an easiy to read string. 196 | 197 | ```tsx 198 | /** 199 | * Overrides the toString on a function so that it addon-jsx prints 200 | * the callbacks in a copy-paste-able way. 201 | */ 202 | export const callback = (fn: T): T => { 203 | /** A toString to render the function in storybook */ 204 | // eslint-disable-next-line no-param-reassign 205 | fn.toString = () => '() => {}'; 206 | return fn; 207 | }; 208 | ``` 209 | 210 | This works well with the `@storybook/addon-actions` too. 211 | 212 | ```tsx 213 | export ExampleStory = () => ( 214 | 215 | ) 216 | ``` 217 | 218 | ## Including DocGen Information 219 | 220 | This addon will display prop type information while hovering over a component or prop. 221 | This is accomplished through [a babel plugin](https://github.com/storybookjs/babel-plugin-react-docgen) in the default storybook configuration. 222 | To use the docgen information for TypeScript components you must include be using [a typescript docgen loader](https://github.com/strothj/react-docgen-typescript-loader) 223 | 224 | ```js 225 | import { addParameters } from '@storybook/react'; 226 | 227 | addParameters({ 228 | jsx: { 229 | // your options 230 | } 231 | }); 232 | ``` 233 | 234 | ### TypeScript Monorepo DocGen 235 | 236 | In a TypeScript monorepo you will probably be importing components through package names. 237 | In this situation storybook will load your compiled typescript and lose information about the props. 238 | 239 | One solution to get around this is to add a unique property to your component's `package.json` that points directly at the TypeScript source. 240 | We can then set storybook's webpack configuration to look for this property first, which will allow the TypeScript loader to insert docgen information. 241 | 242 | In your component's `package.json`: 243 | 244 | ```jsonc 245 | { 246 | // Can be any string you want, here we choose "source" 247 | "source": "src/index.tsx" 248 | } 249 | ``` 250 | 251 | Then in your webpack config for storybook: 252 | 253 | ```js 254 | config.resolve.mainFields = ['source', 'module', 'main']; 255 | ``` 256 | 257 | ## Testing with storyshots 258 | 259 | If you are using the `addWithJSX` method you will need to include `storybook-addon-jsx` in your test file. 260 | 261 | ```js 262 | import initStoryshots from '@storybook/addon-storyshots'; 263 | import { setAddon } from '@storybook/react'; 264 | import JSXAddon from 'storybook-addon-jsx'; 265 | 266 | setAddon(JSXAddon); 267 | 268 | initStoryshots({ 269 | /* configuration options */ 270 | }); 271 | ``` 272 | 273 | ## Usage with IE11 274 | 275 | Some of the dependencies that this package has use APIs not available in IE11. 276 | To get around this you can add the following to your `webpack.config.js` file 277 | (your paths might be slightly different): 278 | 279 | ```js 280 | config.module.rules.push({ 281 | test: /\.js/, 282 | include: path.resolve(__dirname, '../node_modules/stringify-object'), 283 | use: [ 284 | { 285 | loader: 'babel-loader', 286 | options: { 287 | presets: ['env'] 288 | } 289 | } 290 | ] 291 | }); 292 | ``` 293 | 294 | ## Contributors ✨ 295 | 296 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 |

William

💻 🎨 🤔 📖

Andrew Lisowski

💻 📖 🚇 🚧

Norbert de Langen

💻 📖

Samuel Vaillant

💻 📖

Alexandre BODIN

💻

Christophe Coevoet

💻

Leonel Galán

💻

Lincoln Anderson

💻

Simon Mollweide

💻

lflpowell

💻

lionelbenychou

💻

Brad Adams

📖

Andrew Hansen

💻

Peter Mikitsh

📖 💻

lisamartin00

💻

Semih Raif Gürel

📖

Lee Powell

🚇 💻

Jimmy Andrade

🚇
327 | 328 | 329 | 330 | 331 | 332 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 333 | -------------------------------------------------------------------------------- /example/.storybook/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "shippedProposals": true 7 | } 8 | ], 9 | "@babel/preset-react" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /example/.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '../../register'; 2 | import '@storybook/addon-options/register'; 3 | import 'storybook-addon-react-docgen/register'; 4 | -------------------------------------------------------------------------------- /example/.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure, setAddon, addDecorator } from '@storybook/react'; 2 | import { withOptions } from '@storybook/addon-options'; 3 | import { withPropsTable } from 'storybook-addon-react-docgen'; 4 | import JSXAddon from '../../lib/index'; 5 | 6 | setAddon(JSXAddon); 7 | addDecorator(withPropsTable); 8 | 9 | function loadStories() { 10 | require('../stories'); 11 | } 12 | 13 | withOptions({ 14 | brandTitle: 'CUSTOM-OPTIONS', 15 | brandUrl: 'https://github.com/kadirahq/storybook-addon-options' 16 | }); 17 | 18 | configure(loadStories, module); 19 | -------------------------------------------------------------------------------- /example/components/simple.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { storiesOf } from '@storybook/react'; 4 | 5 | /** A simple component */ 6 | export const Simple = ({ children }) => ( 7 |
8 | Hello 9 | {children} 10 |
11 | ); 12 | 13 | Simple.propTypes = { 14 | /** A test string */ 15 | foo: PropTypes.string.isRequired, 16 | /** A test string */ 17 | test: PropTypes.string, 18 | /** A test boolean */ 19 | value: PropTypes.bool 20 | }; 21 | 22 | Simple.defaultProps = { 23 | value: false 24 | }; 25 | 26 | export default Simple; 27 | -------------------------------------------------------------------------------- /example/stories/array.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | 4 | const Component = () =>
; 5 | const Simple = props => ( 6 |
7 | Hello 8 | {props.children} 9 |
10 | ); 11 | 12 | storiesOf('Children Array', module) 13 | .addWithJSX( 14 | 'Simple Array', 15 | () => ( 16 |
17 |
18 |
19 |
20 | ), 21 | { skip: 1 } 22 | ) 23 | .addWithJSX( 24 | 'Array with function', 25 | () => ( 26 |
27 |
28 |
29 | {Component()} 30 |
31 | ), 32 | { skip: 1 } 33 | ) 34 | .addWithJSX( 35 | 'Array with nested component', 36 | () => ( 37 |
38 |
39 | 40 | hello 41 | 42 |
43 | ), 44 | { skip: 1 } 45 | ); 46 | -------------------------------------------------------------------------------- /example/stories/decorator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import { jsxDecorator } from '../../lib/index'; 4 | 5 | const Component = () =>
; 6 | const Simple = props => ( 7 |
8 | Hello 9 | {props.children} 10 |
11 | ); 12 | 13 | storiesOf('Decorator', module) 14 | .addDecorator(jsxDecorator) 15 | .add( 16 | 'Simple Array', 17 | () => ( 18 |
19 |
20 |
21 |
22 | ), 23 | { skip: 1 } 24 | ) 25 | .add( 26 | 'Array with function', 27 | () => ( 28 |
29 |
30 |
31 | {Component()} 32 |
33 | ), 34 | { skip: 1 } 35 | ) 36 | .add( 37 | 'Array with nested component', 38 | () => ( 39 |
40 |
41 | 42 | hello 43 | 44 |
45 | ), 46 | { skip: 1 } 47 | ); 48 | -------------------------------------------------------------------------------- /example/stories/deep.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | 4 | const Deep = ({ children }) => ( 5 |
6 |
7 |
8 |
9 | Hello 10 | {children ? children : null} 11 |
12 |
13 |
14 |
15 | ); 16 | 17 | storiesOf('Deep Test', module) 18 | .addWithJSX('No children - No options', () => ( 19 |
20 |
21 |
    22 |
  • Deeper
  • 23 |
24 |
25 |
26 | Hello 27 | 28 |
29 |
30 |
31 |
32 | )) 33 | .addWithJSX( 34 | 'No children - Rename', 35 | () => ( 36 |
37 |
38 |
39 |
40 | Hello 41 | 42 |
43 |
44 |
45 |
46 | ), 47 | { 48 | displayName: () => 'Renamed' 49 | } 50 | ) 51 | .addWithJSX('With children - No options', () => ( 52 |
53 |
54 |
55 |
56 | Hello 57 | 58 |
59 |
60 |
61 |
62 | )) 63 | .addWithJSX( 64 | 'With children - Skip', 65 | () => ( 66 |
67 |
68 |
69 |
70 | Hello 71 | 72 |
73 |
74 |
75 |
76 | ), 77 | { skip: 1 } 78 | ) 79 | .addWithJSX( 80 | 'With children - Skip and rename', 81 | () => ( 82 |
83 |
84 |
    85 |
  • Deeper
  • 86 |
87 |
88 |
89 | Hello 90 | 91 |
92 |
93 |
94 |
95 | ), 96 | { skip: 1, displayName: () => 'Renamed' } 97 | ); 98 | -------------------------------------------------------------------------------- /example/stories/fragments.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import { jsxDecorator } from '../../lib/index'; 4 | 5 | storiesOf('Fragments', module) 6 | .addDecorator(jsxDecorator) 7 | .add('Simple Fragment', () => ( 8 | 9 |
10 |
11 | 12 | )) 13 | .add('Array', () => [
,
]) 14 | .add('Fragment Shorthand', () => ( 15 | <> 16 |
17 |
18 | 19 | )); 20 | -------------------------------------------------------------------------------- /example/stories/functions.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | 4 | class Simple extends React.Component { 5 | render() { 6 | return ( 7 |
8 | {typeof this.props.children === 'function' 9 | ? this.props.children() 10 | : this.props.children} 11 |
12 | ); 13 | } 14 | } 15 | const withErrors = BaseComponent => props => { 16 | const style = { color: 'red' }; 17 | return ; 18 | }; 19 | 20 | const SimpleWithErrors = withErrors(Simple); 21 | 22 | storiesOf('Function as a children', module) 23 | .addWithJSX('No options', () => {() => World}) 24 | .addWithJSX('Skip', () => {() => World}, { 25 | skip: 1 26 | }) 27 | .addWithJSX( 28 | 'Skip and Rename', 29 | () => {() => World}, 30 | { skip: 1, displayName: () => 'Renamed' } 31 | ) 32 | .addWithJSX('Deep function - No options', () => ( 33 | 34 | {() => ( 35 | 36 | World 37 | 38 | )} 39 | 40 | )) 41 | .addWithJSX( 42 | 'Deep function - Skip', 43 | () => ( 44 | 45 | {() => ( 46 | 47 | World 48 | 49 | )} 50 | 51 | ), 52 | { skip: 2 } 53 | ); 54 | -------------------------------------------------------------------------------- /example/stories/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { setAddon } from '@storybook/react'; 3 | 4 | import './simple'; 5 | import './deep'; 6 | import './decorator'; 7 | import './functions'; 8 | import './fragments'; 9 | import './array'; 10 | import './withProps'; 11 | -------------------------------------------------------------------------------- /example/stories/simple.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | 4 | const Simple = ({ children }) => ( 5 |
6 | Hello 7 | {children} 8 |
9 | ); 10 | 11 | storiesOf('Simple Test', module) 12 | .addWithJSX('No children - No options', () => ) 13 | .addWithJSX('No children - Rename', () => , { 14 | displayName: 'Renamed' 15 | }) 16 | .addWithJSX('With children - No options', () => ( 17 | 18 | World 19 | 20 | )) 21 | .addWithJSX( 22 | 'With children - Skip', 23 | () => ( 24 | 25 | World 26 | 27 | ), 28 | { skip: 1 } 29 | ) 30 | .addWithJSX( 31 | 'With children - Skip and rename', 32 | () => ( 33 | 34 | World 35 | 36 | ), 37 | { skip: 1, displayName: 'Renamed' } 38 | ) 39 | .addWithJSX('Data strings', () => ( 40 |
41 | )); 42 | -------------------------------------------------------------------------------- /example/stories/withProps.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | 4 | import Simple from '../components/simple'; 5 | 6 | storiesOf('With Props', module) 7 | .addWithJSX('No children - No options', () => ) 8 | .addWithJSX( 9 | 'No children - Rename', 10 | () => , 11 | { 12 | displayName: 'Renamed' 13 | } 14 | ) 15 | .addWithJSX('With children - No options', () => ( 16 | 17 | World 18 | 19 | )) 20 | .addWithJSX( 21 | 'With children - Skip', 22 | () => ( 23 | 24 | 25 |
26 | 27 | 28 | ), 29 | { skip: 1 } 30 | ) 31 | .addWithJSX( 32 | 'With children - Skip and rename', 33 | () => ( 34 | 35 | 36 | World 37 | 38 | 39 | ), 40 | { skip: 1, displayName: () => 'Renamed' } 41 | ) 42 | .addWithJSX( 43 | 'With dangerouslySetInnerHTML - onBeforeRender', 44 | () => ( 45 | 46 | 47 |
Inner HTML
  • 1
  • 2
' 50 | }} 51 | /> 52 |
53 |
54 | ), 55 | { 56 | skip: 1, 57 | onBeforeRender: domString => { 58 | if (domString.search('dangerouslySetInnerHTML') < 0) { 59 | return ''; 60 | } 61 | try { 62 | domString = /(dangerouslySetInnerHTML={{)([^}}]*)/.exec(domString)[2]; 63 | domString = /(')([^']*)/.exec(domString)[2]; 64 | } catch (err) {} 65 | return domString; 66 | } 67 | } 68 | ); 69 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "storybook-static" 3 | command = "yarn prepare && yarn netlify" 4 | [build.environment] 5 | NODE_VERSION = "10" 6 | YARN_VERSION = "1.12.3" 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "storybook-addon-jsx", 3 | "version": "7.3.14", 4 | "description": "Display the JSX of the story", 5 | "repository": "storybookjs/addon-jsx", 6 | "license": "MIT", 7 | "author": "Andrew Lisowski ", 8 | "main": "lib/index.js", 9 | "scripts": { 10 | "prebuild": "rimraf lib", 11 | "build": "babel src --out-dir lib --ignore spec.js,test.js --extensions .ts --extensions .js --extensions .tsx && npm run build:types", 12 | "build-storybook": "build-storybook -c ./example/.storybook", 13 | "build:dev": "babel -w src --out-dir lib --ignore spec.js,test.js --extensions .ts --extensions .js --extensions .tsx", 14 | "build:types": "tsc -p tsconfig.json", 15 | "lint": "eslint --cache src --ext ts --ext tsx", 16 | "netlify": "build-storybook -c ./example/.storybook", 17 | "prepare": "yarn build", 18 | "release": "auto shipit", 19 | "storybook": "start-storybook -p 9009 -c ./example/.storybook", 20 | "test": "yarn test:all", 21 | "test:all": "jest src", 22 | "test:dev": "jest src --watch" 23 | }, 24 | "husky": { 25 | "hooks": { 26 | "pre-commit": "lint-staged" 27 | } 28 | }, 29 | "lint-staged": { 30 | "*.{js,json,css,md}": [ 31 | "prettier --write", 32 | "git add" 33 | ] 34 | }, 35 | "prettier": { 36 | "singleQuote": true 37 | }, 38 | "jest": { 39 | "testURL": "http://localhost" 40 | }, 41 | "dependencies": { 42 | "copy-to-clipboard": "^3.0.8", 43 | "js-beautify": "^1.8.8", 44 | "react-element-to-jsx-string": "^14.3.1", 45 | "storybook-pretty-props": "^1.0.3" 46 | }, 47 | "devDependencies": { 48 | "@auto-it/all-contributors": "^9.34.1", 49 | "@auto-it/first-time-contributor": "^9.34.1", 50 | "@babel/cli": "^7.1.2", 51 | "@babel/core": "^7.1.6", 52 | "@babel/preset-env": "^7.1.0", 53 | "@babel/preset-react": "^7.0.0", 54 | "@babel/preset-typescript": "^7.3.3", 55 | "@design-systems/eslint-config": "^1.4.15", 56 | "@storybook/addon-options": "^5.0.3", 57 | "@storybook/addon-storyshots": "^5.0.3", 58 | "@storybook/addons": "^5.2.5", 59 | "@storybook/channels": "^5.2.5", 60 | "@storybook/components": "^5.2.5", 61 | "@storybook/core-events": "^5.2.5", 62 | "@storybook/react": "^5.0.3", 63 | "@storybook/theming": "^5.2.5", 64 | "@types/js-beautify": "^1.8.0", 65 | "@types/react": "^16.8.8", 66 | "@types/storybook__react": "^4.0.1", 67 | "all-contributors-cli": "^6.14.2", 68 | "auto": "^9.34.1", 69 | "babel-core": "^7.0.0-bridge.0", 70 | "babel-jest": "^24.5.0", 71 | "babel-loader": "^8.0.6", 72 | "eslint": "^6.8.0", 73 | "husky": "^3.0.9", 74 | "jest": "^24.5.0", 75 | "lint-staged": "^9.4.2", 76 | "prettier": "^1.15.2", 77 | "prop-types": "^15.6.2", 78 | "react": "^16.11.0", 79 | "react-docgen-typescript-loader": "^3.7.2", 80 | "react-dom": "^16.11.0", 81 | "react-test-renderer": "^16.11.0", 82 | "regenerator-runtime": "^0.13.3", 83 | "rimraf": "^3.0.0", 84 | "storybook-addon-react-docgen": "^1.2.32", 85 | "typescript": "^3.3.3333" 86 | }, 87 | "peerDependencies": { 88 | "@storybook/addons": ">= 5.x", 89 | "@storybook/channels": ">= 5.x", 90 | "@storybook/components": ">= 5.x", 91 | "@storybook/core-events": ">= 5.x", 92 | "@storybook/theming": ">= 5.x", 93 | "react": "^16.2.0 || ^17.0.0", 94 | "react-dom": "^16.2.0 || ^17.0.0" 95 | }, 96 | "auto": { 97 | "plugins": [ 98 | "npm", 99 | "released", 100 | "first-time-contributor", 101 | "all-contributors" 102 | ] 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /register.js: -------------------------------------------------------------------------------- 1 | require('./lib/register.js'); 2 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/addon-jsx/61f84631e1c4334cf138d1814f9476e0fe6386f9/screenshot.png -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/index.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Storyshots Children Array Array with function 1`] = ` 4 |
5 |
6 |
7 |
10 |
11 | `; 12 | 13 | exports[`Storyshots Children Array Array with nested component 1`] = ` 14 |
15 |
16 |
17 | 18 | Hello 19 | 20 | 21 | hello 22 | 23 |
24 |
25 | `; 26 | 27 | exports[`Storyshots Children Array Simple Array 1`] = ` 28 |
29 |
30 |
31 |
32 | `; 33 | 34 | exports[`Storyshots Decorator Array with function 1`] = ` 35 |
36 |
37 |
38 |
39 |
40 | `; 41 | 42 | exports[`Storyshots Decorator Array with nested component 1`] = ` 43 |
44 |
45 |
46 | 47 | Hello 48 | 49 | 50 | hello 51 | 52 |
53 |
54 | `; 55 | 56 | exports[`Storyshots Decorator Simple Array 1`] = ` 57 |
58 |
59 |
60 |
61 | `; 62 | 63 | exports[`Storyshots Deep Test No children - No options 1`] = ` 64 |
65 |
66 |
    67 |
  • 68 | Deeper 69 |
  • 70 |
71 |
72 |
73 | 74 | Hello 75 | 76 |
77 |
78 |
79 |
80 | 81 | Hello 82 | 83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | `; 92 | 93 | exports[`Storyshots Deep Test No children - Rename 1`] = ` 94 |
95 |
96 |
97 |
98 | 99 | Hello 100 | 101 |
102 |
103 |
104 |
105 | 106 | Hello 107 | 108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | `; 117 | 118 | exports[`Storyshots Deep Test With children - No options 1`] = ` 119 |
120 |
121 |
122 |
123 | 124 | Hello 125 | 126 |
127 |
128 |
129 |
130 | 131 | Hello 132 | 133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 | `; 142 | 143 | exports[`Storyshots Deep Test With children - Skip 1`] = ` 144 |
145 |
146 |
147 |
148 | 149 | Hello 150 | 151 |
152 |
153 |
154 |
155 | 156 | Hello 157 | 158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 | `; 167 | 168 | exports[`Storyshots Deep Test With children - Skip and rename 1`] = ` 169 |
170 |
171 |
    172 |
  • 173 | Deeper 174 |
  • 175 |
176 |
177 |
178 | 179 | Hello 180 | 181 |
182 |
183 |
184 |
185 | 186 | Hello 187 | 188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 | `; 197 | 198 | exports[`Storyshots Fragments Array 1`] = ` 199 | Array [ 200 |
, 201 |
, 202 | ] 203 | `; 204 | 205 | exports[`Storyshots Fragments Fragment Shorthand 1`] = ` 206 | Array [ 207 |
, 208 |
, 209 | ] 210 | `; 211 | 212 | exports[`Storyshots Fragments Simple Fragment 1`] = ` 213 | Array [ 214 |
, 215 |
, 216 | ] 217 | `; 218 | 219 | exports[`Storyshots Function as a children Deep function - No options 1`] = ` 220 |
221 |
222 | 223 | World 224 | 225 |
226 |
227 | `; 228 | 229 | exports[`Storyshots Function as a children Deep function - Skip 1`] = ` 230 |
231 |
232 | 233 | World 234 | 235 |
236 |
237 | `; 238 | 239 | exports[`Storyshots Function as a children No options 1`] = ` 240 |
241 | 242 | World 243 | 244 |
245 | `; 246 | 247 | exports[`Storyshots Function as a children Skip 1`] = ` 248 |
249 | 250 | World 251 | 252 |
253 | `; 254 | 255 | exports[`Storyshots Function as a children Skip and Rename 1`] = ` 256 |
257 | 258 | World 259 | 260 |
261 | `; 262 | 263 | exports[`Storyshots Simple Test Data strings 1`] = ` 264 |
269 | `; 270 | 271 | exports[`Storyshots Simple Test No children - No options 1`] = ` 272 |
273 | 274 | Hello 275 | 276 |
277 | `; 278 | 279 | exports[`Storyshots Simple Test No children - Rename 1`] = ` 280 |
281 | 282 | Hello 283 | 284 |
285 | `; 286 | 287 | exports[`Storyshots Simple Test With children - No options 1`] = ` 288 |
289 | 290 | Hello 291 | 292 | 293 | World 294 | 295 |
296 | `; 297 | 298 | exports[`Storyshots Simple Test With children - Skip 1`] = ` 299 |
300 | 301 | Hello 302 | 303 | 304 | World 305 | 306 |
307 | `; 308 | 309 | exports[`Storyshots Simple Test With children - Skip and rename 1`] = ` 310 |
311 | 312 | Hello 313 | 314 | 315 | World 316 | 317 |
318 | `; 319 | 320 | exports[`Storyshots With Props No children - No options 1`] = ` 321 |
322 | 323 | Hello 324 | 325 |
326 | `; 327 | 328 | exports[`Storyshots With Props No children - Rename 1`] = ` 329 |
330 | 331 | Hello 332 | 333 |
334 | `; 335 | 336 | exports[`Storyshots With Props With children - No options 1`] = ` 337 |
338 | 339 | Hello 340 | 341 | 342 | World 343 | 344 |
345 | `; 346 | 347 | exports[`Storyshots With Props With children - Skip 1`] = ` 348 |
349 | 350 | Hello 351 | 352 |
353 | 354 | Hello 355 | 356 |
357 |
358 |
359 | `; 360 | 361 | exports[`Storyshots With Props With children - Skip and rename 1`] = ` 362 |
363 | 364 | Hello 365 | 366 |
367 | 368 | Hello 369 | 370 | 371 | World 372 | 373 |
374 |
375 | `; 376 | 377 | exports[`Storyshots With Props With dangerouslySetInnerHTML - onBeforeRender 1`] = ` 378 |
379 | 380 | Hello 381 | 382 |
383 | 384 | Hello 385 | 386 |
Inner HTML
  • 1
  • 2
", 390 | } 391 | } 392 | /> 393 |
394 |
395 | `; 396 | -------------------------------------------------------------------------------- /src/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | import initStoryshots from '@storybook/addon-storyshots'; 2 | 3 | global.requestAnimationFrame = function(callback) { 4 | setTimeout(callback, 0); 5 | }; 6 | 7 | initStoryshots({ 8 | configPath: './example/.storybook' 9 | }); 10 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const ADDON_ID = 'kadira/jsx'; 2 | export const ADDON_PANEL = `${ADDON_ID}/panel`; 3 | 4 | const ADD_JSX = `${ADDON_ID}/add_jsx`; 5 | 6 | export const EVENTS = { ADD_JSX }; 7 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-underscore-dangle, @typescript-eslint/no-explicit-any */ 2 | 3 | import React from 'react'; 4 | import { 5 | addons, 6 | makeDecorator, 7 | StoryContext, 8 | StoryFn, 9 | StoryApi, 10 | ClientStoryApi 11 | } from '@storybook/addons'; 12 | import reactElementToJSXString, { Options } from 'react-element-to-jsx-string'; 13 | import { html as beautifyHTML } from 'js-beautify'; 14 | 15 | import { EVENTS } from './constants'; 16 | import { ComponentMap } from './renderer'; 17 | 18 | export declare const addDecorator: ClientStoryApi['addDecorator']; 19 | export declare type DecoratorFn = Parameters[0]; 20 | 21 | type VueComponent = { 22 | /** The template for the Vue component */ 23 | template?: string; 24 | }; 25 | 26 | interface JSXOptions extends HTMLBeautifyOptions { 27 | /** How many wrappers to skip when rendering the jsx */ 28 | skip?: number; 29 | /** Whether to show the function in the jsx tab */ 30 | showFunctions?: boolean; 31 | /** Whether to format HTML or Vue markup */ 32 | enableBeautify?: boolean; 33 | /** Override the display name used for a component */ 34 | displayName?: string | Options['displayName']; 35 | /** A function ran before the story is rendered */ 36 | onBeforeRender?(dom: string): string; 37 | } 38 | 39 | /** Run the user supplied onBeforeRender function if it exists */ 40 | const applyBeforeRender = (domString: string, options: JSXOptions) => { 41 | if (typeof options.onBeforeRender !== 'function') { 42 | return domString; 43 | } 44 | 45 | return options.onBeforeRender(domString); 46 | }; 47 | 48 | /** Apply the users parameters and render the jsx for a story */ 49 | const renderJsx = (code: React.ReactElement, options: Required) => { 50 | let renderedJSX = code; 51 | let Type = renderedJSX.type; 52 | 53 | for (let i = 0; i < options.skip; i++) { 54 | if (typeof renderedJSX === 'undefined') { 55 | // eslint-disable-next-line no-console 56 | console.warn('Cannot skip undefined element'); 57 | return; 58 | } 59 | 60 | if (React.Children.count(renderedJSX) > 1) { 61 | // eslint-disable-next-line no-console 62 | console.warn('Trying to skip an array of elements'); 63 | return; 64 | } 65 | 66 | if (typeof renderedJSX.props.children === 'undefined') { 67 | // eslint-disable-next-line no-console 68 | console.warn('Not enough children to skip elements.'); 69 | 70 | if (typeof Type === 'function' && Type.name === '') { 71 | renderedJSX = ; 72 | } 73 | } else if (typeof renderedJSX.props.children === 'function') { 74 | renderedJSX = renderedJSX.props.children(); 75 | } else { 76 | renderedJSX = renderedJSX.props.children; 77 | } 78 | } 79 | 80 | if (typeof renderedJSX === 'undefined') { 81 | // eslint-disable-next-line no-console 82 | return console.warn('Too many skip or undefined component'); 83 | } 84 | 85 | while (typeof Type === 'function' && Type.name === '') { 86 | renderedJSX = ; 87 | Type = renderedJSX.type; 88 | } 89 | 90 | const ooo = 91 | typeof options.displayName === 'string' 92 | ? { 93 | ...options, 94 | showFunctions: true, 95 | displayName: () => options.displayName 96 | } 97 | : options; 98 | 99 | return React.Children.map(renderedJSX, c => { 100 | let string = applyBeforeRender( 101 | reactElementToJSXString(c, ooo as Options), 102 | options 103 | ); 104 | const matches = string.match(/\S+=\\"([^"]*)\\"/g); 105 | 106 | if (matches) { 107 | matches.forEach(match => { 108 | string = string.replace(match, match.replace(/"/g, "'")); 109 | }); 110 | } 111 | 112 | return string; 113 | }).join('\n'); 114 | }; 115 | 116 | /** Extract the docs for all components used in a story */ 117 | const getDocs = (story: React.ReactElement) => { 118 | const types: ComponentMap = {}; 119 | 120 | /** Walk the story for components */ 121 | function extract(innerChildren: React.ReactElement) { 122 | if (!innerChildren) { 123 | return; 124 | } 125 | 126 | if (Array.isArray(innerChildren)) { 127 | innerChildren.forEach(extract); 128 | return; 129 | } 130 | 131 | if (innerChildren.props && innerChildren.props.children) { 132 | extract(innerChildren.props.children); 133 | } 134 | 135 | Object.values(innerChildren.props || {}).forEach(prop => { 136 | extract(prop as React.ReactElement); 137 | }); 138 | 139 | if (typeof innerChildren.type !== 'string' && innerChildren.type) { 140 | const childType = innerChildren.type as any; 141 | const name: string = childType.displayName || childType.name; 142 | 143 | if (name && !types[name]) { 144 | types[name] = childType.__docgenInfo; 145 | } 146 | } 147 | } 148 | 149 | extract(story); 150 | 151 | return types; 152 | }; 153 | 154 | const defaultOpts = { 155 | skip: 0, 156 | showDefaultProps: true, 157 | showFunctions: true, 158 | enableBeautify: true 159 | }; 160 | 161 | /** Extract components from story and emit them to the panel */ 162 | export const jsxDecorator = makeDecorator({ 163 | name: 'jsx', 164 | parameterName: 'jsx', 165 | wrapper: (storyFn: any, parameters: StoryContext) => { 166 | const story: ReturnType & VueComponent = storyFn(); 167 | 168 | const channel = addons.getChannel(); 169 | 170 | const options = { 171 | ...defaultOpts, 172 | ...((parameters && parameters.parameters.jsx) || {}) 173 | } as Required; 174 | 175 | let components: ComponentMap = {}; 176 | let jsx = ''; 177 | 178 | if (story.template) { 179 | if (options.enableBeautify) { 180 | jsx = beautifyHTML(story.template, options); 181 | } else { 182 | jsx = story.template; 183 | } 184 | } else { 185 | const rendered = renderJsx(story, options); 186 | 187 | if (rendered) { 188 | jsx = rendered; 189 | components = getDocs(story); 190 | } 191 | } 192 | 193 | channel.emit(EVENTS.ADD_JSX, (parameters || {}).id, jsx, components); 194 | 195 | return story; 196 | } 197 | }); 198 | 199 | export default { 200 | addWithJSX(this: StoryApi, kind: string, storyFn: StoryFn) { 201 | return this.add(kind, context => jsxDecorator(storyFn, context)); 202 | } 203 | }; 204 | -------------------------------------------------------------------------------- /src/jsx.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ActionBar, SyntaxHighlighter } from '@storybook/components'; 3 | import { styled } from '@storybook/theming'; 4 | import copy from 'copy-to-clipboard'; 5 | 6 | import { Listener } from './register'; 7 | import jsxRenderer, { ComponentMap } from './renderer'; 8 | 9 | const Container = styled.div(({ theme }) => ({ 10 | padding: theme.layoutMargin 11 | })); 12 | 13 | interface JSXProps { 14 | /** Whether the panel is active */ 15 | active: boolean; 16 | ob(listener: Listener): void; 17 | } 18 | 19 | /** The panel that renders the jsx for the story */ 20 | const JSX = ({ ob, active }: JSXProps) => { 21 | const [current, setCurrent] = React.useState(undefined); 22 | const [jsx, setJsx] = React.useState>( 23 | {} 24 | ); 25 | 26 | React.useEffect(() => { 27 | ob({ 28 | next: type => { 29 | if (type === 'jsx') { 30 | return (id: string, newJsx: string, components: ComponentMap) => 31 | setJsx({ ...jsx, [id]: [newJsx, components] }); 32 | } 33 | 34 | return setCurrent; 35 | } 36 | }); 37 | // eslint-disable-next-line react-hooks/exhaustive-deps 38 | }, []); 39 | 40 | const [code, components] = current && jsx[current] ? jsx[current] : ['', {}]; 41 | 42 | const copyJsx = React.useCallback(() => copy(code), [code]); 43 | 44 | return active ? ( 45 | <> 46 | 47 | 53 | {code} 54 | 55 | 56 | 64 | 65 | ) : null; 66 | }; 67 | 68 | export default JSX; 69 | -------------------------------------------------------------------------------- /src/register.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { addons } from '@storybook/addons'; 3 | import { STORY_RENDERED } from '@storybook/core-events'; 4 | import Channel from '@storybook/channels'; 5 | 6 | import JSX from './jsx'; 7 | import { ComponentMap } from './renderer'; 8 | import { ADDON_ID, ADDON_PANEL, EVENTS } from './constants'; 9 | 10 | export interface Listener { 11 | next( 12 | scope: 'current' | 'jsx' 13 | ): typeof scope extends 'current' 14 | ? (id: string) => void 15 | : (id: string, jsx: string, components: ComponentMap) => void; 16 | } 17 | 18 | /** A function that lets the panel listen to storybook event */ 19 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 20 | const observable = (channel: Channel, api: any) => (listener: Listener) => { 21 | channel.on(EVENTS.ADD_JSX, listener.next('jsx')); 22 | api.on(STORY_RENDERED, listener.next('current')); 23 | }; 24 | 25 | addons.register(ADDON_ID, api => { 26 | const ob = observable(addons.getChannel(), api); 27 | 28 | addons.addPanel(ADDON_PANEL, { 29 | title: 'JSX', 30 | render: ({ active }) => 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/renderer.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentProps } from 'react'; 2 | import { WithTooltip, TooltipMessage } from '@storybook/components'; 3 | import PrettyPropType from 'storybook-pretty-props'; 4 | 5 | type StyleSheet = Record; 6 | export type ComponentMap = Record< 7 | string, 8 | { 9 | /** The description of the component */ 10 | description?: string; 11 | /** The display name of the component */ 12 | displayName: string; 13 | /** The children of the component */ 14 | children: (Node | string)[]; 15 | /** The props of the component */ 16 | props?: Record< 17 | string, 18 | { 19 | /** Whether the prop is required */ 20 | required?: boolean; 21 | /** The description of the prop */ 22 | description?: string; 23 | /** The type of the prop */ 24 | type: ComponentProps['propType']; 25 | } 26 | >; 27 | } 28 | >; 29 | 30 | /** Combine the classnames and the stylesheet into a style prop */ 31 | function createStyleObject( 32 | classNames: string[], 33 | elementStyle: React.CSSProperties, 34 | stylesheet: StyleSheet 35 | ) { 36 | return classNames.reduce((styleObject, className) => { 37 | return { ...styleObject, ...stylesheet[className] }; 38 | }, elementStyle); 39 | } 40 | 41 | /** Join and array of classnames into one */ 42 | function createClassNameString(classNames: string[]) { 43 | return classNames.join(' '); 44 | } 45 | 46 | interface Node { 47 | /** The type of node */ 48 | type: string; 49 | /** The HTML tag to use to render the node */ 50 | tagName: string; 51 | /** Properties of the HTML node to create */ 52 | properties: { 53 | /** The classNames to put on the node */ 54 | className: string[]; 55 | /** The style object to put on the node */ 56 | style?: React.CSSProperties; 57 | }; 58 | /** The children of the HTML node to create */ 59 | children: React.ReactNode; 60 | /** The value of the node to create */ 61 | value?: string; 62 | } 63 | 64 | /** Render the rows of children to HTML nodes or text */ 65 | function createChildren( 66 | components: ComponentMap, 67 | stylesheet: StyleSheet, 68 | useInlineStyles?: boolean 69 | ) { 70 | let childrenCount = 0; 71 | 72 | return (children: Node[]) => { 73 | childrenCount += 1; 74 | 75 | return children.map((child, i) => 76 | // eslint-disable-next-line @typescript-eslint/no-use-before-define 77 | createElement({ 78 | node: child, 79 | stylesheet, 80 | useInlineStyles, 81 | components, 82 | key: `code-segment-${childrenCount}-${i}` 83 | }) 84 | ); 85 | }; 86 | } 87 | 88 | interface CreateElementOptions { 89 | /** The node to create */ 90 | node: Node; 91 | /** A react key to apply to the node */ 92 | key: string; 93 | /** The stylesheet to use to style the highlighted output */ 94 | stylesheet?: StyleSheet; 95 | /** A map of component used in the story and their docs */ 96 | components: ComponentMap; 97 | /** Whether to inline all of the styles in the highlighted output */ 98 | useInlineStyles?: boolean; 99 | /** The style object to put on the node */ 100 | style?: React.CSSProperties; 101 | } 102 | 103 | const componentStack: string[] = []; 104 | 105 | /** Transform a row of highlighted output into an HTML node */ 106 | function createElement({ 107 | node, 108 | stylesheet = {}, 109 | components, 110 | style = {}, 111 | useInlineStyles, 112 | key 113 | }: CreateElementOptions) { 114 | const { properties, type, tagName, value } = node; 115 | 116 | if (type === 'text') { 117 | return value; 118 | } 119 | 120 | if (tagName) { 121 | const TagName = tagName as keyof JSX.IntrinsicElements; 122 | const childrenCreator = createChildren( 123 | components, 124 | stylesheet, 125 | useInlineStyles 126 | ); 127 | const nonStylesheetClassNames = 128 | useInlineStyles && 129 | properties.className && 130 | properties.className.filter(className => !stylesheet[className]); 131 | const className = 132 | nonStylesheetClassNames && nonStylesheetClassNames.length 133 | ? nonStylesheetClassNames 134 | : ''; 135 | const props = useInlineStyles 136 | ? { 137 | ...properties, 138 | ...{ 139 | className: className ? createClassNameString(className) : className 140 | }, 141 | style: createStyleObject( 142 | properties.className, 143 | { ...properties.style, ...style }, 144 | stylesheet 145 | ) 146 | } 147 | : { 148 | ...properties, 149 | className: createClassNameString(properties.className) 150 | }; 151 | const children = childrenCreator(node.children as Node[]); 152 | 153 | const lastComponent = componentStack[componentStack.length - 1] || ''; 154 | const name = typeof children[0] === 'string' ? children[0] : ''; 155 | const hasDocs = 156 | props.className.includes('class-name') || 157 | props.className.includes('attr-name'); 158 | 159 | if (hasDocs) { 160 | let message; 161 | let title; 162 | 163 | if (props.className.includes('class-name')) { 164 | const docs = components[name] || {}; 165 | 166 | if (docs.description) { 167 | title = children; 168 | message =
{docs.description}
; 169 | } 170 | 171 | componentStack.push(name); 172 | } else if (lastComponent.match(/^[A-Z]/)) { 173 | const { props: componentProps = {} } = components[lastComponent] || {}; 174 | const docs = componentProps[name] || {}; 175 | 176 | if (docs.type || docs.description || docs.required) { 177 | title = ( 178 |
179 | {children} 180 | {docs.required && ( 181 |
Required
182 | )} 183 |
184 | ); 185 | message = ( 186 |
187 |
188 | 189 |
190 | {docs.description} 191 |
192 | ); 193 | } 194 | } 195 | 196 | if (title) { 197 | return ( 198 | } 203 | > 204 | 205 | {children} 206 | 207 | 208 | ); 209 | } 210 | } else if (name === '/>' || name === '>') { 211 | componentStack.pop(); 212 | } 213 | 214 | return ( 215 | 216 | {children} 217 | 218 | ); 219 | } 220 | } 221 | 222 | interface RenderRows { 223 | /** A row to render in the highlighted output */ 224 | rows: Node[]; 225 | /** The stylesheet to use to style the highlighted output */ 226 | stylesheet: StyleSheet; 227 | /** Whether to inline all of the styles in the highlighted output */ 228 | useInlineStyles?: boolean; 229 | } 230 | 231 | /** Render a row from the react-syntax-highlighter output */ 232 | const jsxRenderer = (components: ComponentMap) => ({ 233 | rows, 234 | stylesheet, 235 | useInlineStyles 236 | }: RenderRows) => 237 | rows.map((node, i) => 238 | createElement({ 239 | node, 240 | stylesheet, 241 | useInlineStyles, 242 | components, 243 | key: `code-segement${i}` 244 | }) 245 | ); 246 | 247 | export default jsxRenderer; 248 | -------------------------------------------------------------------------------- /src/types/react-element-to-jsx-string.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'react-element-to-jsx-string' { 2 | export interface Options { 3 | showFunctions?: boolean; 4 | displayName?(): string; 5 | } 6 | 7 | export default function render( 8 | element: React.ReactNode, 9 | options: Options 10 | ): string; 11 | } 12 | -------------------------------------------------------------------------------- /src/types/storybook__ui.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@storybook/ui/dist/core/context'; 2 | -------------------------------------------------------------------------------- /storybook-jsx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/addon-jsx/61f84631e1c4334cf138d1814f9476e0fe6386f9/storybook-jsx.png -------------------------------------------------------------------------------- /storybook-jsx.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/addon-jsx/61f84631e1c4334cf138d1814f9476e0fe6386f9/storybook-jsx.sketch -------------------------------------------------------------------------------- /storybook-jsx.svg: -------------------------------------------------------------------------------- 1 | JSX -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "lib": ["es2017", "dom"], 5 | "jsx": "react", 6 | "moduleResolution": "node", 7 | // "allowSyntheticDefaultImports": true, 8 | "esModuleInterop": true, 9 | "downlevelIteration": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "outDir": "dist", 13 | "declarationDir": "lib", 14 | "emitDeclarationOnly": true, 15 | "preserveConstEnums": true, 16 | "removeComments": false, 17 | "skipLibCheck": true, 18 | "sourceMap": true, 19 | "strict": true, 20 | "typeRoots": ["src/types/", "node_modules/@types/"] 21 | }, 22 | "include": ["src/**/*.ts", "src/**/*.tsx", "typings/**/*"] 23 | } 24 | --------------------------------------------------------------------------------