├── .babelrc ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── README.md ├── example ├── .storybook │ ├── addons.js │ └── config.js ├── MyComponent.js ├── MyComponent.story.js ├── MyComponent.test.js ├── list │ ├── index.js │ ├── index.story.js │ └── index.test.js ├── private-card │ ├── index.js │ ├── index.story.js │ └── index.test.js └── withTests.js ├── package.json ├── register.js ├── src ├── colors.js ├── components │ ├── Indicator.js │ ├── TestsPanel.js │ └── TestsPanelTitle.js ├── hoc │ └── provideTests.js ├── index.js ├── register.js ├── styles.js └── withTests.js ├── storybook-addon-jest.gif ├── styles.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { 5 | "browsers": ["last 3 versions", "IE >= 11"] 6 | } 7 | }], 8 | "react" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": ["airbnb", "prettier"], 3 | plugins: ["react"], 4 | rules: { 5 | "arrow-parens": ['error', 'as-needed'], 6 | "comma-dangle": [1, { 7 | "arrays": 'always-multiline', 8 | "objects": 'always-multiline', 9 | "imports": 'always-multiline', 10 | "exports": 'always-multiline', 11 | "functions": "ignore", 12 | }], 13 | "import/prefer-default-export": 0, 14 | "react/forbid-prop-types": 0, 15 | "react/no-danger": 0, 16 | "react/jsx-no-target-blank": 0, 17 | "react/require-default-props": 0, 18 | "import/no-extraneous-dependencies": 2, 19 | "react/jsx-filename-extension": 0, 20 | "jsx-a11y/href-no-hash": 0, 21 | }, 22 | "env": { 23 | "browser": true, 24 | "es6": true, 25 | "commonjs": true, 26 | "node": true, 27 | "jest": true, 28 | }, 29 | "settings": { 30 | "import/resolver": { 31 | "webpack": { 32 | config: 'webpack-development.config.js' 33 | }, 34 | }, 35 | }, 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | .jest-test-results.json 5 | build 6 | package-lock.json 7 | 8 | coverage -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Storybook Addon Actions 2 | This contents of this repo was moved to the Storybook monorepo and the NPM package name has been changed. 3 | 4 | The location of the code is: storybooks/storybook:addons/jest@master 5 | The repo you're looking at now is out of date and no longer maintained. 6 | -------------------------------------------------------------------------------- /example/.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-options/register'; 2 | // --> import 'storybook-addon-jest/register' 3 | import '../../register'; 4 | // --> import 'storybook-addon-jest/styles' 5 | import '../../dist/styles'; 6 | 7 | -------------------------------------------------------------------------------- /example/.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure, addDecorator } from '@storybook/react'; 2 | import { setOptions } from '@storybook/addon-options'; 3 | // --> import { setupJestAddon } from 'storybook-addon-jest'; 4 | 5 | 6 | setOptions({ 7 | name: 'JEST ADDON', 8 | url: 'https://github.com/renaudtertrais/storybook-addon-jest', 9 | downPanelInRight: true, 10 | showLeftPanel: true, 11 | }); 12 | 13 | function loadStories() { 14 | require('../foo/index.story'); 15 | require('../list/index.story'); 16 | require('../MyComponent.story'); 17 | } 18 | 19 | configure(loadStories, module); 20 | -------------------------------------------------------------------------------- /example/MyComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const MyComponent = () => ( 4 |

Hello world

5 | ); 6 | 7 | export default MyComponent; 8 | -------------------------------------------------------------------------------- /example/MyComponent.story.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react'; 3 | import { storiesOf } from '@storybook/react'; 4 | 5 | import withTests from './withTests'; 6 | import MyComponent from './MyComponent'; 7 | 8 | storiesOf('MyComponent', module) 9 | .addDecorator(withTests('MyComponent')) 10 | .add('classic render', () => ( 11 | 12 | )); 13 | -------------------------------------------------------------------------------- /example/MyComponent.test.js: -------------------------------------------------------------------------------- 1 | describe('Description: ', () => { 2 | it('should contain 3 items', () => { 3 | expect(3).toBe(3); 4 | }); 5 | 6 | it('should work fine', () => { 7 | expect(true).toBe(true); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /example/list/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const List = ({ items }) => ; 4 | 5 | List.defaultProps = { 6 | items: [], 7 | }; 8 | 9 | export default List; 10 | -------------------------------------------------------------------------------- /example/list/index.story.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react'; 3 | import { storiesOf } from '@storybook/react'; 4 | 5 | import withTests from '../withTests'; 6 | import List from './index'; 7 | 8 | storiesOf('List', module) 9 | .addDecorator(withTests('list/index')) 10 | .add('3 items', () => ( 11 | 12 | )); 13 | -------------------------------------------------------------------------------- /example/list/index.test.js: -------------------------------------------------------------------------------- 1 | describe('Description: ', () => { 2 | it('should contain 3 items', () => { 3 | expect(3).toBe(3); 4 | }); 5 | 6 | it('should work fine', () => { 7 | expect(true).toBe(true); 8 | }); 9 | }); 10 | 11 | test('Failing test',() => { 12 | expect(['foo', 'bar', 89]).toEqual(['foo', 'bar']); 13 | }) 14 | -------------------------------------------------------------------------------- /example/private-card/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const PrivateCard = ({ firstname, lastname, age, job }) => { 4 | return ( 5 |
6 |
7 | 8 |
9 |
    10 |
  • { firstname }
  • 11 |
  • { lastname }
  • 12 |
  • { job }
  • 13 |
14 | { age >= 18 &&

Can drive

} 15 | { age < 18 &&

Can't drive

} 16 |
17 | ) 18 | } 19 | 20 | export default PrivateCard; 21 | -------------------------------------------------------------------------------- /example/private-card/index.story.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import React from 'react'; 3 | import { storiesOf } from '@storybook/react'; 4 | 5 | import withTests from '../withTests'; 6 | import PrivateCard from './index'; 7 | 8 | storiesOf('PrivateCard', module) 9 | .addDecorator(withTests('private-card/index')) 10 | .add('Minor', () => ( 11 | 12 | )); 13 | -------------------------------------------------------------------------------- /example/private-card/index.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import PrivateCard from './index' 5 | 6 | 7 | describe(' rendering', () => { 8 | it('renders without crashing', () => { 9 | const div = document.createElement('div'); 10 | 11 | ReactDOM.render(, div); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /example/withTests.js: -------------------------------------------------------------------------------- 1 | // ---> import { withTests } from 'storybook-addon-jest'; 2 | import withTests from '../dist'; 3 | 4 | import jestTestResuls from './.jest-test-results.json'; 5 | 6 | 7 | export default withTests(jestTestResuls, { 8 | filesExt: '.test.js', 9 | }); 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "storybook-addon-jest", 3 | "version": "0.0.1", 4 | "description": "React storybook addon that show component jest report", 5 | "main": "dist/index.js", 6 | "homepage": "https://github.com/renaudtertrais/storybook-addon-jest", 7 | "bugs": "https://github.com/renaudtertrais/storybook-addon-jest/issues", 8 | "keywords": [], 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/renaudtertrais/storybook-addon-jest" 12 | }, 13 | "author": "", 14 | "files": [ 15 | "dist", 16 | "styles.js", 17 | "register.js" 18 | ], 19 | "scripts": { 20 | "test:example": "jest example --json --outputFile=example/.jest-test-results.json", 21 | "clean:storybook": "rm -rf build", 22 | "prebuild:storybook": "npm run clean:storybook", 23 | "build:storybook": "build-storybook -c example/.storybook -o build/", 24 | "storybook": "cross-env NODE_ENV=development start-storybook -p 3001 -c example/.storybook", 25 | "prebuild": "npm run clear", 26 | "build": "cross-env NODE_ENV=production babel -d ./dist ./src", 27 | "start": "./node_modules/.bin/cross-env NODE_ENV=production babel -d ./dist ./src && npm run test:example || true && npm run build:storybook && ./node_modules/.bin/serve ./build -p $PORT", 28 | "prepublish": "npm run build", 29 | "clear": "rm -rf ./dist" 30 | }, 31 | "dependencies": {}, 32 | "devDependencies": { 33 | "@storybook/addon-options": "^3.2.14", 34 | "@storybook/react": "^3.2.14", 35 | "babel-cli": "^6.26.0", 36 | "babel-core": "^6.26.0", 37 | "babel-preset-env": "^1.6.1", 38 | "babel-preset-react": "^6.24.1", 39 | "cross-env": "^5.1.4", 40 | "css-loader": "^0.28.7", 41 | "enzyme": "^3.1.1", 42 | "eslint": "^4.10.0", 43 | "eslint-config-airbnb": "^16.1.0", 44 | "eslint-config-prettier": "^2.7.0", 45 | "eslint-import-resolver-webpack": "^0.8.3", 46 | "eslint-plugin-import": "^2.8.0", 47 | "eslint-plugin-jsx-a11y": "^6.0.2", 48 | "eslint-plugin-react": "^7.4.0", 49 | "jest": "^21.2.1", 50 | "react": "^16.1.0", 51 | "react-dom": "^16.1.0", 52 | "serve": "^6.4.1", 53 | "style-loader": "^0.19.0", 54 | "upath": "^1.0.4" 55 | }, 56 | "peerDependencies": { 57 | "@storybook/addons": "^3.2.14", 58 | "prop-types": "^15.6.0", 59 | "react": "^16.0.0", 60 | "react-dom": "^16.0.0" 61 | }, 62 | "license": "MIT" 63 | } 64 | -------------------------------------------------------------------------------- /register.js: -------------------------------------------------------------------------------- 1 | require('./dist/register'); 2 | -------------------------------------------------------------------------------- /src/colors.js: -------------------------------------------------------------------------------- 1 | export default { 2 | success: 'LIGHTSEAGREEN', 3 | error: 'CRIMSON', 4 | warning: 'DARKORANGE', 5 | grey: 'LIGHTSLATEGRAY', 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Indicator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Indicator = ({ color, size, children = '', right }) => ( 4 |
20 | {children} 21 |
22 | ); 23 | 24 | export default Indicator; 25 | -------------------------------------------------------------------------------- /src/components/TestsPanel.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Indicator from './Indicator'; 4 | import provideTests from '../hoc/provideTests'; 5 | import colors from '../colors'; 6 | 7 | const TestsPanel = ({ tests }) => { 8 | const style = { 9 | padding: '10px 20px', 10 | flex: 1, 11 | }; 12 | 13 | if (!tests) { 14 | return ( 15 |
16 |

No tests added.

17 |
18 | ); 19 | } 20 | 21 | return ( 22 |
23 | {tests.map(({ name, result }) => { 24 | if (!result) { 25 | return ( 26 |
27 |
28 |

29 |
{name}
30 | 31 | File not found 32 | 33 |

34 |
35 |
36 | ); 37 | } 38 | 39 | const successNumber = result.assertionResults.filter(({ status }) => status === 'passed') 40 | .length; 41 | const failedNumber = result.assertionResults.length - successNumber; 42 | 43 | return ( 44 |
45 |

53 |
54 |
{name}
55 | 60 | {result.status} 61 | 62 |
63 |
71 | {successNumber > 0 && ( 72 |
73 | {successNumber} passed 74 |
75 | )} 76 | {failedNumber > 0 && ( 77 |
{failedNumber} failed
78 | )} 79 |
{result.assertionResults.length} total
80 |
{result.endTime - result.startTime}ms
81 |
82 | 83 |
93 |
105 |
106 |

107 |
    108 | {result.assertionResults.map(({ fullName, status, failureMessages, title }) => { 109 | const color = status === 'passed' ? colors.success : colors.error; 110 | return ( 111 |
  • 112 |
    119 |
    120 | 121 |
    {fullName || title}
    122 |
    123 |
    124 | 125 | {status} 126 | 127 |
    128 |
    129 | {failureMessages && 130 | failureMessages.map((msg, i) => ( 131 |
    `)
    138 |                               .replace(/\[32m/g, ``)
    139 |                               .replace(/\[39m/g, ``),
    140 |                           }}
    141 |                         />
    142 |                       ))}
    143 |                   
  • 144 | ); 145 | })} 146 |
147 |
148 | ); 149 | })} 150 |
151 | ); 152 | }; 153 | 154 | export default provideTests(TestsPanel); 155 | -------------------------------------------------------------------------------- /src/components/TestsPanelTitle.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import provideTests from '../hoc/provideTests'; 5 | import Indicator from './Indicator'; 6 | import colors from '../colors'; 7 | 8 | const TestPanelTitle = ({ tests }) => { 9 | if (!tests) { 10 | return ( 11 |
12 | 13 |
Tests
14 |
15 | ); 16 | } 17 | 18 | const results = tests.map(report => report.result).filter(report => !!report); 19 | const success = results.reduce((acc, result) => acc & (result.status === 'passed'), true); 20 | const color = success ? colors.success : colors.error; 21 | 22 | return ( 23 |
24 | 25 |
Tests
26 |
27 | ); 28 | }; 29 | 30 | export default provideTests(TestPanelTitle); 31 | -------------------------------------------------------------------------------- /src/hoc/provideTests.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const provideTests = Component => { 4 | class TestProvider extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = {}; 9 | this.onAddTests = this.onAddTests.bind(this); 10 | } 11 | 12 | componentDidMount() { 13 | this.stopListeningOnStory = this.props.api.onStory((kind, storyName) => { 14 | this.onAddTests({}); 15 | }); 16 | 17 | this.props.channel.on('storybook/tests/add_tests', this.onAddTests); 18 | } 19 | 20 | onAddTests({ kind, storyName, tests }) { 21 | this.setState({ kind, storyName, tests }); 22 | } 23 | 24 | componentWillUnmount() { 25 | if (this.stopListeningOnStory) { 26 | this.stopListeningOnStory(); 27 | } 28 | this.props.channel.removeListener('storybook/tests/add_tests', this.onAddTests); 29 | } 30 | 31 | render() { 32 | return ; 33 | } 34 | } 35 | 36 | return TestProvider; 37 | }; 38 | 39 | export default provideTests; 40 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './withTests'; 2 | -------------------------------------------------------------------------------- /src/register.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/no-danger */ 2 | import React from 'react'; 3 | import addons from '@storybook/addons'; 4 | 5 | import TestPanelTitle from './components/TestsPanelTitle'; 6 | import TestsPanel from './components/TestsPanel'; 7 | 8 | // Register the addon with a unique name. 9 | addons.register('storybook/tests', api => { 10 | // Also need to set a unique name to the panel. 11 | addons.addPanel('storybook/tests/panel', { 12 | title: , 13 | render: () => , 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/styles.js: -------------------------------------------------------------------------------- 1 | const styles = ` 2 | @font-face { 3 | font-family: octicons-link; 4 | src: url(data:font/woff;charset=utf-8;base64,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) format('woff'); 5 | } 6 | .markdown-body { 7 | -ms-text-size-adjust: 100%; 8 | -webkit-text-size-adjust: 100%; 9 | line-height: 1.5; 10 | color: #333; 11 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 12 | font-size: 16px; 13 | line-height: 1.5; 14 | word-wrap: break-word; 15 | } 16 | .markdown-body .pl-c { 17 | color: #969896; 18 | } 19 | .markdown-body .pl-c1, 20 | .markdown-body .pl-s .pl-v { 21 | color: #0086b3; 22 | } 23 | .markdown-body .pl-e, 24 | .markdown-body .pl-en { 25 | color: #795da3; 26 | } 27 | .markdown-body .pl-smi, 28 | .markdown-body .pl-s .pl-s1 { 29 | color: #333; 30 | } 31 | .markdown-body .pl-ent { 32 | color: #63a35c; 33 | } 34 | .markdown-body .pl-k { 35 | color: #a71d5d; 36 | } 37 | .markdown-body .pl-s, 38 | .markdown-body .pl-pds, 39 | .markdown-body .pl-s .pl-pse .pl-s1, 40 | .markdown-body .pl-sr, 41 | .markdown-body .pl-sr .pl-cce, 42 | .markdown-body .pl-sr .pl-sre, 43 | .markdown-body .pl-sr .pl-sra { 44 | color: #183691; 45 | } 46 | .markdown-body .pl-v { 47 | color: #ed6a43; 48 | } 49 | .markdown-body .pl-id { 50 | color: #b52a1d; 51 | } 52 | .markdown-body .pl-ii { 53 | color: #f8f8f8; 54 | background-color: #b52a1d; 55 | } 56 | .markdown-body .pl-sr .pl-cce { 57 | font-weight: bold; 58 | color: #63a35c; 59 | } 60 | .markdown-body .pl-ml { 61 | color: #693a17; 62 | } 63 | .markdown-body .pl-mh, 64 | .markdown-body .pl-mh .pl-en, 65 | .markdown-body .pl-ms { 66 | font-weight: bold; 67 | color: #1d3e81; 68 | } 69 | .markdown-body .pl-mq { 70 | color: #008080; 71 | } 72 | .markdown-body .pl-mi { 73 | font-style: italic; 74 | color: #333; 75 | } 76 | .markdown-body .pl-mb { 77 | font-weight: bold; 78 | color: #333; 79 | } 80 | .markdown-body .pl-md { 81 | color: #bd2c00; 82 | background-color: #ffecec; 83 | } 84 | .markdown-body .pl-mi1 { 85 | color: #55a532; 86 | background-color: #eaffea; 87 | } 88 | .markdown-body .pl-mdr { 89 | font-weight: bold; 90 | color: #795da3; 91 | } 92 | .markdown-body .pl-mo { 93 | color: #1d3e81; 94 | } 95 | .markdown-body .octicon { 96 | display: inline-block; 97 | vertical-align: text-top; 98 | fill: currentColor; 99 | } 100 | .markdown-body a { 101 | background-color: transparent; 102 | -webkit-text-decoration-skip: objects; 103 | } 104 | .markdown-body a:active, 105 | .markdown-body a:hover { 106 | outline-width: 0; 107 | } 108 | .markdown-body strong { 109 | font-weight: inherit; 110 | } 111 | .markdown-body strong { 112 | font-weight: bolder; 113 | } 114 | .markdown-body h1 { 115 | font-size: 2em; 116 | margin: 0.67em 0; 117 | } 118 | .markdown-body img { 119 | border-style: none; 120 | } 121 | .markdown-body svg:not(:root) { 122 | overflow: hidden; 123 | } 124 | .markdown-body code, 125 | .markdown-body kbd, 126 | .markdown-body pre { 127 | font-family: monospace, monospace; 128 | font-size: 1em; 129 | } 130 | .markdown-body hr { 131 | box-sizing: content-box; 132 | height: 0; 133 | overflow: visible; 134 | } 135 | .markdown-body input { 136 | font: inherit; 137 | margin: 0; 138 | } 139 | .markdown-body input { 140 | overflow: visible; 141 | } 142 | .markdown-body [type="checkbox"] { 143 | box-sizing: border-box; 144 | padding: 0; 145 | } 146 | .markdown-body * { 147 | box-sizing: border-box; 148 | } 149 | .markdown-body input { 150 | font-family: inherit; 151 | font-size: inherit; 152 | line-height: inherit; 153 | } 154 | .markdown-body a { 155 | color: #4078c0; 156 | text-decoration: none; 157 | } 158 | .markdown-body a:hover, 159 | .markdown-body a:active { 160 | text-decoration: underline; 161 | } 162 | .markdown-body strong { 163 | font-weight: 600; 164 | } 165 | .markdown-body hr { 166 | height: 0; 167 | margin: 15px 0; 168 | overflow: hidden; 169 | background: transparent; 170 | border: 0; 171 | border-bottom: 1px solid #ddd; 172 | } 173 | .markdown-body hr::before { 174 | display: table; 175 | content: ""; 176 | } 177 | .markdown-body hr::after { 178 | display: table; 179 | clear: both; 180 | content: ""; 181 | } 182 | .markdown-body table { 183 | border-spacing: 0; 184 | border-collapse: collapse; 185 | } 186 | .markdown-body td, 187 | .markdown-body th { 188 | padding: 0; 189 | } 190 | .markdown-body h1, 191 | .markdown-body h2, 192 | .markdown-body h3, 193 | .markdown-body h4, 194 | .markdown-body h5, 195 | .markdown-body h6 { 196 | margin-top: 0; 197 | margin-bottom: 0; 198 | } 199 | .markdown-body h1 { 200 | font-size: 32px; 201 | font-weight: 600; 202 | } 203 | .markdown-body h2 { 204 | font-size: 24px; 205 | font-weight: 600; 206 | } 207 | .markdown-body h3 { 208 | font-size: 20px; 209 | font-weight: 600; 210 | } 211 | .markdown-body h4 { 212 | font-size: 16px; 213 | font-weight: 600; 214 | } 215 | .markdown-body h5 { 216 | font-size: 14px; 217 | font-weight: 600; 218 | } 219 | .markdown-body h6 { 220 | font-size: 12px; 221 | font-weight: 600; 222 | } 223 | .markdown-body p { 224 | margin-top: 0; 225 | margin-bottom: 10px; 226 | } 227 | .markdown-body blockquote { 228 | margin: 0; 229 | } 230 | .markdown-body ul, 231 | .markdown-body ol { 232 | padding-left: 0; 233 | margin-top: 0; 234 | margin-bottom: 0; 235 | } 236 | .markdown-body ol ol, 237 | .markdown-body ul ol { 238 | list-style-type: lower-roman; 239 | } 240 | .markdown-body ul ul ol, 241 | .markdown-body ul ol ol, 242 | .markdown-body ol ul ol, 243 | .markdown-body ol ol ol { 244 | list-style-type: lower-alpha; 245 | } 246 | .markdown-body dd { 247 | margin-left: 0; 248 | } 249 | .markdown-body code { 250 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 251 | font-size: 12px; 252 | } 253 | .markdown-body pre { 254 | margin-top: 0; 255 | margin-bottom: 0; 256 | font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace; 257 | } 258 | .markdown-body .octicon { 259 | vertical-align: text-bottom; 260 | } 261 | .markdown-body input { 262 | -webkit-font-feature-settings: "liga" 0; 263 | font-feature-settings: "liga" 0; 264 | } 265 | .markdown-body::before { 266 | display: table; 267 | content: ""; 268 | } 269 | .markdown-body::after { 270 | display: table; 271 | clear: both; 272 | content: ""; 273 | } 274 | .markdown-body>*:first-child { 275 | margin-top: 0 !important; 276 | } 277 | .markdown-body>*:last-child { 278 | margin-bottom: 0 !important; 279 | } 280 | .markdown-body a:not([href]) { 281 | color: inherit; 282 | text-decoration: none; 283 | } 284 | .markdown-body .anchor { 285 | float: left; 286 | padding-right: 4px; 287 | margin-left: -20px; 288 | line-height: 1; 289 | } 290 | .markdown-body .anchor:focus { 291 | outline: none; 292 | } 293 | .markdown-body p, 294 | .markdown-body blockquote, 295 | .markdown-body ul, 296 | .markdown-body ol, 297 | .markdown-body dl, 298 | .markdown-body table, 299 | .markdown-body pre { 300 | margin-top: 0; 301 | margin-bottom: 16px; 302 | } 303 | .markdown-body hr { 304 | height: 0.25em; 305 | padding: 0; 306 | margin: 24px 0; 307 | background-color: #e7e7e7; 308 | border: 0; 309 | } 310 | .markdown-body blockquote { 311 | padding: 0 1em; 312 | color: #777; 313 | border-left: 0.25em solid #ddd; 314 | } 315 | .markdown-body blockquote>:first-child { 316 | margin-top: 0; 317 | } 318 | .markdown-body blockquote>:last-child { 319 | margin-bottom: 0; 320 | } 321 | .markdown-body kbd { 322 | display: inline-block; 323 | padding: 3px 5px; 324 | font-size: 11px; 325 | line-height: 10px; 326 | color: #555; 327 | vertical-align: middle; 328 | background-color: #fcfcfc; 329 | border: solid 1px #ccc; 330 | border-bottom-color: #bbb; 331 | border-radius: 3px; 332 | box-shadow: inset 0 -1px 0 #bbb; 333 | } 334 | .markdown-body h1, 335 | .markdown-body h2, 336 | .markdown-body h3, 337 | .markdown-body h4, 338 | .markdown-body h5, 339 | .markdown-body h6 { 340 | margin-top: 24px; 341 | margin-bottom: 16px; 342 | font-weight: 600; 343 | line-height: 1.25; 344 | } 345 | .markdown-body h1 .octicon-link, 346 | .markdown-body h2 .octicon-link, 347 | .markdown-body h3 .octicon-link, 348 | .markdown-body h4 .octicon-link, 349 | .markdown-body h5 .octicon-link, 350 | .markdown-body h6 .octicon-link { 351 | color: #000; 352 | vertical-align: middle; 353 | visibility: hidden; 354 | } 355 | .markdown-body h1:hover .anchor, 356 | .markdown-body h2:hover .anchor, 357 | .markdown-body h3:hover .anchor, 358 | .markdown-body h4:hover .anchor, 359 | .markdown-body h5:hover .anchor, 360 | .markdown-body h6:hover .anchor { 361 | text-decoration: none; 362 | } 363 | .markdown-body h1:hover .anchor .octicon-link, 364 | .markdown-body h2:hover .anchor .octicon-link, 365 | .markdown-body h3:hover .anchor .octicon-link, 366 | .markdown-body h4:hover .anchor .octicon-link, 367 | .markdown-body h5:hover .anchor .octicon-link, 368 | .markdown-body h6:hover .anchor .octicon-link { 369 | visibility: visible; 370 | } 371 | .markdown-body h1 { 372 | padding-bottom: 0.3em; 373 | font-size: 2em; 374 | border-bottom: 1px solid #eee; 375 | } 376 | .markdown-body h2 { 377 | padding-bottom: 0.3em; 378 | font-size: 1.5em; 379 | border-bottom: 1px solid #eee; 380 | } 381 | .markdown-body h3 { 382 | font-size: 1.25em; 383 | } 384 | .markdown-body h4 { 385 | font-size: 1em; 386 | } 387 | .markdown-body h5 { 388 | font-size: 0.875em; 389 | } 390 | .markdown-body h6 { 391 | font-size: 0.85em; 392 | color: #777; 393 | } 394 | .markdown-body ul, 395 | .markdown-body ol { 396 | padding-left: 2em; 397 | } 398 | .markdown-body ul ul, 399 | .markdown-body ul ol, 400 | .markdown-body ol ol, 401 | .markdown-body ol ul { 402 | margin-top: 0; 403 | margin-bottom: 0; 404 | } 405 | .markdown-body li>p { 406 | margin-top: 16px; 407 | } 408 | .markdown-body li+li { 409 | margin-top: 0.25em; 410 | } 411 | .markdown-body dl { 412 | padding: 0; 413 | } 414 | .markdown-body dl dt { 415 | padding: 0; 416 | margin-top: 16px; 417 | font-size: 1em; 418 | font-style: italic; 419 | font-weight: bold; 420 | } 421 | .markdown-body dl dd { 422 | padding: 0 16px; 423 | margin-bottom: 16px; 424 | } 425 | .markdown-body table { 426 | display: block; 427 | width: 100%; 428 | overflow: auto; 429 | } 430 | .markdown-body table th { 431 | font-weight: bold; 432 | } 433 | .markdown-body table th, 434 | .markdown-body table td { 435 | padding: 6px 13px; 436 | border: 1px solid #ddd; 437 | } 438 | .markdown-body table tr { 439 | background-color: #fff; 440 | border-top: 1px solid #ccc; 441 | } 442 | .markdown-body table tr:nth-child(2n) { 443 | background-color: #f8f8f8; 444 | } 445 | .markdown-body img { 446 | max-width: 100%; 447 | box-sizing: content-box; 448 | background-color: #fff; 449 | } 450 | .markdown-body code { 451 | padding: 0; 452 | padding-top: 0.2em; 453 | padding-bottom: 0.2em; 454 | margin: 0; 455 | font-size: 85%; 456 | background-color: rgba(0,0,0,0.04); 457 | border-radius: 3px; 458 | } 459 | .markdown-body code::before, 460 | .markdown-body code::after { 461 | letter-spacing: -0.2em; 462 | content: "\00a0"; 463 | } 464 | .markdown-body pre { 465 | word-wrap: normal; 466 | } 467 | .markdown-body pre>code { 468 | padding: 0; 469 | margin: 0; 470 | font-size: 100%; 471 | word-break: normal; 472 | white-space: pre; 473 | background: transparent; 474 | border: 0; 475 | } 476 | .markdown-body .highlight { 477 | margin-bottom: 16px; 478 | } 479 | .markdown-body .highlight pre { 480 | margin-bottom: 0; 481 | word-break: normal; 482 | } 483 | .markdown-body .highlight pre, 484 | .markdown-body pre { 485 | padding: 16px; 486 | overflow: auto; 487 | font-size: 85%; 488 | line-height: 1.45; 489 | background-color: #f7f7f7; 490 | border-radius: 3px; 491 | } 492 | .markdown-body pre code { 493 | display: inline; 494 | max-width: auto; 495 | padding: 0; 496 | margin: 0; 497 | overflow: visible; 498 | line-height: inherit; 499 | word-wrap: normal; 500 | background-color: transparent; 501 | border: 0; 502 | } 503 | .markdown-body pre code::before, 504 | .markdown-body pre code::after { 505 | content: normal; 506 | } 507 | .markdown-body .pl-0 { 508 | padding-left: 0 !important; 509 | } 510 | .markdown-body .pl-1 { 511 | padding-left: 3px !important; 512 | } 513 | .markdown-body .pl-2 { 514 | padding-left: 6px !important; 515 | } 516 | .markdown-body .pl-3 { 517 | padding-left: 12px !important; 518 | } 519 | .markdown-body .pl-4 { 520 | padding-left: 24px !important; 521 | } 522 | .markdown-body .pl-5 { 523 | padding-left: 36px !important; 524 | } 525 | .markdown-body .pl-6 { 526 | padding-left: 48px !important; 527 | } 528 | .markdown-body .full-commit .btn-outline:not(:disabled):hover { 529 | color: #4078c0; 530 | border: 1px solid #4078c0; 531 | } 532 | .markdown-body kbd { 533 | display: inline-block; 534 | padding: 3px 5px; 535 | font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace; 536 | line-height: 10px; 537 | color: #555; 538 | vertical-align: middle; 539 | background-color: #fcfcfc; 540 | border: solid 1px #ccc; 541 | border-bottom-color: #bbb; 542 | border-radius: 3px; 543 | box-shadow: inset 0 -1px 0 #bbb; 544 | } 545 | .markdown-body :checked+.radio-label { 546 | position: relative; 547 | z-index: 1; 548 | border-color: #4078c0; 549 | } 550 | .markdown-body .task-list-item { 551 | list-style-type: none; 552 | } 553 | .markdown-body .task-list-item+.task-list-item { 554 | margin-top: 3px; 555 | } 556 | .markdown-body .task-list-item input { 557 | margin: 0 0.2em 0.25em -1.6em; 558 | vertical-align: middle; 559 | } 560 | .markdown-body hr { 561 | border-bottom-color: #eee; 562 | } 563 | `; 564 | 565 | if(!document.getElementById('github-markdown-css')) { 566 | const styleNode = document.createElement('style'); 567 | styleNode.id = 'github-markdown-css'; 568 | styleNode.innerHTML = styles; 569 | 570 | document.head.appendChild(styleNode); 571 | } 572 | 573 | -------------------------------------------------------------------------------- /src/withTests.js: -------------------------------------------------------------------------------- 1 | import addons from '@storybook/addons'; 2 | import { normalize } from 'upath' 3 | 4 | const findTestResults = (testFiles, jestTestResults, jestTestFilesOptions) => 5 | testFiles.map(name => { 6 | const fileName = `${name}${jestTestFilesOptions.filesExt}`; 7 | if (jestTestResults && jestTestResults.testResults) { 8 | return { 9 | fileName, 10 | name, 11 | result: jestTestResults.testResults.find(t => normalize(t.name).includes(fileName)), 12 | }; 13 | } 14 | return { fileName, name }; 15 | }); 16 | 17 | const withTests = (results, options) => (...testFiles) => { 18 | 19 | const emitAddTests = ({ kind, story }) => { 20 | addons.getChannel().emit('storybook/tests/add_tests', { 21 | kind, 22 | storyName: story, 23 | tests: findTestResults(testFiles, results, options), 24 | }); 25 | }; 26 | 27 | return (storyFn, { kind, story }) => { 28 | emitAddTests({ kind, story }); 29 | return storyFn(); 30 | }; 31 | } 32 | 33 | export default withTests; 34 | -------------------------------------------------------------------------------- /storybook-addon-jest.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-eol/storybook-addon-jest/5e2859043c9c486514629d8d86f9c87e98a2bb4e/storybook-addon-jest.gif -------------------------------------------------------------------------------- /styles.js: -------------------------------------------------------------------------------- 1 | require('./dist/styles'); 2 | --------------------------------------------------------------------------------