├── .babelrc ├── .gitignore ├── .npmignore ├── .storybook ├── addons.js └── config.js ├── README.md ├── example └── basicUsage.story.js ├── index.js ├── package.json ├── register.js └── src ├── index.js └── register.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '../src/register'; 2 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure, setAddon } from '@storybook/react' 2 | 3 | import staticMarkup from '../src/' 4 | 5 | setAddon(staticMarkup) // to use addWithStaticMarkup method 6 | 7 | const req = require.context('../example', true, /.story.js$/) 8 | 9 | configure(() => { 10 | req.keys().forEach((filename) => req(filename)) 11 | }, module) 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/react-storybook-addon-static-markup.svg)](https://badge.fury.io/js/react-storybook-addon-static-markup) 2 | 3 | # Static Markup addon for [React Storybook](https://github.com/storybooks/react-storybook) 4 | 5 | Displays a panel with an "html version" of a story, as suggested in [this](https://github.com/storybooks/react-storybook/issues/617) thread. 6 | 7 | ### [Live demo](https://evgenykochetkov.github.io/react-storybook-addon-static-markup/) 8 | 9 | ## Installation 10 | 11 | Install the package: 12 | 13 | ```sh 14 | npm i -D react-storybook-addon-static-markup 15 | ``` 16 | 17 | Then set the addon in your `.storybook/config.js`: 18 | 19 | ```js 20 | import { configure, setAddon } from '@storybook/react' 21 | 22 | import staticMarkup from 'react-storybook-addon-static-markup' 23 | 24 | setAddon(staticMarkup) 25 | 26 | configure(() => { 27 | // ... 28 | }, module) 29 | ``` 30 | 31 | ...and register it in your `.storybook/addons.js`: 32 | ```js 33 | import 'react-storybook-addon-static-markup/register'; 34 | ``` 35 | 36 | 37 | ## Usage 38 | 39 | ```js 40 | import React from 'react'; 41 | import { storiesOf } from '@storybook/react'; 42 | 43 | import { ShowStaticMarkup } from '../src' 44 | 45 | storiesOf('Usage examples', module) 46 | .add( 47 | 'with HOC', 48 | () => ( 49 | 50 | 53 | 54 | ) 55 | ) 56 | .addWithStaticMarkup( 57 | 'with a "shortcut" method', 58 | () => ( 59 |
60 | hello 61 | 64 |
65 | ) 66 | ) 67 | ``` 68 | -------------------------------------------------------------------------------- /example/basicUsage.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | 4 | import { ShowStaticMarkup } from '../src' 5 | 6 | storiesOf('Usage examples', module) 7 | .add( 8 | 'with HOC', 9 | () => ( 10 | 11 | 14 | 15 | ) 16 | ) 17 | .addWithStaticMarkup( 18 | 'with a "shortcut" method', 19 | () => ( 20 |
21 | hello 22 | 25 |
26 | ) 27 | ) 28 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.ShowStaticMarkup = undefined; 7 | 8 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 9 | 10 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 11 | 12 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 13 | 14 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 15 | 16 | var _createClass2 = require('babel-runtime/helpers/createClass'); 17 | 18 | var _createClass3 = _interopRequireDefault(_createClass2); 19 | 20 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 21 | 22 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 23 | 24 | var _inherits2 = require('babel-runtime/helpers/inherits'); 25 | 26 | var _inherits3 = _interopRequireDefault(_inherits2); 27 | 28 | var _react = require('react'); 29 | 30 | var _react2 = _interopRequireDefault(_react); 31 | 32 | var _addons = require('@storybook/addons'); 33 | 34 | var _addons2 = _interopRequireDefault(_addons); 35 | 36 | var _pretty = require('pretty'); 37 | 38 | var _pretty2 = _interopRequireDefault(_pretty); 39 | 40 | var _server = require('react-dom/server'); 41 | 42 | var _server2 = _interopRequireDefault(_server); 43 | 44 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 45 | 46 | var ShowStaticMarkup = exports.ShowStaticMarkup = function (_React$Component) { 47 | (0, _inherits3.default)(ShowStaticMarkup, _React$Component); 48 | 49 | function ShowStaticMarkup() { 50 | (0, _classCallCheck3.default)(this, ShowStaticMarkup); 51 | return (0, _possibleConstructorReturn3.default)(this, (ShowStaticMarkup.__proto__ || (0, _getPrototypeOf2.default)(ShowStaticMarkup)).apply(this, arguments)); 52 | } 53 | 54 | (0, _createClass3.default)(ShowStaticMarkup, [{ 55 | key: 'render', 56 | value: function render() { 57 | var children = this.props.children; 58 | 59 | 60 | var markup = (0, _pretty2.default)(_server2.default.renderToStaticMarkup(children)); 61 | 62 | var channel = _addons2.default.getChannel(); 63 | channel.emit('evgenykochetkov/static-markup/show-markup', markup); 64 | 65 | return children; 66 | } 67 | }]); 68 | return ShowStaticMarkup; 69 | }(_react2.default.Component); 70 | 71 | exports.default = { 72 | addWithStaticMarkup: function addWithStaticMarkup(storyName, story) { 73 | this.add(storyName, function () { 74 | return _react2.default.createElement( 75 | ShowStaticMarkup, 76 | null, 77 | story() 78 | ); 79 | }); 80 | } 81 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-storybook-addon-static-markup", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "storybook": "start-storybook -p 9001 -c .storybook", 8 | "deploy-storybook": "storybook-to-ghpages", 9 | "transpile": "babel --plugins \"transform-runtime\" ./src --out-dir ." 10 | }, 11 | "keywords": [ 12 | "react", 13 | "storybook", 14 | "react-storybook", 15 | "addon", 16 | "plugin" 17 | ], 18 | "author": "Evgeny Kochetkov", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/evgenykochetkov/react-storybook-addon-static-markup.git" 22 | }, 23 | "license": "ISC", 24 | "devDependencies": { 25 | "@kadira/storybook-deployer": "^1.2.0", 26 | "babel-cli": "^6.18.0", 27 | "babel-core": "^6.18.2", 28 | "babel-plugin-transform-runtime": "^6.15.0", 29 | "babel-polyfill": "^6.16.0", 30 | "babel-preset-es2015": "^6.18.0", 31 | "babel-preset-react": "^6.16.0", 32 | "babel-preset-stage-2": "^6.18.0", 33 | "react": "^15.4.1", 34 | "react-dom": "^15.4.1", 35 | "@storybook/react": "3.1.3" 36 | }, 37 | "dependencies": { 38 | "pretty": "^2.0.0" 39 | }, 40 | "peerDependencies": { 41 | "react": "^0.14.7 || ^15.0.0", 42 | "react-dom": "^0.14.7 || ^15.0.0" 43 | }, 44 | "engines": { 45 | "npm": "^3.0.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /register.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 4 | 5 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 6 | 7 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 8 | 9 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 10 | 11 | var _createClass2 = require('babel-runtime/helpers/createClass'); 12 | 13 | var _createClass3 = _interopRequireDefault(_createClass2); 14 | 15 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 16 | 17 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 18 | 19 | var _inherits2 = require('babel-runtime/helpers/inherits'); 20 | 21 | var _inherits3 = _interopRequireDefault(_inherits2); 22 | 23 | var _react = require('react'); 24 | 25 | var _react2 = _interopRequireDefault(_react); 26 | 27 | var _addons = require('@storybook/addons'); 28 | 29 | var _addons2 = _interopRequireDefault(_addons); 30 | 31 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 32 | 33 | var styles = { 34 | markupPanel: { 35 | margin: 10, 36 | fontFamily: 'monospace', 37 | whiteSpace: 'pre', 38 | fontSize: 14, 39 | color: '#444', 40 | width: '100%', 41 | overflow: 'auto' 42 | } 43 | }; 44 | 45 | var StaticMarkup = function (_React$Component) { 46 | (0, _inherits3.default)(StaticMarkup, _React$Component); 47 | 48 | function StaticMarkup() { 49 | var _ref; 50 | 51 | (0, _classCallCheck3.default)(this, StaticMarkup); 52 | 53 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 54 | args[_key] = arguments[_key]; 55 | } 56 | 57 | var _this = (0, _possibleConstructorReturn3.default)(this, (_ref = StaticMarkup.__proto__ || (0, _getPrototypeOf2.default)(StaticMarkup)).call.apply(_ref, [this].concat(args))); 58 | 59 | _this.state = { 60 | markup: '' 61 | }; 62 | 63 | _this.onShowStaticMarkup = _this.onShowStaticMarkup.bind(_this); 64 | return _this; 65 | } 66 | 67 | (0, _createClass3.default)(StaticMarkup, [{ 68 | key: 'onShowStaticMarkup', 69 | value: function onShowStaticMarkup(markup) { 70 | this.setState({ markup: markup }); 71 | } 72 | }, { 73 | key: 'componentDidMount', 74 | value: function componentDidMount() { 75 | var _this2 = this; 76 | 77 | var _props = this.props, 78 | channel = _props.channel, 79 | api = _props.api; 80 | 81 | channel.on('evgenykochetkov/static-markup/show-markup', this.onShowStaticMarkup); 82 | 83 | // Clear the current state on every story change. 84 | this.stopListeningOnStory = api.onStory(function () { 85 | _this2.onShowStaticMarkup(''); 86 | }); 87 | } 88 | }, { 89 | key: 'render', 90 | value: function render() { 91 | var markup = this.state.markup; 92 | 93 | 94 | return _react2.default.createElement( 95 | 'div', 96 | { style: styles.markupPanel }, 97 | markup 98 | ); 99 | } 100 | 101 | // This is some cleanup tasks when the StaticMarkup panel is unmounting. 102 | 103 | }, { 104 | key: 'componentWillUnmount', 105 | value: function componentWillUnmount() { 106 | if (this.stopListeningOnStory) { 107 | this.stopListeningOnStory(); 108 | } 109 | 110 | this.unmounted = true; 111 | var _props2 = this.props, 112 | channel = _props2.channel, 113 | api = _props2.api; 114 | 115 | channel.removeListener('evgenykochetkov/static-markup/show-markup', this.onShowStaticMarkup); 116 | } 117 | }]); 118 | return StaticMarkup; 119 | }(_react2.default.Component); 120 | 121 | // Register the addon with a unique name. 122 | 123 | 124 | _addons2.default.register('evgenykochetkov/static-markup', function (api) { 125 | // Also need to set a unique name to the panel. 126 | _addons2.default.addPanel('evgenykochetkov/static-markup/panel', { 127 | title: 'Static Markup', 128 | render: function render() { 129 | return _react2.default.createElement(StaticMarkup, { channel: _addons2.default.getChannel(), api: api }); 130 | } 131 | }); 132 | }); -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import addons from '@storybook/addons'; 3 | import pretty from 'pretty'; 4 | 5 | import ReactDOMServer from 'react-dom/server' 6 | 7 | export class ShowStaticMarkup extends React.Component { 8 | render() { 9 | const { children } = this.props; 10 | 11 | const markup = pretty(ReactDOMServer.renderToStaticMarkup(children)); 12 | 13 | const channel = addons.getChannel(); 14 | channel.emit('evgenykochetkov/static-markup/show-markup', markup); 15 | 16 | return children; 17 | } 18 | } 19 | 20 | export default { 21 | addWithStaticMarkup (storyName, story) { 22 | this.add(storyName, () => ( 23 | 24 | { story() } 25 | 26 | )) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/register.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import addons from '@storybook/addons'; 3 | 4 | const styles = { 5 | markupPanel: { 6 | margin: 10, 7 | fontFamily: 'monospace', 8 | whiteSpace: 'pre', 9 | fontSize: 14, 10 | color: '#444', 11 | width: '100%', 12 | overflow: 'auto', 13 | } 14 | }; 15 | 16 | class StaticMarkup extends React.Component { 17 | constructor(...args) { 18 | super(...args); 19 | 20 | this.state = { 21 | markup: '' 22 | }; 23 | 24 | this.onShowStaticMarkup = this.onShowStaticMarkup.bind(this); 25 | } 26 | 27 | onShowStaticMarkup(markup) { 28 | this.setState({markup}); 29 | } 30 | 31 | componentDidMount() { 32 | const { channel, api } = this.props; 33 | channel.on('evgenykochetkov/static-markup/show-markup', this.onShowStaticMarkup); 34 | 35 | // Clear the current state on every story change. 36 | this.stopListeningOnStory = api.onStory(() => { 37 | this.onShowStaticMarkup(''); 38 | }); 39 | } 40 | 41 | render() { 42 | const { markup } = this.state; 43 | 44 | return ( 45 |
46 | { markup } 47 |
48 | ); 49 | } 50 | 51 | // This is some cleanup tasks when the StaticMarkup panel is unmounting. 52 | componentWillUnmount() { 53 | if(this.stopListeningOnStory) { 54 | this.stopListeningOnStory(); 55 | } 56 | 57 | this.unmounted = true; 58 | const { channel, api } = this.props; 59 | channel.removeListener('evgenykochetkov/static-markup/show-markup', this.onShowStaticMarkup); 60 | } 61 | } 62 | 63 | // Register the addon with a unique name. 64 | addons.register('evgenykochetkov/static-markup', (api) => { 65 | // Also need to set a unique name to the panel. 66 | addons.addPanel('evgenykochetkov/static-markup/panel', { 67 | title: 'Static Markup', 68 | render: () => ( 69 | 70 | ), 71 | }) 72 | }) 73 | --------------------------------------------------------------------------------