60 |
Welcome to STORYBOOK
61 |
62 | This is a UI component dev environment for your app.
63 |
64 |
65 | We've added some basic stories inside the src/stories directory.
66 |
67 | A story is a single state of one or more UI components. You can have as many stories as you want.
68 |
69 | (Basically a story is like a visual test case.)
70 |
71 |
72 | See these sample stories for a component called Button.
73 |
74 |
75 | Just like that, you can add your own components as stories.
76 |
77 | Here's how to add your App component as a story.
78 |
${codeBlock}`}}
81 | />
82 |
83 |
84 | Usually we create stories with smaller UI components in the app.
85 | Have a look at the Writing Stories section in our documentation.
86 |
87 |
88 | );
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/server/config/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import webpack from 'webpack';
3 | import babelLoaderConfig from './babel.prod.js';
4 | import {
5 | OccurenceOrderPlugin,
6 | includePaths,
7 | excludePaths,
8 | loadEnv,
9 | nodePaths,
10 | } from './utils';
11 |
12 | export default function () {
13 | const entries = {
14 | preview: [
15 | require.resolve('./polyfills'),
16 | require.resolve('./globals'),
17 | ],
18 | manager: [
19 | require.resolve('./polyfills'),
20 | path.resolve(__dirname, '../../client/manager'),
21 | ],
22 | };
23 |
24 | const config = {
25 | bail: true,
26 | devtool: '#cheap-module-source-map',
27 | entry: entries,
28 | output: {
29 | filename: 'static/[name].[chunkhash].bundle.js',
30 | // Here we set the publicPath to ''.
31 | // This allows us to deploy storybook into subpaths like GitHub pages.
32 | // This works with css and image loaders too.
33 | // This is working for storybook since, we don't use pushState urls and
34 | // relative URLs works always.
35 | publicPath: '',
36 | },
37 | plugins: [
38 | new webpack.DefinePlugin(loadEnv({ production: true })),
39 | new webpack.optimize.DedupePlugin(),
40 | new webpack.optimize.UglifyJsPlugin({
41 | compress: {
42 | screw_ie8: true,
43 | warnings: false,
44 | },
45 | mangle: false,
46 | output: {
47 | comments: false,
48 | screw_ie8: true,
49 | },
50 | }),
51 | ],
52 | module: {
53 | loaders: [
54 | {
55 | test: /\.jsx?$/,
56 | loader: require.resolve('babel-loader'),
57 | query: babelLoaderConfig,
58 | include: includePaths,
59 | exclude: excludePaths,
60 | },
61 | ],
62 | },
63 | resolve: {
64 | // Since we ship with json-loader always, it's better to move extensions to here
65 | // from the default config.
66 | extensions: ['.js', '.json', '.jsx', ''],
67 | // Add support to NODE_PATH. With this we could avoid relative path imports.
68 | // Based on this CRA feature: https://github.com/facebookincubator/create-react-app/issues/253
69 | fallback: nodePaths,
70 | alias: {
71 | // This is to add addon support for NPM2
72 | '@kadira/storybook-addons': require.resolve('@kadira/storybook-addons'),
73 | },
74 | },
75 | };
76 |
77 | // Webpack 2 doesn't have a OccurenceOrderPlugin plugin in the production mode.
78 | // But webpack 1 has it. That's why we do this.
79 | if (OccurenceOrderPlugin) {
80 | config.plugins.unshift(new OccurenceOrderPlugin());
81 | }
82 |
83 | return config;
84 | }
85 |
--------------------------------------------------------------------------------
/src/server/track_usage.js:
--------------------------------------------------------------------------------
1 | /* eslint global-require: 0 */
2 |
3 | // ### WHAT?
4 | //
5 | // We will track anonymous usage of how you use storybook.
6 | // We don't want any personal information.
7 | // We just need to collect following information
8 | //
9 | // * How many time a user runs start-storybook a day
10 | //
11 | // We will send a ping to our server when you run storybook with above information.
12 |
13 | // ### WHY?
14 | //
15 | // We are maintaining React Storybook and we want to improve it.
16 | // We also working on storybooks.io which is storybook related service.
17 | // In order to get a real picture about the storybook usage,
18 | // we need to get some real usage stats, not the amount of NPM downloads.
19 | // This is why we do this.
20 |
21 | // ### CAN I STOP THIS?
22 | //
23 | // You(or your company) may have certain policies.
24 | // If so, you can stop sending these metrics.
25 | // To do that, use --dont-track flag when running React Storybook.
26 | // start-storybook --dont-track -p 9001
27 |
28 | // ### HELP US?
29 | //
30 | // Maintaining a open source project is hard.
31 | // It's even harder for a startup like us (Kadira)
32 | // Help us to make storybook better by sending these few metrics.
33 | // Based on these metrics, we could improve storybook and build a profitable
34 | // service around it. With that, we could continue to maintain and
35 | // improve Storybook.
36 |
37 | import ConfigStore from 'configstore';
38 | import UUID from 'uuid';
39 | import request from 'request';
40 |
41 | const logger = console;
42 |
43 | let DONT_TRACK = false;
44 |
45 | export function getStore() {
46 | const key = 'react-storybook-usage';
47 | const store = new ConfigStore(key);
48 | return store;
49 | }
50 |
51 | export function track() {
52 | if (DONT_TRACK) return;
53 |
54 | const store = getStore();
55 |
56 | // Just a hash to aggregate metrics. Don't use any personal info.
57 | let userId = store.get('userId');
58 | if (!userId) {
59 | userId = UUID.v4();
60 | store.set('userId', userId);
61 | }
62 |
63 | const pkg = require('../../package.json');
64 |
65 | // We don't wanna worry about the success or failure of this.
66 | request.post('https://ping.getstorybook.io/react-storybook-usage', {
67 | json: {
68 | userId,
69 | version: pkg.version,
70 | },
71 | }, () => {});
72 |
73 | if (!store.get('startTrackingOn')) {
74 | store.set('startTrackingOn', Date.now());
75 | }
76 |
77 | const pingsSent = store.get('pingsSent') || 0;
78 | store.set('pingsSent', pingsSent + 1);
79 |
80 | if (pingsSent < 5) {
81 | logger.log(' We will collect some anonymous usage stats of how you use storybook.');
82 | logger.log(' See why?: https://getstorybook.io/tracking');
83 | logger.log();
84 | }
85 | }
86 |
87 | export function dontTrack() {
88 | DONT_TRACK = true;
89 | }
90 |
--------------------------------------------------------------------------------
/dist/server/config/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | exports.default = function () {
8 | var entries = {
9 | preview: [require.resolve('./polyfills'), require.resolve('./globals')],
10 | manager: [require.resolve('./polyfills'), _path2.default.resolve(__dirname, '../../client/manager')]
11 | };
12 |
13 | var config = {
14 | bail: true,
15 | devtool: '#cheap-module-source-map',
16 | entry: entries,
17 | output: {
18 | filename: 'static/[name].[chunkhash].bundle.js',
19 | // Here we set the publicPath to ''.
20 | // This allows us to deploy storybook into subpaths like GitHub pages.
21 | // This works with css and image loaders too.
22 | // This is working for storybook since, we don't use pushState urls and
23 | // relative URLs works always.
24 | publicPath: ''
25 | },
26 | plugins: [new _webpack2.default.DefinePlugin((0, _utils.loadEnv)({ production: true })), new _webpack2.default.optimize.DedupePlugin(), new _webpack2.default.optimize.UglifyJsPlugin({
27 | compress: {
28 | screw_ie8: true,
29 | warnings: false
30 | },
31 | mangle: false,
32 | output: {
33 | comments: false,
34 | screw_ie8: true
35 | }
36 | })],
37 | module: {
38 | loaders: [{
39 | test: /\.jsx?$/,
40 | loader: require.resolve('babel-loader'),
41 | query: _babelProd2.default,
42 | include: _utils.includePaths,
43 | exclude: _utils.excludePaths
44 | }]
45 | },
46 | resolve: {
47 | // Since we ship with json-loader always, it's better to move extensions to here
48 | // from the default config.
49 | extensions: ['.js', '.json', '.jsx', ''],
50 | // Add support to NODE_PATH. With this we could avoid relative path imports.
51 | // Based on this CRA feature: https://github.com/facebookincubator/create-react-app/issues/253
52 | fallback: _utils.nodePaths,
53 | alias: {
54 | // This is to add addon support for NPM2
55 | '@kadira/storybook-addons': require.resolve('@kadira/storybook-addons')
56 | }
57 | }
58 | };
59 |
60 | // Webpack 2 doesn't have a OccurenceOrderPlugin plugin in the production mode.
61 | // But webpack 1 has it. That's why we do this.
62 | if (_utils.OccurenceOrderPlugin) {
63 | config.plugins.unshift(new _utils.OccurenceOrderPlugin());
64 | }
65 |
66 | return config;
67 | };
68 |
69 | var _path = require('path');
70 |
71 | var _path2 = _interopRequireDefault(_path);
72 |
73 | var _webpack = require('webpack');
74 |
75 | var _webpack2 = _interopRequireDefault(_webpack);
76 |
77 | var _babelProd = require('./babel.prod.js');
78 |
79 | var _babelProd2 = _interopRequireDefault(_babelProd);
80 |
81 | var _utils = require('./utils');
82 |
83 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
--------------------------------------------------------------------------------
/dist/client/preview/config_api.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
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 _actions = require('./actions');
16 |
17 | var _ = require('./');
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20 |
21 | /* globals location */
22 |
23 | var ConfigApi = function () {
24 | function ConfigApi(_ref) {
25 | var channel = _ref.channel;
26 | var storyStore = _ref.storyStore;
27 | var reduxStore = _ref.reduxStore;
28 | (0, _classCallCheck3.default)(this, ConfigApi);
29 |
30 | // channel can be null when running in node
31 | // always check whether channel is available
32 | this._channel = channel;
33 | this._storyStore = storyStore;
34 | this._reduxStore = reduxStore;
35 | }
36 |
37 | (0, _createClass3.default)(ConfigApi, [{
38 | key: '_renderMain',
39 | value: function _renderMain(loaders) {
40 | if (loaders) loaders();
41 |
42 | var stories = this._storyStore.dumpStoryBook();
43 | // send to the parent frame.
44 | this._channel.emit('setStories', { stories: stories });
45 |
46 | // clear the error if exists.
47 | this._reduxStore.dispatch((0, _actions.clearError)());
48 | this._reduxStore.dispatch((0, _actions.setInitialStory)(stories));
49 | }
50 | }, {
51 | key: '_renderError',
52 | value: function _renderError(e) {
53 | var stack = e.stack;
54 | var message = e.message;
55 |
56 | var error = { stack: stack, message: message };
57 | this._reduxStore.dispatch((0, _actions.setError)(error));
58 | }
59 | }, {
60 | key: 'configure',
61 | value: function configure(loaders, module) {
62 | var _this = this;
63 |
64 | var render = function render() {
65 | try {
66 | _this._renderMain(loaders);
67 | } catch (error) {
68 | if (module.hot && module.hot.status() === 'apply') {
69 | // We got this issue, after webpack fixed it and applying it.
70 | // Therefore error message is displayed forever even it's being fixed.
71 | // So, we'll detect it reload the page.
72 | location.reload();
73 | } else {
74 | // If we are accessing the site, but the error is not fixed yet.
75 | // There we can render the error.
76 | _this._renderError(error);
77 | }
78 | }
79 | };
80 |
81 | if (module.hot) {
82 | module.hot.accept(function () {
83 | setTimeout(render);
84 | });
85 | module.hot.dispose(function () {
86 | (0, _.clearDecorators)();
87 | });
88 | }
89 |
90 | if (this._channel) {
91 | render();
92 | } else {
93 | loaders();
94 | }
95 | }
96 | }]);
97 | return ConfigApi;
98 | }();
99 |
100 | exports.default = ConfigApi;
--------------------------------------------------------------------------------
/dist/client/preview/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.configure = exports.getStorybook = exports.clearDecorators = exports.addDecorator = exports.setAddon = exports.storiesOf = undefined;
7 |
8 | var _assign = require('babel-runtime/core-js/object/assign');
9 |
10 | var _assign2 = _interopRequireDefault(_assign);
11 |
12 | var _redux = require('redux');
13 |
14 | var _storybookAddons = require('@kadira/storybook-addons');
15 |
16 | var _storybookAddons2 = _interopRequireDefault(_storybookAddons);
17 |
18 | var _storybookChannelPostmsg = require('@kadira/storybook-channel-postmsg');
19 |
20 | var _storybookChannelPostmsg2 = _interopRequireDefault(_storybookChannelPostmsg);
21 |
22 | var _qs = require('qs');
23 |
24 | var _qs2 = _interopRequireDefault(_qs);
25 |
26 | var _story_store = require('./story_store');
27 |
28 | var _story_store2 = _interopRequireDefault(_story_store);
29 |
30 | var _client_api = require('./client_api');
31 |
32 | var _client_api2 = _interopRequireDefault(_client_api);
33 |
34 | var _config_api = require('./config_api');
35 |
36 | var _config_api2 = _interopRequireDefault(_config_api);
37 |
38 | var _render = require('./render');
39 |
40 | var _render2 = _interopRequireDefault(_render);
41 |
42 | var _init = require('./init');
43 |
44 | var _init2 = _interopRequireDefault(_init);
45 |
46 | var _actions = require('./actions');
47 |
48 | var _reducer = require('./reducer');
49 |
50 | var _reducer2 = _interopRequireDefault(_reducer);
51 |
52 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
53 |
54 | // check whether we're running on node/browser
55 | var _global = global; /* global window */
56 |
57 | var navigator = _global.navigator;
58 |
59 | var isBrowser = navigator && navigator.userAgent !== 'storyshots';
60 |
61 | var storyStore = new _story_store2.default();
62 | var reduxStore = (0, _redux.createStore)(_reducer2.default);
63 | var context = { storyStore: storyStore, reduxStore: reduxStore };
64 |
65 | if (isBrowser) {
66 | var queryParams = _qs2.default.parse(window.location.search.substring(1));
67 | var channel = (0, _storybookChannelPostmsg2.default)({ page: 'preview' });
68 | channel.on('setCurrentStory', function (data) {
69 | reduxStore.dispatch((0, _actions.selectStory)(data.kind, data.story));
70 | });
71 | (0, _assign2.default)(context, { channel: channel, window: window, queryParams: queryParams });
72 | _storybookAddons2.default.setChannel(channel);
73 | (0, _init2.default)(context);
74 | }
75 |
76 | var clientApi = new _client_api2.default(context);
77 | var configApi = new _config_api2.default(context);
78 |
79 | // do exports
80 | var storiesOf = exports.storiesOf = clientApi.storiesOf.bind(clientApi);
81 | var setAddon = exports.setAddon = clientApi.setAddon.bind(clientApi);
82 | var addDecorator = exports.addDecorator = clientApi.addDecorator.bind(clientApi);
83 | var clearDecorators = exports.clearDecorators = clientApi.clearDecorators.bind(clientApi);
84 | var getStorybook = exports.getStorybook = clientApi.getStorybook.bind(clientApi);
85 | var configure = exports.configure = configApi.configure.bind(configApi);
86 |
87 | // initialize the UI
88 | var renderUI = function renderUI() {
89 | if (isBrowser) {
90 | (0, _render2.default)(context);
91 | }
92 | };
93 |
94 | reduxStore.subscribe(renderUI);
--------------------------------------------------------------------------------
/dist/server/track_usage.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.getStore = getStore;
7 | exports.track = track;
8 | exports.dontTrack = dontTrack;
9 |
10 | var _configstore = require('configstore');
11 |
12 | var _configstore2 = _interopRequireDefault(_configstore);
13 |
14 | var _uuid = require('uuid');
15 |
16 | var _uuid2 = _interopRequireDefault(_uuid);
17 |
18 | var _request = require('request');
19 |
20 | var _request2 = _interopRequireDefault(_request);
21 |
22 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23 |
24 | var logger = console; /* eslint global-require: 0 */
25 |
26 | // ### WHAT?
27 | //
28 | // We will track anonymous usage of how you use storybook.
29 | // We don't want any personal information.
30 | // We just need to collect following information
31 | //
32 | // * How many time a user runs start-storybook a day
33 | //
34 | // We will send a ping to our server when you run storybook with above information.
35 |
36 | // ### WHY?
37 | //
38 | // We are maintaining React Storybook and we want to improve it.
39 | // We also working on storybooks.io which is storybook related service.
40 | // In order to get a real picture about the storybook usage,
41 | // we need to get some real usage stats, not the amount of NPM downloads.
42 | // This is why we do this.
43 |
44 | // ### CAN I STOP THIS?
45 | //
46 | // You(or your company) may have certain policies.
47 | // If so, you can stop sending these metrics.
48 | // To do that, use --dont-track flag when running React Storybook.
49 | // start-storybook --dont-track -p 9001
50 |
51 | // ### HELP US?
52 | //
53 | // Maintaining a open source project is hard.
54 | // It's even harder for a startup like us (Kadira)
55 | // Help us to make storybook better by sending these few metrics.
56 | // Based on these metrics, we could improve storybook and build a profitable
57 | // service around it. With that, we could continue to maintain and
58 | // improve Storybook.
59 |
60 | var DONT_TRACK = false;
61 |
62 | function getStore() {
63 | var key = 'react-storybook-usage';
64 | var store = new _configstore2.default(key);
65 | return store;
66 | }
67 |
68 | function track() {
69 | if (DONT_TRACK) return;
70 |
71 | var store = getStore();
72 |
73 | // Just a hash to aggregate metrics. Don't use any personal info.
74 | var userId = store.get('userId');
75 | if (!userId) {
76 | userId = _uuid2.default.v4();
77 | store.set('userId', userId);
78 | }
79 |
80 | var pkg = require('../../package.json');
81 |
82 | // We don't wanna worry about the success or failure of this.
83 | _request2.default.post('https://ping.getstorybook.io/react-storybook-usage', {
84 | json: {
85 | userId: userId,
86 | version: pkg.version
87 | }
88 | }, function () {});
89 |
90 | if (!store.get('startTrackingOn')) {
91 | store.set('startTrackingOn', Date.now());
92 | }
93 |
94 | var pingsSent = store.get('pingsSent') || 0;
95 | store.set('pingsSent', pingsSent + 1);
96 |
97 | if (pingsSent < 5) {
98 | logger.log(' We will collect some anonymous usage stats of how you use storybook.');
99 | logger.log(' See why?: https://getstorybook.io/tracking');
100 | logger.log();
101 | }
102 | }
103 |
104 | function dontTrack() {
105 | DONT_TRACK = true;
106 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "storybook",
3 | "version": "2.31.0",
4 | "description": "React Storybook: Isolate React Component Development with Hot Reloading.",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/kadirahq/react-storybook.git"
8 | },
9 | "typings": "./config/storybook.d.ts",
10 | "license": "MIT",
11 | "options": {
12 | "mocha": "--require scripts/mocha_runner src/**/__tests__/**/*.js"
13 | },
14 | "scripts": {
15 | "prepublish": "sh scripts/prepublish.sh",
16 | "lint": "eslint src",
17 | "lintfix": "eslint src --fix",
18 | "testonly": "mocha $npm_package_options_mocha",
19 | "test": "npm run lint && npm run testonly",
20 | "test-watch": "npm run testonly -- --watch --watch-extensions js",
21 | "dev": "DEV_BUILD=1 nodemon --watch src --exec 'npm run prepublish'",
22 | "postpublish": "greenkeeper-postpublish"
23 | },
24 | "dependencies": {
25 | "@kadira/react-split-pane": "^1.4.0",
26 | "@kadira/storybook-addon-actions": "^1.0.2",
27 | "@kadira/storybook-addon-links": "^1.0.0",
28 | "@kadira/storybook-addons": "^1.5.0",
29 | "@kadira/storybook-channel-postmsg": "^2.0.0",
30 | "@kadira/storybook-ui": "^3.8.0",
31 | "airbnb-js-shims": "^1.0.1",
32 | "autoprefixer": "^6.3.7",
33 | "babel-core": "^6.11.4",
34 | "babel-loader": "^6.2.4",
35 | "babel-plugin-react-docgen": "^1.4.1",
36 | "babel-preset-react-app": "^1.0.0",
37 | "babel-runtime": "^6.9.2",
38 | "case-sensitive-paths-webpack-plugin": "^1.1.2",
39 | "chalk": "^1.1.3",
40 | "commander": "^2.9.0",
41 | "common-tags": "^1.3.1",
42 | "configstore": "^2.0.0",
43 | "css-loader": "0.25.0",
44 | "express": "^4.13.3",
45 | "file-loader": "^0.9.0",
46 | "find-cache-dir": "^0.1.1",
47 | "json-loader": "^0.5.4",
48 | "json-stringify-safe": "^5.0.1",
49 | "json5": "^0.5.0",
50 | "lodash.pick": "^4.2.0",
51 | "postcss-loader": "1.1.0",
52 | "qs": "^6.1.0",
53 | "react-modal": "^1.2.0",
54 | "redux": "^3.5.2",
55 | "request": "^2.74.0",
56 | "serve-favicon": "^2.3.0",
57 | "shelljs": "^0.7.4",
58 | "style-loader": "0.13.1",
59 | "url-loader": "^0.5.7",
60 | "uuid": "^2.0.3",
61 | "webpack": "^1.13.1",
62 | "webpack-dev-middleware": "^1.6.0",
63 | "webpack-hot-middleware": "^2.13.2"
64 | },
65 | "peerDependencies": {
66 | "react": "^0.14.7 || ^15.0.0",
67 | "react-dom": "^0.14.7 || ^15.0.0"
68 | },
69 | "devDependencies": {
70 | "babel-cli": "^6.11.4",
71 | "babel-eslint": "^6.1.2",
72 | "babel-preset-stage-0": "^6.5.0",
73 | "chai": "^3.5.0",
74 | "deep-equal": "^1.0.1",
75 | "enzyme": "^2.2.0",
76 | "eslint": "^3.5.0",
77 | "eslint-config-airbnb": "^11.1.0",
78 | "eslint-plugin-import": "^1.13.0",
79 | "eslint-plugin-jsx-a11y": "^2.1.0",
80 | "eslint-plugin-react": "^6.1.2",
81 | "expect": "^1.6.0",
82 | "greenkeeper-postpublish": "^1.0.1",
83 | "jsdom": "^9.5.0",
84 | "mocha": "^3.0.2",
85 | "mock-fs": "^3.8.0",
86 | "nodemon": "^1.9.1",
87 | "react": "^15.0.0",
88 | "react-addons-test-utils": "^15.0.1",
89 | "react-dom": "^15.0.0",
90 | "sinon": "^1.17.3"
91 | },
92 | "main": "dist/client/index.js",
93 | "bin": {
94 | "start-storybook": "./dist/server/index.js",
95 | "build-storybook": "./dist/server/build.js",
96 | "storybook-server": "./dist/server/index.js"
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/server/build.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import webpack from 'webpack';
4 | import program from 'commander';
5 | import path from 'path';
6 | import fs from 'fs';
7 | import chalk from 'chalk';
8 | import shelljs from 'shelljs';
9 | import packageJson from '../../package.json';
10 | import getBaseConfig from './config/webpack.config.prod';
11 | import loadConfig from './config';
12 | import getIndexHtml from './index.html';
13 | import getIframeHtml from './iframe.html';
14 | import { getHeadHtml, parseList, getEnvConfig } from './utils';
15 |
16 | process.env.NODE_ENV = process.env.NODE_ENV || 'production';
17 |
18 | // avoid ESLint errors
19 | const logger = console;
20 |
21 | program
22 | .version(packageJson.version)
23 | .option('-s, --static-dir
', 'Directory where to load static files from', parseList)
24 | .option('-o, --output-dir [dir-name]', 'Directory where to store built files')
25 | .option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from')
26 | .option('-d, --db-path [db-file]', 'DEPRECATED!')
27 | .option('--enable-db', 'DEPRECATED!')
28 | .parse(process.argv);
29 |
30 | logger.info(chalk.bold(`${packageJson.name} v${packageJson.version}\n`));
31 |
32 | if (program.enableDb || program.dbPath) {
33 | logger.error([
34 | 'Error: the experimental local database addon is no longer bundled with',
35 | 'react-storybook. Please remove these flags (-d,--db-path,--enable-db)',
36 | 'from the command or npm script and try again.',
37 | ].join(' '));
38 | process.exit(1);
39 | }
40 |
41 | // The key is the field created in `program` variable for
42 | // each command line argument. Value is the env variable.
43 | getEnvConfig(program, {
44 | staticDir: 'SBCONFIG_STATIC_DIR',
45 | outputDir: 'SBCONFIG_OUTPUT_DIR',
46 | configDir: 'SBCONFIG_CONFIG_DIR',
47 | });
48 |
49 | const configDir = program.configDir || './.storybook';
50 | const outputDir = program.outputDir || './storybook-static';
51 |
52 | // create output directory (and the static dir) if not exists
53 | shelljs.rm('-rf', outputDir);
54 | shelljs.mkdir('-p', path.resolve(outputDir));
55 | shelljs.cp(path.resolve(__dirname, 'public/favicon.ico'), outputDir);
56 |
57 | // Build the webpack configuration using the `baseConfig`
58 | // custom `.babelrc` file and `webpack.config.js` files
59 | // NOTE changes to env should be done before calling `getBaseConfig`
60 | const config = loadConfig('PRODUCTION', getBaseConfig(), configDir);
61 | config.output.path = outputDir;
62 |
63 | // copy all static files
64 | if (program.staticDir) {
65 | program.staticDir.forEach((dir) => {
66 | if (!fs.existsSync(dir)) {
67 | logger.error(`Error: no such directory to load static files: ${dir}`);
68 | process.exit(-1);
69 | }
70 | logger.log(`=> Copying static files from: ${dir}`);
71 | shelljs.cp('-r', `${dir}/*`, outputDir);
72 | });
73 | }
74 |
75 | // compile all resources with webpack and write them to the disk.
76 | logger.log('Building storybook ...');
77 | webpack(config).run(function (err, stats) {
78 | if (err) {
79 | logger.error('Failed to build the storybook');
80 | logger.error(err.message);
81 | process.exit(1);
82 | }
83 |
84 | const data = {
85 | publicPath: config.output.publicPath,
86 | assets: stats.toJson().assetsByChunkName,
87 | };
88 | const headHtml = getHeadHtml(configDir);
89 |
90 | // Write both the storybook UI and IFRAME HTML files to destination path.
91 | fs.writeFileSync(path.resolve(outputDir, 'index.html'), getIndexHtml(data));
92 | fs.writeFileSync(path.resolve(outputDir, 'iframe.html'), getIframeHtml({ ...data, headHtml }));
93 | });
94 |
--------------------------------------------------------------------------------
/dist/client/manager/provider.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
8 |
9 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
10 |
11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
12 |
13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
14 |
15 | var _createClass2 = require('babel-runtime/helpers/createClass');
16 |
17 | var _createClass3 = _interopRequireDefault(_createClass2);
18 |
19 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
20 |
21 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
22 |
23 | var _inherits2 = require('babel-runtime/helpers/inherits');
24 |
25 | var _inherits3 = _interopRequireDefault(_inherits2);
26 |
27 | var _qs = require('qs');
28 |
29 | var _qs2 = _interopRequireDefault(_qs);
30 |
31 | var _react = require('react');
32 |
33 | var _react2 = _interopRequireDefault(_react);
34 |
35 | var _storybookUi = require('@kadira/storybook-ui');
36 |
37 | var _storybookAddons = require('@kadira/storybook-addons');
38 |
39 | var _storybookAddons2 = _interopRequireDefault(_storybookAddons);
40 |
41 | var _storybookChannelPostmsg = require('@kadira/storybook-channel-postmsg');
42 |
43 | var _storybookChannelPostmsg2 = _interopRequireDefault(_storybookChannelPostmsg);
44 |
45 | var _preview = require('./preview');
46 |
47 | var _preview2 = _interopRequireDefault(_preview);
48 |
49 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
50 |
51 | /* global location */
52 | /* eslint class-methods-use-this: 0 */
53 |
54 | var ReactProvider = function (_Provider) {
55 | (0, _inherits3.default)(ReactProvider, _Provider);
56 |
57 | function ReactProvider() {
58 | (0, _classCallCheck3.default)(this, ReactProvider);
59 |
60 | var _this = (0, _possibleConstructorReturn3.default)(this, (ReactProvider.__proto__ || (0, _getPrototypeOf2.default)(ReactProvider)).call(this));
61 |
62 | _this.channel = (0, _storybookChannelPostmsg2.default)({ page: 'manager' });
63 | _storybookAddons2.default.setChannel(_this.channel);
64 | return _this;
65 | }
66 |
67 | (0, _createClass3.default)(ReactProvider, [{
68 | key: 'getPanels',
69 | value: function getPanels() {
70 | return _storybookAddons2.default.getPanels();
71 | }
72 | }, {
73 | key: 'renderPreview',
74 | value: function renderPreview(selectedKind, selectedStory) {
75 | var queryParams = {
76 | selectedKind: selectedKind,
77 | selectedStory: selectedStory
78 | };
79 |
80 | var queryString = _qs2.default.stringify(queryParams);
81 | var url = 'iframe.html?' + queryString;
82 | return _react2.default.createElement(_preview2.default, { url: url });
83 | }
84 | }, {
85 | key: 'handleAPI',
86 | value: function handleAPI(api) {
87 | var _this2 = this;
88 |
89 | api.onStory(function (kind, story) {
90 | _this2.channel.emit('setCurrentStory', { kind: kind, story: story });
91 | });
92 | this.channel.on('setStories', function (data) {
93 | api.setStories(data.stories);
94 | });
95 | this.channel.on('selectStory', function (data) {
96 | api.selectStory(data.kind, data.story);
97 | });
98 | this.channel.on('applyShortcut', function (data) {
99 | api.handleShortcut(data.event);
100 | });
101 | _storybookAddons2.default.loadAddons(api);
102 | }
103 | }]);
104 | return ReactProvider;
105 | }(_storybookUi.Provider);
106 |
107 | exports.default = ReactProvider;
--------------------------------------------------------------------------------
/src/client/preview/render.js:
--------------------------------------------------------------------------------
1 | /* global document */
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 | import { stripIndents } from 'common-tags';
6 | import ErrorDisplay from './error_display';
7 |
8 | // check whether we're running on node/browser
9 | const isBrowser = typeof window !== 'undefined';
10 |
11 | const logger = console;
12 |
13 | let rootEl = null;
14 | let previousKind = '';
15 | let previousStory = '';
16 |
17 | if (isBrowser) {
18 | rootEl = document.getElementById('root');
19 | }
20 |
21 | export function renderError(error) {
22 | const properError = new Error(error.title);
23 | properError.stack = error.description;
24 |
25 | const redBox = ();
26 | ReactDOM.render(redBox, rootEl);
27 | }
28 |
29 | export function renderException(error) {
30 | // We always need to render redbox in the mainPage if we get an error.
31 | // Since this is an error, this affects to the main page as well.
32 | const realError = new Error(error.message);
33 | realError.stack = error.stack;
34 | const redBox = ();
35 | ReactDOM.render(redBox, rootEl);
36 |
37 | // Log the stack to the console. So, user could check the source code.
38 | logger.error(error.stack);
39 | }
40 |
41 | export function renderMain(data, storyStore) {
42 | if (storyStore.size() === 0) return null;
43 |
44 | const NoPreview = () => (No Preview Available!
);
45 | const noPreview = ();
46 | const { selectedKind, selectedStory } = data;
47 |
48 | const story = storyStore.getStory(selectedKind, selectedStory);
49 | if (!story) {
50 | ReactDOM.render(noPreview, rootEl);
51 | return null;
52 | }
53 |
54 | // Unmount the previous story only if selectedKind or selectedStory has changed.
55 | // renderMain() gets executed after each action. Actions will cause the whole
56 | // story to re-render without this check.
57 | // https://github.com/kadirahq/react-storybook/issues/116
58 | if (selectedKind !== previousKind || previousStory !== selectedStory) {
59 | // We need to unmount the existing set of components in the DOM node.
60 | // Otherwise, React may not recrease instances for every story run.
61 | // This could leads to issues like below:
62 | // https://github.com/kadirahq/react-storybook/issues/81
63 | previousKind = selectedKind;
64 | previousStory = selectedStory;
65 | ReactDOM.unmountComponentAtNode(rootEl);
66 | }
67 |
68 | const context = {
69 | kind: selectedKind,
70 | story: selectedStory,
71 | };
72 |
73 | const element = story(context);
74 |
75 | if (!element) {
76 | const error = {
77 | title: `Expecting a React element from the story: "${selectedStory}" of "${selectedKind}".`,
78 | /* eslint-disable */
79 | description: stripIndents`
80 | Did you forget to return the React element from the story?
81 | Use "() => ()" or "() => { return ; }" when defining the story.
82 | `,
83 | /* eslint-enable */
84 | };
85 | return renderError(error);
86 | }
87 |
88 | if (element.type === undefined) {
89 | const error = {
90 | title: `Expecting a valid React element from the story: "${selectedStory}" of "${selectedKind}".`,
91 | description: stripIndents`
92 | Seems like you are not returning a correct React element form the story.
93 | Could you double check that?
94 | `,
95 | };
96 | return renderError(error);
97 | }
98 |
99 | ReactDOM.render(element, rootEl);
100 | return null;
101 | }
102 |
103 | export default function renderPreview({ reduxStore, storyStore }) {
104 | const state = reduxStore.getState();
105 | if (state.error) {
106 | return renderException(state.error);
107 | }
108 |
109 | try {
110 | return renderMain(state, storyStore);
111 | } catch (ex) {
112 | return renderException(ex);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/dist/client/preview/story_store.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _keys = require("babel-runtime/core-js/object/keys");
8 |
9 | var _keys2 = _interopRequireDefault(_keys);
10 |
11 | var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");
12 |
13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
14 |
15 | var _createClass2 = require("babel-runtime/helpers/createClass");
16 |
17 | var _createClass3 = _interopRequireDefault(_createClass2);
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20 |
21 | var cnt = 0;
22 |
23 | function getId() {
24 | cnt += 1;
25 | return cnt;
26 | }
27 |
28 | var StoryStore = function () {
29 | function StoryStore() {
30 | (0, _classCallCheck3.default)(this, StoryStore);
31 |
32 | this._data = {};
33 | }
34 |
35 | (0, _createClass3.default)(StoryStore, [{
36 | key: "addStory",
37 | value: function addStory(kind, name, fn) {
38 | if (!this._data[kind]) {
39 | this._data[kind] = {
40 | kind: kind,
41 | index: getId(),
42 | stories: {}
43 | };
44 | }
45 |
46 | this._data[kind].stories[name] = {
47 | name: name,
48 | index: getId(),
49 | fn: fn
50 | };
51 | }
52 | }, {
53 | key: "getStoryKinds",
54 | value: function getStoryKinds() {
55 | var _this = this;
56 |
57 | return (0, _keys2.default)(this._data).map(function (key) {
58 | return _this._data[key];
59 | }).filter(function (kind) {
60 | return (0, _keys2.default)(kind.stories).length > 0;
61 | }).sort(function (info1, info2) {
62 | return info1.index - info2.index;
63 | }).map(function (info) {
64 | return info.kind;
65 | });
66 | }
67 | }, {
68 | key: "getStories",
69 | value: function getStories(kind) {
70 | var _this2 = this;
71 |
72 | if (!this._data[kind]) {
73 | return [];
74 | }
75 |
76 | return (0, _keys2.default)(this._data[kind].stories).map(function (name) {
77 | return _this2._data[kind].stories[name];
78 | }).sort(function (info1, info2) {
79 | return info1.index - info2.index;
80 | }).map(function (info) {
81 | return info.name;
82 | });
83 | }
84 | }, {
85 | key: "getStory",
86 | value: function getStory(kind, name) {
87 | var storiesKind = this._data[kind];
88 | if (!storiesKind) {
89 | return null;
90 | }
91 |
92 | var storyInfo = storiesKind.stories[name];
93 | if (!storyInfo) {
94 | return null;
95 | }
96 |
97 | return storyInfo.fn;
98 | }
99 | }, {
100 | key: "removeStoryKind",
101 | value: function removeStoryKind(kind) {
102 | this._data[kind].stories = {};
103 | }
104 | }, {
105 | key: "hasStoryKind",
106 | value: function hasStoryKind(kind) {
107 | return Boolean(this._data[kind]);
108 | }
109 | }, {
110 | key: "hasStory",
111 | value: function hasStory(kind, name) {
112 | return Boolean(this.getStory(kind, name));
113 | }
114 | }, {
115 | key: "dumpStoryBook",
116 | value: function dumpStoryBook() {
117 | var _this3 = this;
118 |
119 | var data = this.getStoryKinds().map(function (kind) {
120 | return { kind: kind, stories: _this3.getStories(kind) };
121 | });
122 |
123 | return data;
124 | }
125 | }, {
126 | key: "size",
127 | value: function size() {
128 | return (0, _keys2.default)(this._data).length;
129 | }
130 | }, {
131 | key: "clean",
132 | value: function clean() {
133 | var _this4 = this;
134 |
135 | this.getStoryKinds().forEach(function (kind) {
136 | return delete _this4._data[kind];
137 | });
138 | }
139 | }]);
140 | return StoryStore;
141 | }();
142 |
143 | exports.default = StoryStore;
--------------------------------------------------------------------------------
/src/server/config.js:
--------------------------------------------------------------------------------
1 | /* eslint global-require: 0 */
2 |
3 | import fs from 'fs';
4 | import path from 'path';
5 | import loadBabelConfig from './babel_config';
6 | import { includePaths } from './config/utils';
7 |
8 | // avoid ESLint errors
9 | const logger = console;
10 |
11 | export function addJsonLoaderIfNotAvailable(config) {
12 | const jsonLoaderExists = config.module.loaders.reduce(
13 | (value, loader) => {
14 | return value || [].concat(loader.test).some((matcher) => {
15 | const isRegex = matcher instanceof RegExp;
16 | const testString = 'my_package.json';
17 | if (isRegex) {
18 | return matcher.test(testString);
19 | }
20 | if (typeof matcher === 'function') {
21 | return matcher(testString);
22 | }
23 | return false;
24 | });
25 | },
26 | false
27 | );
28 |
29 | if (!jsonLoaderExists) {
30 | config.module.loaders.push({
31 | test: /\.json$/,
32 | include: includePaths,
33 | loader: require.resolve('json-loader'),
34 | });
35 | }
36 | }
37 |
38 | // `baseConfig` is a webpack configuration bundled with storybook.
39 | // React Storybook will look in the `configDir` directory
40 | // (inside working directory) if a config path is not provided.
41 | export default function (configType, baseConfig, configDir) {
42 | const config = baseConfig;
43 |
44 | const babelConfig = loadBabelConfig(configDir);
45 | config.module.loaders[0].query = babelConfig;
46 |
47 | // Check whether a config.js file exists inside the storybook
48 | // config directory and throw an error if it's not.
49 | const storybookConfigPath = path.resolve(configDir, 'config.js');
50 | if (!fs.existsSync(storybookConfigPath)) {
51 | const err = new Error(`=> Create a storybook config file in "${configDir}/config.js".`);
52 | throw err;
53 | }
54 | config.entry.preview.push(require.resolve(storybookConfigPath));
55 |
56 | // Check whether addons.js file exists inside the storybook.
57 | // Load the default addons.js file if it's missing.
58 | const storybookDefaultAddonsPath = path.resolve(__dirname, 'addons.js');
59 | const storybookCustomAddonsPath = path.resolve(configDir, 'addons.js');
60 | if (fs.existsSync(storybookCustomAddonsPath)) {
61 | logger.info('=> Loading custom addons config.');
62 | config.entry.manager.unshift(storybookCustomAddonsPath);
63 | } else {
64 | config.entry.manager.unshift(storybookDefaultAddonsPath);
65 | }
66 |
67 | // Check whether user has a custom webpack config file and
68 | // return the (extended) base configuration if it's not available.
69 | let customConfigPath = path.resolve(configDir, 'webpack.config.js');
70 | if (!fs.existsSync(customConfigPath)) {
71 | logger.info('=> Using default webpack setup based on "Create React App".');
72 | customConfigPath = path.resolve(__dirname, './config/defaults/webpack.config.js');
73 | }
74 |
75 | const customConfig = require(customConfigPath);
76 |
77 | if (typeof customConfig === 'function') {
78 | logger.info('=> Loading custom webpack config (full-control mode).');
79 | return customConfig(config, configType);
80 | }
81 |
82 | logger.info('=> Loading custom webpack config.');
83 |
84 | customConfig.module = customConfig.module || {};
85 |
86 | const newConfig = {
87 | ...customConfig,
88 | // We'll always load our configurations after the custom config.
89 | // So, we'll always load the stuff we need.
90 | ...config,
91 | // We need to use our and custom plugins.
92 | plugins: [
93 | ...config.plugins,
94 | ...customConfig.plugins || [],
95 | ],
96 | module: {
97 | ...config.module,
98 | // We need to use our and custom loaders.
99 | ...customConfig.module,
100 | loaders: [
101 | ...config.module.loaders,
102 | ...customConfig.module.loaders || [],
103 | ],
104 | },
105 | resolve: {
106 | ...config.resolve,
107 | ...customConfig.resolve,
108 | alias: {
109 | ...config.alias,
110 | ...(customConfig.resolve && customConfig.resolve.alias),
111 | },
112 | },
113 | };
114 |
115 | addJsonLoaderIfNotAvailable(newConfig);
116 |
117 | return newConfig;
118 | }
119 |
--------------------------------------------------------------------------------
/dist/client/preview/client_api.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _toConsumableArray2 = require("babel-runtime/helpers/toConsumableArray");
8 |
9 | var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
10 |
11 | var _keys = require("babel-runtime/core-js/object/keys");
12 |
13 | var _keys2 = _interopRequireDefault(_keys);
14 |
15 | var _extends2 = require("babel-runtime/helpers/extends");
16 |
17 | var _extends3 = _interopRequireDefault(_extends2);
18 |
19 | var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");
20 |
21 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
22 |
23 | var _createClass2 = require("babel-runtime/helpers/createClass");
24 |
25 | var _createClass3 = _interopRequireDefault(_createClass2);
26 |
27 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28 |
29 | var ClientApi = function () {
30 | function ClientApi(_ref) {
31 | var channel = _ref.channel;
32 | var storyStore = _ref.storyStore;
33 | (0, _classCallCheck3.default)(this, ClientApi);
34 |
35 | // channel can be null when running in node
36 | // always check whether channel is available
37 | this._channel = channel;
38 | this._storyStore = storyStore;
39 | this._addons = {};
40 | this._globalDecorators = [];
41 | }
42 |
43 | (0, _createClass3.default)(ClientApi, [{
44 | key: "setAddon",
45 | value: function setAddon(addon) {
46 | this._addons = (0, _extends3.default)({}, this._addons, addon);
47 | }
48 | }, {
49 | key: "addDecorator",
50 | value: function addDecorator(decorator) {
51 | this._globalDecorators.push(decorator);
52 | }
53 | }, {
54 | key: "clearDecorators",
55 | value: function clearDecorators() {
56 | this._globalDecorators = [];
57 | }
58 | }, {
59 | key: "storiesOf",
60 | value: function storiesOf(kind, m) {
61 | var _this = this;
62 |
63 | if (m && m.hot) {
64 | m.hot.dispose(function () {
65 | _this._storyStore.removeStoryKind(kind);
66 | });
67 | }
68 |
69 | var localDecorators = [];
70 | var api = {
71 | kind: kind
72 | };
73 |
74 | // apply addons
75 | (0, _keys2.default)(this._addons).forEach(function (name) {
76 | var addon = _this._addons[name];
77 | api[name] = function () {
78 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
79 | args[_key] = arguments[_key];
80 | }
81 |
82 | addon.apply(api, args);
83 | return api;
84 | };
85 | });
86 |
87 | api.add = function (storyName, getStory) {
88 | // Wrap the getStory function with each decorator. The first
89 | // decorator will wrap the story function. The second will
90 | // wrap the first decorator and so on.
91 | var decorators = [].concat(localDecorators, (0, _toConsumableArray3.default)(_this._globalDecorators));
92 |
93 | var fn = decorators.reduce(function (decorated, decorator) {
94 | return function (context) {
95 | return decorator(function () {
96 | return decorated(context);
97 | }, context);
98 | };
99 | }, getStory);
100 |
101 | // Add the fully decorated getStory function.
102 | _this._storyStore.addStory(kind, storyName, fn);
103 | return api;
104 | };
105 |
106 | api.addDecorator = function (decorator) {
107 | localDecorators.push(decorator);
108 | return api;
109 | };
110 |
111 | return api;
112 | }
113 | }, {
114 | key: "getStorybook",
115 | value: function getStorybook() {
116 | var _this2 = this;
117 |
118 | return this._storyStore.getStoryKinds().map(function (kind) {
119 | var stories = _this2._storyStore.getStories(kind).map(function (name) {
120 | var render = _this2._storyStore.getStory(kind, name);
121 | return { name: name, render: render };
122 | });
123 | return { kind: kind, stories: stories };
124 | });
125 | }
126 | }]);
127 | return ClientApi;
128 | }();
129 |
130 | exports.default = ClientApi;
--------------------------------------------------------------------------------
/src/server/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import express from 'express';
4 | import favicon from 'serve-favicon';
5 | import program from 'commander';
6 | import path from 'path';
7 | import fs from 'fs';
8 | import chalk from 'chalk';
9 | import shelljs from 'shelljs';
10 | import storybook from './middleware';
11 | import packageJson from '../../package.json';
12 | import { parseList, getEnvConfig } from './utils';
13 | import { track, dontTrack } from './track_usage';
14 |
15 | process.env.NODE_ENV = process.env.NODE_ENV || 'development';
16 |
17 | const logger = console;
18 |
19 | program
20 | .version(packageJson.version)
21 | .option('-p, --port [number]', 'Port to run Storybook (Required)', parseInt)
22 | .option('-h, --host [string]', 'Host to run Storybook')
23 | .option('-s, --static-dir ', 'Directory where to load static files from')
24 | .option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from')
25 | .option('--dont-track', 'Do not send anonymous usage stats.')
26 | .option('-d, --db-path [db-file]', 'DEPRECATED!')
27 | .option('--enable-db', 'DEPRECATED!')
28 | .parse(process.argv);
29 |
30 | logger.info(chalk.bold(`${packageJson.name} v${packageJson.version}\n`));
31 |
32 | if (program.enableDb || program.dbPath) {
33 | logger.error([
34 | 'Error: the experimental local database addon is no longer bundled with',
35 | 'react-storybook. Please remove these flags (-d,--db-path,--enable-db)',
36 | 'from the command or npm script and try again.',
37 | ].join(' '));
38 | process.exit(1);
39 | }
40 |
41 | // The key is the field created in `program` variable for
42 | // each command line argument. Value is the env variable.
43 | getEnvConfig(program, {
44 | port: 'SBCONFIG_PORT',
45 | host: 'SBCONFIG_HOSTNAME',
46 | staticDir: 'SBCONFIG_STATIC_DIR',
47 | configDir: 'SBCONFIG_CONFIG_DIR',
48 | dontTrack: 'SBCONFIG_DO_NOT_TRACK',
49 | });
50 |
51 | if (program.dontTrack) {
52 | dontTrack();
53 | }
54 |
55 | if (!program.port) {
56 | logger.error('Error: port to run Storybook is required!\n');
57 | program.help();
58 | process.exit(-1);
59 | }
60 |
61 | // Used with `app.listen` below
62 | const listenAddr = [program.port];
63 |
64 | if (program.host) {
65 | listenAddr.push(program.host);
66 | }
67 |
68 | const app = express();
69 |
70 | let hasCustomFavicon = false;
71 |
72 | if (program.staticDir) {
73 | program.staticDir = parseList(program.staticDir);
74 | program.staticDir.forEach((dir) => {
75 | const staticPath = path.resolve(dir);
76 | if (!fs.existsSync(staticPath)) {
77 | logger.error(`Error: no such directory to load static files: ${staticPath}`);
78 | process.exit(-1);
79 | }
80 | logger.log(`=> Loading static files from: ${staticPath} .`);
81 | app.use(express.static(staticPath, { index: false }));
82 |
83 | const faviconPath = path.resolve(staticPath, 'favicon.ico');
84 | if (fs.existsSync(faviconPath)) {
85 | hasCustomFavicon = true;
86 | app.use(favicon(faviconPath));
87 | }
88 | });
89 | }
90 |
91 | if (!hasCustomFavicon) {
92 | app.use(favicon(path.resolve(__dirname, 'public/favicon.ico')));
93 | }
94 |
95 | // Build the webpack configuration using the `baseConfig`
96 | // custom `.babelrc` file and `webpack.config.js` files
97 | const configDir = program.configDir || './.storybook';
98 |
99 | // The repository info is sent to the storybook while running on
100 | // development mode so it'll be easier for tools to integrate.
101 | const exec = cmd => shelljs.exec(cmd, { silent: true }).stdout.trim();
102 | process.env.STORYBOOK_GIT_ORIGIN = process.env.STORYBOOK_GIT_ORIGIN || exec('git remote get-url origin');
103 | process.env.STORYBOOK_GIT_BRANCH = process.env.STORYBOOK_GIT_BRANCH || exec('git symbolic-ref HEAD --short');
104 |
105 | // NOTE changes to env should be done before calling `getBaseConfig`
106 | // `getBaseConfig` function which is called inside the middleware
107 | app.use(storybook(configDir));
108 |
109 | app.listen(...listenAddr, function (error) {
110 | if (error) {
111 | throw error;
112 | } else {
113 | const address = `http://${program.host || 'localhost'}:${program.port}/`;
114 | logger.info(`\nReact Storybook started on => ${chalk.cyan(address)}\n`);
115 | track();
116 | }
117 | });
118 |
--------------------------------------------------------------------------------
/dist/server/build.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | 'use strict';
3 |
4 | var _extends2 = require('babel-runtime/helpers/extends');
5 |
6 | var _extends3 = _interopRequireDefault(_extends2);
7 |
8 | var _webpack = require('webpack');
9 |
10 | var _webpack2 = _interopRequireDefault(_webpack);
11 |
12 | var _commander = require('commander');
13 |
14 | var _commander2 = _interopRequireDefault(_commander);
15 |
16 | var _path = require('path');
17 |
18 | var _path2 = _interopRequireDefault(_path);
19 |
20 | var _fs = require('fs');
21 |
22 | var _fs2 = _interopRequireDefault(_fs);
23 |
24 | var _chalk = require('chalk');
25 |
26 | var _chalk2 = _interopRequireDefault(_chalk);
27 |
28 | var _shelljs = require('shelljs');
29 |
30 | var _shelljs2 = _interopRequireDefault(_shelljs);
31 |
32 | var _package = require('../../package.json');
33 |
34 | var _package2 = _interopRequireDefault(_package);
35 |
36 | var _webpackConfig = require('./config/webpack.config.prod');
37 |
38 | var _webpackConfig2 = _interopRequireDefault(_webpackConfig);
39 |
40 | var _config = require('./config');
41 |
42 | var _config2 = _interopRequireDefault(_config);
43 |
44 | var _index = require('./index.html');
45 |
46 | var _index2 = _interopRequireDefault(_index);
47 |
48 | var _iframe = require('./iframe.html');
49 |
50 | var _iframe2 = _interopRequireDefault(_iframe);
51 |
52 | var _utils = require('./utils');
53 |
54 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
55 |
56 | process.env.NODE_ENV = process.env.NODE_ENV || 'production';
57 |
58 | // avoid ESLint errors
59 | var logger = console;
60 |
61 | _commander2.default.version(_package2.default.version).option('-s, --static-dir ', 'Directory where to load static files from', _utils.parseList).option('-o, --output-dir [dir-name]', 'Directory where to store built files').option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from').option('-d, --db-path [db-file]', 'DEPRECATED!').option('--enable-db', 'DEPRECATED!').parse(process.argv);
62 |
63 | logger.info(_chalk2.default.bold(_package2.default.name + ' v' + _package2.default.version + '\n'));
64 |
65 | if (_commander2.default.enableDb || _commander2.default.dbPath) {
66 | logger.error(['Error: the experimental local database addon is no longer bundled with', 'react-storybook. Please remove these flags (-d,--db-path,--enable-db)', 'from the command or npm script and try again.'].join(' '));
67 | process.exit(1);
68 | }
69 |
70 | // The key is the field created in `program` variable for
71 | // each command line argument. Value is the env variable.
72 | (0, _utils.getEnvConfig)(_commander2.default, {
73 | staticDir: 'SBCONFIG_STATIC_DIR',
74 | outputDir: 'SBCONFIG_OUTPUT_DIR',
75 | configDir: 'SBCONFIG_CONFIG_DIR'
76 | });
77 |
78 | var configDir = _commander2.default.configDir || './.storybook';
79 | var outputDir = _commander2.default.outputDir || './storybook-static';
80 |
81 | // create output directory (and the static dir) if not exists
82 | _shelljs2.default.rm('-rf', outputDir);
83 | _shelljs2.default.mkdir('-p', _path2.default.resolve(outputDir));
84 | _shelljs2.default.cp(_path2.default.resolve(__dirname, 'public/favicon.ico'), outputDir);
85 |
86 | // Build the webpack configuration using the `baseConfig`
87 | // custom `.babelrc` file and `webpack.config.js` files
88 | // NOTE changes to env should be done before calling `getBaseConfig`
89 | var config = (0, _config2.default)('PRODUCTION', (0, _webpackConfig2.default)(), configDir);
90 | config.output.path = outputDir;
91 |
92 | // copy all static files
93 | if (_commander2.default.staticDir) {
94 | _commander2.default.staticDir.forEach(function (dir) {
95 | if (!_fs2.default.existsSync(dir)) {
96 | logger.error('Error: no such directory to load static files: ' + dir);
97 | process.exit(-1);
98 | }
99 | logger.log('=> Copying static files from: ' + dir);
100 | _shelljs2.default.cp('-r', dir + '/*', outputDir);
101 | });
102 | }
103 |
104 | // compile all resources with webpack and write them to the disk.
105 | logger.log('Building storybook ...');
106 | (0, _webpack2.default)(config).run(function (err, stats) {
107 | if (err) {
108 | logger.error('Failed to build the storybook');
109 | logger.error(err.message);
110 | process.exit(1);
111 | }
112 |
113 | var data = {
114 | publicPath: config.output.publicPath,
115 | assets: stats.toJson().assetsByChunkName
116 | };
117 | var headHtml = (0, _utils.getHeadHtml)(configDir);
118 |
119 | // Write both the storybook UI and IFRAME HTML files to destination path.
120 | _fs2.default.writeFileSync(_path2.default.resolve(outputDir, 'index.html'), (0, _index2.default)(data));
121 | _fs2.default.writeFileSync(_path2.default.resolve(outputDir, 'iframe.html'), (0, _iframe2.default)((0, _extends3.default)({}, data, { headHtml: headHtml })));
122 | });
--------------------------------------------------------------------------------
/dist/server/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
8 |
9 | var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
10 |
11 | var _extends2 = require('babel-runtime/helpers/extends');
12 |
13 | var _extends3 = _interopRequireDefault(_extends2);
14 |
15 | exports.addJsonLoaderIfNotAvailable = addJsonLoaderIfNotAvailable;
16 |
17 | exports.default = function (configType, baseConfig, configDir) {
18 | var config = baseConfig;
19 |
20 | var babelConfig = (0, _babel_config2.default)(configDir);
21 | config.module.loaders[0].query = babelConfig;
22 |
23 | // Check whether a config.js file exists inside the storybook
24 | // config directory and throw an error if it's not.
25 | var storybookConfigPath = _path2.default.resolve(configDir, 'config.js');
26 | if (!_fs2.default.existsSync(storybookConfigPath)) {
27 | var err = new Error('=> Create a storybook config file in "' + configDir + '/config.js".');
28 | throw err;
29 | }
30 | config.entry.preview.push(require.resolve(storybookConfigPath));
31 |
32 | // Check whether addons.js file exists inside the storybook.
33 | // Load the default addons.js file if it's missing.
34 | var storybookDefaultAddonsPath = _path2.default.resolve(__dirname, 'addons.js');
35 | var storybookCustomAddonsPath = _path2.default.resolve(configDir, 'addons.js');
36 | if (_fs2.default.existsSync(storybookCustomAddonsPath)) {
37 | logger.info('=> Loading custom addons config.');
38 | config.entry.manager.unshift(storybookCustomAddonsPath);
39 | } else {
40 | config.entry.manager.unshift(storybookDefaultAddonsPath);
41 | }
42 |
43 | // Check whether user has a custom webpack config file and
44 | // return the (extended) base configuration if it's not available.
45 | var customConfigPath = _path2.default.resolve(configDir, 'webpack.config.js');
46 | if (!_fs2.default.existsSync(customConfigPath)) {
47 | logger.info('=> Using default webpack setup based on "Create React App".');
48 | customConfigPath = _path2.default.resolve(__dirname, './config/defaults/webpack.config.js');
49 | }
50 |
51 | var customConfig = require(customConfigPath);
52 |
53 | if (typeof customConfig === 'function') {
54 | logger.info('=> Loading custom webpack config (full-control mode).');
55 | return customConfig(config, configType);
56 | }
57 |
58 | logger.info('=> Loading custom webpack config.');
59 |
60 | customConfig.module = customConfig.module || {};
61 |
62 | var newConfig = (0, _extends3.default)({}, customConfig, config, {
63 | // We need to use our and custom plugins.
64 | plugins: [].concat((0, _toConsumableArray3.default)(config.plugins), (0, _toConsumableArray3.default)(customConfig.plugins || [])),
65 | module: (0, _extends3.default)({}, config.module, customConfig.module, {
66 | loaders: [].concat((0, _toConsumableArray3.default)(config.module.loaders), (0, _toConsumableArray3.default)(customConfig.module.loaders || []))
67 | }),
68 | resolve: (0, _extends3.default)({}, config.resolve, customConfig.resolve, {
69 | alias: (0, _extends3.default)({}, config.alias, customConfig.resolve && customConfig.resolve.alias)
70 | })
71 | });
72 |
73 | addJsonLoaderIfNotAvailable(newConfig);
74 |
75 | return newConfig;
76 | };
77 |
78 | var _fs = require('fs');
79 |
80 | var _fs2 = _interopRequireDefault(_fs);
81 |
82 | var _path = require('path');
83 |
84 | var _path2 = _interopRequireDefault(_path);
85 |
86 | var _babel_config = require('./babel_config');
87 |
88 | var _babel_config2 = _interopRequireDefault(_babel_config);
89 |
90 | var _utils = require('./config/utils');
91 |
92 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
93 |
94 | // avoid ESLint errors
95 | /* eslint global-require: 0 */
96 |
97 | var logger = console;
98 |
99 | function addJsonLoaderIfNotAvailable(config) {
100 | var jsonLoaderExists = config.module.loaders.reduce(function (value, loader) {
101 | return value || [].concat(loader.test).some(function (matcher) {
102 | var isRegex = matcher instanceof RegExp;
103 | var testString = 'my_package.json';
104 | if (isRegex) {
105 | return matcher.test(testString);
106 | }
107 | if (typeof matcher === 'function') {
108 | return matcher(testString);
109 | }
110 | return false;
111 | });
112 | }, false);
113 |
114 | if (!jsonLoaderExists) {
115 | config.module.loaders.push({
116 | test: /\.json$/,
117 | include: _utils.includePaths,
118 | loader: require.resolve('json-loader')
119 | });
120 | }
121 | }
122 |
123 | // `baseConfig` is a webpack configuration bundled with storybook.
124 | // React Storybook will look in the `configDir` directory
125 | // (inside working directory) if a config path is not provided.
--------------------------------------------------------------------------------
/dist/server/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | 'use strict';
3 |
4 | var _express = require('express');
5 |
6 | var _express2 = _interopRequireDefault(_express);
7 |
8 | var _serveFavicon = require('serve-favicon');
9 |
10 | var _serveFavicon2 = _interopRequireDefault(_serveFavicon);
11 |
12 | var _commander = require('commander');
13 |
14 | var _commander2 = _interopRequireDefault(_commander);
15 |
16 | var _path = require('path');
17 |
18 | var _path2 = _interopRequireDefault(_path);
19 |
20 | var _fs = require('fs');
21 |
22 | var _fs2 = _interopRequireDefault(_fs);
23 |
24 | var _chalk = require('chalk');
25 |
26 | var _chalk2 = _interopRequireDefault(_chalk);
27 |
28 | var _shelljs = require('shelljs');
29 |
30 | var _shelljs2 = _interopRequireDefault(_shelljs);
31 |
32 | var _middleware = require('./middleware');
33 |
34 | var _middleware2 = _interopRequireDefault(_middleware);
35 |
36 | var _package = require('../../package.json');
37 |
38 | var _package2 = _interopRequireDefault(_package);
39 |
40 | var _utils = require('./utils');
41 |
42 | var _track_usage = require('./track_usage');
43 |
44 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
45 |
46 | process.env.NODE_ENV = process.env.NODE_ENV || 'development';
47 |
48 | var logger = console;
49 |
50 | _commander2.default.version(_package2.default.version).option('-p, --port [number]', 'Port to run Storybook (Required)', parseInt).option('-h, --host [string]', 'Host to run Storybook').option('-s, --static-dir ', 'Directory where to load static files from').option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from').option('--dont-track', 'Do not send anonymous usage stats.').option('-d, --db-path [db-file]', 'DEPRECATED!').option('--enable-db', 'DEPRECATED!').parse(process.argv);
51 |
52 | logger.info(_chalk2.default.bold(_package2.default.name + ' v' + _package2.default.version + '\n'));
53 |
54 | if (_commander2.default.enableDb || _commander2.default.dbPath) {
55 | logger.error(['Error: the experimental local database addon is no longer bundled with', 'react-storybook. Please remove these flags (-d,--db-path,--enable-db)', 'from the command or npm script and try again.'].join(' '));
56 | process.exit(1);
57 | }
58 |
59 | // The key is the field created in `program` variable for
60 | // each command line argument. Value is the env variable.
61 | (0, _utils.getEnvConfig)(_commander2.default, {
62 | port: 'SBCONFIG_PORT',
63 | host: 'SBCONFIG_HOSTNAME',
64 | staticDir: 'SBCONFIG_STATIC_DIR',
65 | configDir: 'SBCONFIG_CONFIG_DIR',
66 | dontTrack: 'SBCONFIG_DO_NOT_TRACK'
67 | });
68 |
69 | if (_commander2.default.dontTrack) {
70 | (0, _track_usage.dontTrack)();
71 | }
72 |
73 | if (!_commander2.default.port) {
74 | logger.error('Error: port to run Storybook is required!\n');
75 | _commander2.default.help();
76 | process.exit(-1);
77 | }
78 |
79 | // Used with `app.listen` below
80 | var listenAddr = [_commander2.default.port];
81 |
82 | if (_commander2.default.host) {
83 | listenAddr.push(_commander2.default.host);
84 | }
85 |
86 | var app = (0, _express2.default)();
87 |
88 | var hasCustomFavicon = false;
89 |
90 | if (_commander2.default.staticDir) {
91 | _commander2.default.staticDir = (0, _utils.parseList)(_commander2.default.staticDir);
92 | _commander2.default.staticDir.forEach(function (dir) {
93 | var staticPath = _path2.default.resolve(dir);
94 | if (!_fs2.default.existsSync(staticPath)) {
95 | logger.error('Error: no such directory to load static files: ' + staticPath);
96 | process.exit(-1);
97 | }
98 | logger.log('=> Loading static files from: ' + staticPath + ' .');
99 | app.use(_express2.default.static(staticPath, { index: false }));
100 |
101 | var faviconPath = _path2.default.resolve(staticPath, 'favicon.ico');
102 | if (_fs2.default.existsSync(faviconPath)) {
103 | hasCustomFavicon = true;
104 | app.use((0, _serveFavicon2.default)(faviconPath));
105 | }
106 | });
107 | }
108 |
109 | if (!hasCustomFavicon) {
110 | app.use((0, _serveFavicon2.default)(_path2.default.resolve(__dirname, 'public/favicon.ico')));
111 | }
112 |
113 | // Build the webpack configuration using the `baseConfig`
114 | // custom `.babelrc` file and `webpack.config.js` files
115 | var configDir = _commander2.default.configDir || './.storybook';
116 |
117 | // The repository info is sent to the storybook while running on
118 | // development mode so it'll be easier for tools to integrate.
119 | var exec = function exec(cmd) {
120 | return _shelljs2.default.exec(cmd, { silent: true }).stdout.trim();
121 | };
122 | process.env.STORYBOOK_GIT_ORIGIN = process.env.STORYBOOK_GIT_ORIGIN || exec('git remote get-url origin');
123 | process.env.STORYBOOK_GIT_BRANCH = process.env.STORYBOOK_GIT_BRANCH || exec('git symbolic-ref HEAD --short');
124 |
125 | // NOTE changes to env should be done before calling `getBaseConfig`
126 | // `getBaseConfig` function which is called inside the middleware
127 | app.use((0, _middleware2.default)(configDir));
128 |
129 | app.listen.apply(app, listenAddr.concat([function (error) {
130 | if (error) {
131 | throw error;
132 | } else {
133 | var address = 'http://' + (_commander2.default.host || 'localhost') + ':' + _commander2.default.port + '/';
134 | logger.info('\nReact Storybook started on => ' + _chalk2.default.cyan(address) + '\n');
135 | (0, _track_usage.track)();
136 | }
137 | }]));
--------------------------------------------------------------------------------
/dist/client/preview/render.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _taggedTemplateLiteral2 = require('babel-runtime/helpers/taggedTemplateLiteral');
8 |
9 | var _taggedTemplateLiteral3 = _interopRequireDefault(_taggedTemplateLiteral2);
10 |
11 | var _templateObject = (0, _taggedTemplateLiteral3.default)(['\n Did you forget to return the React element from the story?\n Use "() => ()" or "() => { return ; }" when defining the story.\n '], ['\n Did you forget to return the React element from the story?\n Use "() => ()" or "() => { return ; }" when defining the story.\n ']),
12 | _templateObject2 = (0, _taggedTemplateLiteral3.default)(['\n Seems like you are not returning a correct React element form the story.\n Could you double check that?\n '], ['\n Seems like you are not returning a correct React element form the story.\n Could you double check that?\n ']); /* global document */
13 |
14 | exports.renderError = renderError;
15 | exports.renderException = renderException;
16 | exports.renderMain = renderMain;
17 | exports.default = renderPreview;
18 |
19 | var _react = require('react');
20 |
21 | var _react2 = _interopRequireDefault(_react);
22 |
23 | var _reactDom = require('react-dom');
24 |
25 | var _reactDom2 = _interopRequireDefault(_reactDom);
26 |
27 | var _commonTags = require('common-tags');
28 |
29 | var _error_display = require('./error_display');
30 |
31 | var _error_display2 = _interopRequireDefault(_error_display);
32 |
33 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
34 |
35 | // check whether we're running on node/browser
36 | var isBrowser = typeof window !== 'undefined';
37 |
38 | var logger = console;
39 |
40 | var rootEl = null;
41 | var previousKind = '';
42 | var previousStory = '';
43 |
44 | if (isBrowser) {
45 | rootEl = document.getElementById('root');
46 | }
47 |
48 | function renderError(error) {
49 | var properError = new Error(error.title);
50 | properError.stack = error.description;
51 |
52 | var redBox = _react2.default.createElement(_error_display2.default, { error: properError });
53 | _reactDom2.default.render(redBox, rootEl);
54 | }
55 |
56 | function renderException(error) {
57 | // We always need to render redbox in the mainPage if we get an error.
58 | // Since this is an error, this affects to the main page as well.
59 | var realError = new Error(error.message);
60 | realError.stack = error.stack;
61 | var redBox = _react2.default.createElement(_error_display2.default, { error: realError });
62 | _reactDom2.default.render(redBox, rootEl);
63 |
64 | // Log the stack to the console. So, user could check the source code.
65 | logger.error(error.stack);
66 | }
67 |
68 | function renderMain(data, storyStore) {
69 | if (storyStore.size() === 0) return null;
70 |
71 | var NoPreview = function NoPreview() {
72 | return _react2.default.createElement(
73 | 'p',
74 | null,
75 | 'No Preview Available!'
76 | );
77 | };
78 | var noPreview = _react2.default.createElement(NoPreview, null);
79 | var selectedKind = data.selectedKind;
80 | var selectedStory = data.selectedStory;
81 |
82 |
83 | var story = storyStore.getStory(selectedKind, selectedStory);
84 | if (!story) {
85 | _reactDom2.default.render(noPreview, rootEl);
86 | return null;
87 | }
88 |
89 | // Unmount the previous story only if selectedKind or selectedStory has changed.
90 | // renderMain() gets executed after each action. Actions will cause the whole
91 | // story to re-render without this check.
92 | // https://github.com/kadirahq/react-storybook/issues/116
93 | if (selectedKind !== previousKind || previousStory !== selectedStory) {
94 | // We need to unmount the existing set of components in the DOM node.
95 | // Otherwise, React may not recrease instances for every story run.
96 | // This could leads to issues like below:
97 | // https://github.com/kadirahq/react-storybook/issues/81
98 | previousKind = selectedKind;
99 | previousStory = selectedStory;
100 | _reactDom2.default.unmountComponentAtNode(rootEl);
101 | }
102 |
103 | var context = {
104 | kind: selectedKind,
105 | story: selectedStory
106 | };
107 |
108 | var element = story(context);
109 |
110 | if (!element) {
111 | var error = {
112 | title: 'Expecting a React element from the story: "' + selectedStory + '" of "' + selectedKind + '".',
113 | /* eslint-disable */
114 | description: (0, _commonTags.stripIndents)(_templateObject)
115 | };
116 | return renderError(error);
117 | }
118 |
119 | if (element.type === undefined) {
120 | var _error = {
121 | title: 'Expecting a valid React element from the story: "' + selectedStory + '" of "' + selectedKind + '".',
122 | description: (0, _commonTags.stripIndents)(_templateObject2)
123 | };
124 | return renderError(_error);
125 | }
126 |
127 | _reactDom2.default.render(element, rootEl);
128 | return null;
129 | }
130 |
131 | function renderPreview(_ref) {
132 | var reduxStore = _ref.reduxStore;
133 | var storyStore = _ref.storyStore;
134 |
135 | var state = reduxStore.getState();
136 | if (state.error) {
137 | return renderException(state.error);
138 | }
139 |
140 | try {
141 | return renderMain(state, storyStore);
142 | } catch (ex) {
143 | return renderException(ex);
144 | }
145 | }
--------------------------------------------------------------------------------
/src/client/preview/__tests__/client_api.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import ClientAPI from '../client_api';
3 |
4 | const { describe, it } = global;
5 |
6 | class StoryStore {
7 | constructor() {
8 | this.stories = [];
9 | }
10 |
11 | addStory(kind, story, fn) {
12 | this.stories.push({ kind, story, fn });
13 | }
14 |
15 | getStoryKinds() {
16 | return this.stories.reduce((kinds, info) => {
17 | if (kinds.indexOf(info.kind) === -1) {
18 | kinds.push(info.kind);
19 | }
20 | return kinds;
21 | }, []);
22 | }
23 |
24 | getStories(kind) {
25 | return this.stories.reduce((stories, info) => {
26 | if (info.kind === kind) {
27 | stories.push(info.story);
28 | }
29 | return stories;
30 | }, []);
31 | }
32 |
33 | getStory(kind, name) {
34 | return this.stories.reduce((fn, info) => {
35 | if (!fn && info.kind === kind && info.story === name) {
36 | return info.fn;
37 | }
38 | return fn;
39 | }, null);
40 | }
41 | }
42 |
43 | describe('preview.client_api', () => {
44 | describe('setAddon', () => {
45 | it('should register addons', () => {
46 | const api = new ClientAPI({});
47 | let data;
48 |
49 | api.setAddon({
50 | aa() {
51 | data = 'foo';
52 | },
53 | });
54 |
55 | api.storiesOf('none').aa();
56 | expect(data).to.be.equal('foo');
57 | });
58 |
59 | it('should not remove previous addons', () => {
60 | const api = new ClientAPI({});
61 | const data = [];
62 |
63 | api.setAddon({
64 | aa() {
65 | data.push('foo');
66 | },
67 | });
68 |
69 | api.setAddon({
70 | bb() {
71 | data.push('bar');
72 | },
73 | });
74 |
75 | api.storiesOf('none').aa().bb();
76 | expect(data).to.deep.equal(['foo', 'bar']);
77 | });
78 |
79 | it('should call with the api context', () => {
80 | const api = new ClientAPI({});
81 | let data;
82 |
83 | api.setAddon({
84 | aa() {
85 | data = typeof this.add;
86 | },
87 | });
88 |
89 | api.storiesOf('none').aa();
90 | expect(data).to.be.equal('function');
91 | });
92 |
93 | it('should be able to access addons added previously', () => {
94 | const api = new ClientAPI({});
95 | let data;
96 |
97 | api.setAddon({
98 | aa() {
99 | data = 'foo';
100 | },
101 | });
102 |
103 | api.setAddon({
104 | bb() {
105 | this.aa();
106 | },
107 | });
108 |
109 | api.storiesOf('none').bb();
110 | expect(data).to.be.equal('foo');
111 | });
112 |
113 | it('should be able to access the current kind', () => {
114 | const api = new ClientAPI({});
115 | const kind = 'dfdwf3e3';
116 | let data;
117 |
118 | api.setAddon({
119 | aa() {
120 | data = this.kind;
121 | },
122 | });
123 |
124 | api.storiesOf(kind).aa();
125 | expect(data).to.be.equal(kind);
126 | });
127 | });
128 |
129 | describe('addDecorator', () => {
130 | it('should add local decorators', () => {
131 | const storyStore = new StoryStore();
132 | const api = new ClientAPI({ storyStore });
133 | const localApi = api.storiesOf('none');
134 | localApi.addDecorator(function (fn) {
135 | return `aa-${fn()}`;
136 | });
137 |
138 | localApi.add('storyName', () => ('Hello'));
139 | expect(storyStore.stories[0].fn()).to.be.equal('aa-Hello');
140 | });
141 |
142 | it('should add global decorators', () => {
143 | const storyStore = new StoryStore();
144 | const api = new ClientAPI({ storyStore });
145 | api.addDecorator(function (fn) {
146 | return `bb-${fn()}`;
147 | });
148 | const localApi = api.storiesOf('none');
149 |
150 | localApi.add('storyName', () => ('Hello'));
151 | expect(storyStore.stories[0].fn()).to.be.equal('bb-Hello');
152 | });
153 |
154 | it('should utilize both decorators at once', () => {
155 | const storyStore = new StoryStore();
156 | const api = new ClientAPI({ storyStore });
157 | const localApi = api.storiesOf('none');
158 |
159 | api.addDecorator(function (fn) {
160 | return `aa-${fn()}`;
161 | });
162 | localApi.addDecorator(function (fn) {
163 | return `bb-${fn()}`;
164 | });
165 |
166 | localApi.add('storyName', () => ('Hello'));
167 | expect(storyStore.stories[0].fn()).to.be.equal('aa-bb-Hello');
168 | });
169 |
170 | it('should pass the context', () => {
171 | const storyStore = new StoryStore();
172 | const api = new ClientAPI({ storyStore });
173 | const localApi = api.storiesOf('none');
174 | localApi.addDecorator(function (fn) {
175 | return `aa-${fn()}`;
176 | });
177 |
178 | localApi.add('storyName', ({ kind, story }) => (`${kind}-${story}`));
179 |
180 | const kind = 'dfdfd';
181 | const story = 'ef349ff';
182 |
183 | const result = storyStore.stories[0].fn({ kind, story });
184 | expect(result).to.be.equal(`aa-${kind}-${story}`);
185 | });
186 |
187 | it('should have access to the context', () => {
188 | const storyStore = new StoryStore();
189 | const api = new ClientAPI({ storyStore });
190 | const localApi = api.storiesOf('none');
191 | localApi.addDecorator(function (fn, { kind, story }) {
192 | return `${kind}-${story}-${fn()}`;
193 | });
194 |
195 | localApi.add('storyName', () => ('Hello'));
196 |
197 | const kind = 'dfdfd';
198 | const story = 'ef349ff';
199 |
200 | const result = storyStore.stories[0].fn({ kind, story });
201 | expect(result).to.be.equal(`${kind}-${story}-Hello`);
202 | });
203 | });
204 |
205 | describe('clearDecorators', () => {
206 | it('should remove all global decorators', () => {
207 | const api = new ClientAPI({});
208 | api._globalDecorators = 1234;
209 | api.clearDecorators();
210 | expect(api._globalDecorators).to.deep.equal([]);
211 | });
212 | });
213 |
214 | describe('getStorybook', () => {
215 | it('should return storybook when empty', () => {
216 | const storyStore = new StoryStore();
217 | const api = new ClientAPI({ storyStore });
218 | const book = api.getStorybook();
219 | expect(book).to.deep.equal([]);
220 | });
221 |
222 | it('should return storybook with stories', () => {
223 | const storyStore = new StoryStore();
224 | const api = new ClientAPI({ storyStore });
225 | const functions = {
226 | 'story-1.1': () => 'story-1.1',
227 | 'story-1.2': () => 'story-1.2',
228 | 'story-2.1': () => 'story-2.1',
229 | 'story-2.2': () => 'story-2.2',
230 | };
231 | const kind1 = api.storiesOf('kind-1');
232 | kind1.add('story-1.1', functions['story-1.1']);
233 | kind1.add('story-1.2', functions['story-1.2']);
234 | const kind2 = api.storiesOf('kind-2');
235 | kind2.add('story-2.1', functions['story-2.1']);
236 | kind2.add('story-2.2', functions['story-2.2']);
237 | const book = api.getStorybook();
238 | expect(book).to.deep.equal([
239 | {
240 | kind: 'kind-1',
241 | stories: [
242 | { name: 'story-1.1', render: functions['story-1.1'] },
243 | { name: 'story-1.2', render: functions['story-1.2'] },
244 | ],
245 | },
246 | {
247 | kind: 'kind-2',
248 | stories: [
249 | { name: 'story-2.1', render: functions['story-2.1'] },
250 | { name: 'story-2.2', render: functions['story-2.2'] },
251 | ],
252 | },
253 | ]);
254 | });
255 | });
256 | });
257 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## Changelog
2 |
3 | ### v2.31.0
4 | 20-November-2016
5 |
6 | Add the react-storybook version to the build output. [PR621](https://github.com/storybooks/react-storybook/pull/621)
7 |
8 | ### v2.30.1
9 | 17-November-2016
10 |
11 | Update the postmsg channel module to fix issue [#555](https://github.com/storybooks/react-storybook/issues/555) with [PR611](https://github.com/storybooks/react-storybook/pull/611)
12 |
13 | ### v2.30.0
14 | 16-November-2016
15 |
16 | Update to the new Storybook UI which doesn't use Redux.
17 |
18 | ### v2.29.7
19 | 11-November-2016
20 |
21 | Update @kadira/storybook-ui to the latest.
22 |
23 | ### v2.29.6
24 | 10-November-2016
25 |
26 | Fix a typo in the story syntax error messages. [PR610](https://github.com/storybooks/react-storybook/pull/610)
27 |
28 | ### v2.29.5
29 | 09-November-2016
30 |
31 | Check if regex and regex.test is available before calling it. [PR608](https://github.com/storybooks/react-storybook/pull/608)
32 |
33 | ### v2.29.3
34 | 08-November-2016
35 |
36 | Update webpack-hot-middleware to version 2.13.2 to fix the issue [#543](https://github.com/storybooks/react-storybook/issues/543).
37 |
38 | ### v2.29.3
39 | 03-November-2016
40 |
41 | Fix a regression caused by v2.29.2.
42 | There was a text called undefined listed always on the top of the preview.
43 |
44 | ### v2.29.2
45 | 03-November-2016
46 |
47 | Add various fixes.
48 |
49 | * Use webpack chunkhash to enable long-term caching. [PR597](https://github.com/kadirahq/react-storybook/pull/597)
50 | * Fixed json loader testing for when test is multiple. [PR598](https://github.com/kadirahq/react-storybook/pull/598)
51 | * Fix usage of custom favicon [PR592](https://github.com/kadirahq/react-storybook/pull/592)
52 | * Update postcss-loader to v1.1.0 [PR599](https://github.com/kadirahq/react-storybook/pull/599)
53 | * fix for `module.hot` is not available in a static build [PR600](https://github.com/kadirahq/react-storybook/pull/600)
54 |
55 | ### v2.29.1
56 | 03-November-2016
57 |
58 | Update babel-plugin-react-docgen to v1.4.1 to fix HOC [issue](https://github.com/kadirahq/babel-plugin-react-docgen/issues/19)
59 |
60 | ### v2.29.0
61 | 01-November-2016
62 |
63 | Update babel-plugin-react-docgen to 1.4.0.
64 | This will fix some of the compilation issues such as #580.
65 |
66 | ### v2.28.1
67 | 28-October-2016
68 |
69 | Remove preview decorator support. [PR583](https://github.com/kadirahq/react-storybook/pull/583).
70 |
71 | ### v2.28.0
72 | 28-October-2016
73 |
74 | Add preview decorator support. [PR582](https://github.com/kadirahq/react-storybook/pull/582).
75 | This will help us bring storybook designer with some great power.
76 |
77 | ### v2.27.0
78 | 27-October-2016
79 |
80 | Add a few usability improvements to Storybook.
81 |
82 | * Display storybook version. [PR559](https://github.com/kadirahq/react-storybook/pull/559)
83 | * Make the storybooks cacheable. [PR578](https://github.com/kadirahq/react-storybook/pull/578)
84 | * Change the devtool to eval and remove the use of source maps. [PR577](https://github.com/kadirahq/react-storybook/pull/577)
85 | * Update `babel-preset-react-app` to the latest. [PR576](https://github.com/kadirahq/react-storybook/pull/576)
86 | * Ship `json-loader` by default. [PR575](https://github.com/kadirahq/react-storybook/pull/575)
87 |
88 | ### v2.26.0
89 | 24-October-2016
90 |
91 | Get some new features from CRA.
92 |
93 | * Add jsx as a resolve extension [PR563](https://github.com/kadirahq/react-storybook/pull/563)
94 | * Allow to use postcss for CSS @imports [PR564](https://github.com/kadirahq/react-storybook/pull/564)
95 | * Use process.env as a proper object [PR565](https://github.com/kadirahq/react-storybook/pull/565)
96 |
97 | ### v2.25.1
98 | 23-October-2016
99 |
100 | Add a potential fix to [558](https://github.com/kadirahq/react-storybook/issues/558) by updating babel-plugin-react-docgen to the latest(v1.3.2).
101 |
102 | ### v2.25.0
103 | 21-October-2016
104 |
105 | Add react docgen info into React classes with the react-docgen babel plugin. [PR557](https://github.com/kadirahq/react-storybook/pull/557).
106 | With this:
107 |
108 | * We could get docgen info with any React component class using `ClassName.__docgenInfo`.
109 | * From the global collection: `STORYBOOK_REACT_CLASSES`
110 |
111 | Additionally, added `yarn.lock`.
112 |
113 | ### v2.24.1
114 | 19-October-2016
115 |
116 | Do not show git command output. [PR554](https://github.com/kadirahq/react-storybook/pull/554)
117 |
118 | ### v2.24.0
119 | 07-October-2016
120 |
121 | * Export git repository info to support custom tool integrations [PR536](https://github.com/kadirahq/react-storybook/pull/536)
122 |
123 | ### v2.23.0
124 | 06-October-2016
125 |
126 | * Remove the experimental database addon from react-storybook [PR535](https://github.com/kadirahq/react-storybook/pull/535)
127 |
128 | ### v2.22.0
129 | 05-October-2016
130 |
131 | Add some nice development experiment based on suggestion from Dan Abramov.
132 |
133 | * Set a color to the Storybook URL in the console. [PR533](https://github.com/kadirahq/react-storybook/pull/533)
134 | * Add better error message when there's no React element in the story. [PR534](https://github.com/kadirahq/react-storybook/pull/534)
135 |
136 | ### v2.21.0
137 | 05-October-2016
138 |
139 | * Get the latest features from CRA including NODE_PATH support, public folder support and some other minor changes. [#468](https://github.com/kadirahq/react-storybook/issues/468)
140 | * Also bumped `@kadira/storybook-channel-postmsg` to `^1.0.3`
141 |
142 | ### v2.20.1
143 | 28-September-2016
144 |
145 | * Fix story kind order bug [PR499](https://github.com/kadirahq/react-storybook/pull/499)
146 | * Prefix config environment variables [PR503](https://github.com/kadirahq/react-storybook/pull/503)
147 |
148 | ### v2.20.0
149 | 26-September-2016
150 |
151 | * Use postMessage channel [PR498](https://github.com/kadirahq/react-storybook/pull/498)
152 | * Support dynamic panel titles [PR497](https://github.com/kadirahq/react-storybook/pull/497)
153 |
154 | ### v2.19.0
155 | 26-September-2016
156 |
157 | * Support layout options [PR494](https://github.com/kadirahq/react-storybook/pull/494)
158 | * Update Typescript definitions [PR491](https://github.com/kadirahq/react-storybook/pull/491) and [PR493](https://github.com/kadirahq/react-storybook/pull/493)
159 |
160 | ### v2.18.1
161 | 23-September-2016
162 |
163 | * Stop uglifyjs from mangling names [PR483](https://github.com/kadirahq/react-storybook/pull/483)
164 |
165 | ### v2.18.0
166 | 23-September-2016
167 |
168 | * Remove `STORYBOOK_` prefix from config env [PR481](https://github.com/kadirahq/react-storybook/pull/481)
169 |
170 | ### v2.17.0
171 | 22-September-2016
172 |
173 | * Add support for StoryShots. [PR479](https://github.com/kadirahq/react-storybook/pull/479)
174 | * Fix some typos: [PR477](https://github.com/kadirahq/react-storybook/pull/477) & [PR478](https://github.com/kadirahq/react-storybook/pull/478)
175 |
176 | ### v2.16.1
177 | 21-September-2016
178 |
179 | * Fix the 404 error for `addon-db.json` file [PR472](https://github.com/kadirahq/react-storybook/pull/472)
180 | * Serve/Bundle the storybook favicon [PR473](https://github.com/kadirahq/react-storybook/pull/473)
181 |
182 | ### v2.16.0
183 | 21-September-2016
184 |
185 | * Move the babel config loading logic into a seperate file. [PR469](https://github.com/kadirahq/react-storybook/pull/469)
186 | * Update airbnd eslint rules to the latest.
187 |
188 | ### v2.15.1
189 | 19-September-2016
190 |
191 | Add a fix to webpack custom resolve.alias not working. [PR465](https://github.com/kadirahq/react-storybook/pull/465)
192 |
193 | ### v2.15.0
194 | 19-September-2016
195 |
196 | * Use @kadira/storybook-addons as a resolve.alias. So, we can support addons for NPM2 too. [PR462](https://github.com/kadirahq/react-storybook/pull/462)
197 |
198 | ### v2.14.0
199 | 14-September-2016
200 |
201 | * Watch missing NPM modules and force webpack rebuild. [PR446](https://github.com/kadirahq/react-storybook/pull/446)
202 | * Fix issue on error message hanging after even it solved. [PR447](https://github.com/kadirahq/react-storybook/pull/447)
203 | * Allow to reload if HMR goes crazy. [PR448](https://github.com/kadirahq/react-storybook/pull/448)
204 | * Add support to get custom env variables. [PR450](https://github.com/kadirahq/react-storybook/pull/450)
205 |
206 | ### v2.13.1
207 | 14-September-2016
208 |
209 | * Fix 404 error when db file does not exist [PR449](https://github.com/kadirahq/react-storybook/pull/449)
210 |
211 | ### v2.13.0
212 | 9-September-2016
213 |
214 | * Fix [#443](https://github.com/kadirahq/react-storybook/issues/443) where the static version of Storybook doesn't like Safari.
215 | * Update postcss-loader to 0.13.0.
216 |
217 | ### v2.12.1
218 | 8-September-2016
219 |
220 | * Parse static directory provided by env as a list. [PR436](https://github.com/kadirahq/react-storybook/pull/436)
221 |
222 | ### v2.12.0
223 | 8-September-2016
224 |
225 | * Do not include addon register file on preview. [PR426](https://github.com/kadirahq/react-storybook/pull/426)
226 | * Update css-loader to version 0.25.0. [PR427](https://github.com/kadirahq/react-storybook/pull/427)
227 | * Get the head.html values for every page request. [PR432](https://github.com/kadirahq/react-storybook/pull/432)
228 |
229 | ### v2.11.0
230 | 4-September-2016
231 |
232 | * Remove babel-polyfill since we don't use it.
233 | * Update versions with the help from greenkeeper. [PR421](https://github.com/kadirahq/react-storybook/pull/421)
234 |
235 | ### v2.10.0
236 | 3-September-2016
237 |
238 | * Adding airbnb-js-shims again. [PR419](https://github.com/kadirahq/react-storybook/pull/419)
239 |
240 | ### v2.9.1
241 | 2-September-2016.
242 |
243 | * Use the config directory to store the addon database file [PR418](https://github.com/kadirahq/react-storybook/pull/418).
244 |
245 | ### v2.9.0
246 | 2-September-2016.
247 |
248 | * Copy the addon-db.json file when building static storybooks [PR417](https://github.com/kadirahq/react-storybook/pull/417).
249 |
250 | ### v2.8.0
251 | 2-September-2016.
252 |
253 | * Update @kadira/storybook to get the clean query params feature. See [storybook-ui-PR37](https://github.com/kadirahq/storybook-ui/pull/37)
254 |
255 | ### v2.7.0
256 | 1-September-2016
257 |
258 | * Add addon database feature [PR415](https://github.com/kadirahq/react-storybook/pull/415).
259 |
260 | ### v2.6.1
261 | 31-August-2016
262 |
263 | * Bring back HMR dev logs. [PR412](https://github.com/kadirahq/react-storybook/pull/412).
264 |
265 | ### v2.6.0
266 | 30-August-2016
267 |
268 | * Allow start/build params from env variables. [PR413](https://github.com/kadirahq/react-storybook/pull/413)
269 |
270 | ### v2.5.2
271 | 29-August-2016
272 |
273 | * Remove the use of babel-runtime/core-js modules. [PR410](https://github.com/kadirahq/react-storybook/pull/410)
274 |
275 | ### v2.5.1
276 | 24-August-2016
277 |
278 | * Update @kadira/storybook-ui to v3.3.2
279 |
280 | ### v2.5.0
281 | 24-August-2016
282 |
283 | * We are no longer shipping extra polyfills anymore. [PR402](https://github.com/kadirahq/react-storybook/pull/402)
284 |
285 | ### v2.4.2
286 | 24-August-2016
287 |
288 | * Allow file-loader URLs to work on subpaths. [PR401](https://github.com/kadirahq/react-storybook/pull/401)
289 |
290 | ### v2.4.1
291 | 24-August-2016
292 |
293 | * Bump @kadira/storybook ui to v3.3.1 to fix some UI related issues.
294 |
295 | ### v2.4.0
296 | 23-August-2016
297 |
298 | * Simplify the option to stop tracking. [PR399](https://github.com/kadirahq/react-storybook/pull/399)
299 | * Use JSON5 instead of CJSON to parse .babelrc. [PR398](https://github.com/kadirahq/react-storybook/pull/398)
300 | * Add webpack2 support by changing the use of OccurenceOrderPlugin. [PR397](https://github.com/kadirahq/react-storybook/pull/397)
301 | * Use @kadira/storybook-ui 2.3.0, which has new APIs to set URL for addons.
302 |
303 | ### v2.3.0
304 | 16-August-2016
305 |
306 | * Implement anonymous usage tracking. [PR384](https://github.com/kadirahq/react-storybook/pull/384)
307 |
308 | ### v2.2.3
309 | 15-August-2016
310 |
311 | * Add a hash to media file's filename. Otherwise, it'll cause issues when there are multiple images with the same filename but in different directories. [PR380](https://github.com/kadirahq/react-storybook/pull/380)
312 |
313 | ### v2.2.2
314 | 10-August-2016
315 |
316 | * Remove unused extract-text-webpack-plugin. This will add webpack2 support. [PR369](https://github.com/kadirahq/react-storybook/pull/369).
317 |
318 | ### v2.2.1
319 | 09-August-2016
320 |
321 | * Use @kadira/storybook-channel modules. [#PR359](https://github.com/kadirahq/react-storybook/pull/359).
322 | * Update @kadira/storybook-ui to the latest.
323 |
324 | ### v2.2.0
325 | 05-August-2016
326 |
327 | This release bring some webpack config related optimizations and the NPM2 support. Here are the notable changes:
328 |
329 | * Use es6-shim directly into webpack config. [PR355](https://github.com/kadirahq/react-storybook/pull/355)
330 | * Use the default babel-config based on CRA's config. [PR354](https://github.com/kadirahq/react-storybook/pull/354)
331 | * Add NPM2 support. [PR356](https://github.com/kadirahq/react-storybook/pull/356)
332 | * Add autofixer defaults. [PR357](https://github.com/kadirahq/react-storybook/pull/357)
333 |
334 | ### v2.1.1
335 | 03-August-2016
336 |
337 | Remove default webpack config for all config types. [PR348](https://github.com/kadirahq/react-storybook/pull/348)
338 |
339 | Now we only use the Create React App based config if there's no custom webpack config.
340 | This will fix issues like [#347](https://github.com/kadirahq/react-storybook/issues/347).
341 |
342 | ### v2.1.0
343 | 02-August-2016
344 |
345 | Add support for the addon API. See [PR346](https://github.com/kadirahq/react-storybook/pull/346).
346 |
347 | Here after we are using most of the features including actions,links as plugins.
348 | So, this introduced a huge area to add customizations to React Storybook.
349 |
350 | Unfortunately, as of this version, there are no docs for this feature. But, you can have a look at these addons:
351 |
352 | * actions addon (powers the action logger): https://github.com/kadirahq/storybook-addon-actions
353 | * links addon (powers the linkTo feature): https://github.com/kadirahq/storybook-addon-links
354 |
355 | Have a look at [here](https://github.com/kadirahq/react-storybook/blob/master/src/server/config.js#L88) to how to configure addons.
356 |
357 | ### v2.0.0
358 | 01-August-2016
359 |
360 | This is the starting of the next major version of React Storybook. This version is almost compatible with `v1.x.x` but defaults have been changes as discussed below. That's why we are starting out a new version.
361 |
362 | * Update defaults to match create-react-app. [PR342](https://github.com/kadirahq/react-storybook/pull/342). Here are the notable changes:
363 | * Add postcss based CSS loader.
364 | * Add file-loader for images and common types.
365 | * Add url-loader for shorter media files.
366 | * Do not pre-build manager(storybook UI) bundle.
367 | * Continue support for babel's stage-0 preset and add es2016 preset.
368 | * Update @kadira/storybook-ui to v2.6.1 to remove some React warnings.
369 |
370 | ### v1.41.0
371 |
372 | * Fix nodejs require errors [#337](https://github.com/kadirahq/react-storybook/pull/337).
373 | * Add getStorybook method to client API [#332](https://github.com/kadirahq/react-storybook/pull/332).
374 |
375 | ### v1.40.0
376 |
377 | * Fix duplicate decorator bug [#335](https://github.com/kadirahq/react-storybook/pull/335).
378 |
379 | ### v1.39.1
380 |
381 | * Update babel packages [#325](https://github.com/kadirahq/react-storybook/pull/325).
382 | * Hide HMR info logs [#331](https://github.com/kadirahq/react-storybook/pull/331).
383 |
384 | ### v1.39.0
385 |
386 | * Update @kadira/storybook-ui to get features from [v2.5.0](https://github.com/kadirahq/storybook-ui/blob/master/CHANGELOG.md#v250) and [v2.6.0](https://github.com/kadirahq/storybook-ui/blob/master/CHANGELOG.md#v260).
387 |
388 | ### v1.38.3
389 |
390 | * Add names for action and linkTo functions [#321](https://github.com/kadirahq/react-storybook/pull/321).
391 |
392 | ### v1.38.2
393 |
394 | * Fix error in prepublish script [#319](https://github.com/kadirahq/react-storybook/pull/319).
395 |
396 | ### v1.38.1
397 |
398 | * Improve Windows support by writing prepublish script using shelljs [#308](https://github.com/kadirahq/react-storybook/pull/308).
399 |
400 | ### v1.38.0
401 |
402 | * v1.37.0 was a nightmare since it contains the npm-shrinkwrap.json. Fixed by removing it. See: [#306](https://github.com/kadirahq/react-storybook/issues/306) and [#305](https://github.com/kadirahq/react-storybook/pull/305).
403 |
404 | ### v1.37.0
405 |
406 | * Update @kadira/storybook-ui to 2.4.0
407 |
408 | ### v1.36.0
409 |
410 | * Support watchOptions configuration. See: [PR287](https://github.com/kadirahq/react-storybook/pull/287)
411 |
412 | ### v1.35.2
413 |
414 | * Add missing font-face to the ErrorDisplay's heading.
415 |
416 | ### v1.35.1
417 |
418 | * Fix issue related to bad error handling. See issue [#275](https://github.com/kadirahq/react-storybook/issues/275):
419 |
420 | ### v1.35.0
421 |
422 | * Add fuzzy search powered search box and Redux DevTools support via [@kadira/storybook-ui@2.3.0](https://github.com/kadirahq/storybook-ui/blob/master/CHANGELOG.md#v230).
423 |
424 | ### v1.34.1
425 |
426 | * Don't always override NODE_ENV in build-storybook. [PR272](https://github.com/kadirahq/react-storybook/pull/272)
427 |
428 | ### v1.34.0
429 |
430 | * Use storybook-ui v2.2.0 which puts shortcut state into the URL.
431 |
432 | ### v1.33.0
433 |
434 | * Introduce an [extension API](https://github.com/kadirahq/react-storybook/blob/master/docs/extensions.md) for React Storybook. See: [PR258](https://github.com/kadirahq/react-storybook/pull/258)
435 |
436 | ### v1.32.1
437 |
438 | * Extend @kadira/storybook-ui provider from it's base Provider.
439 |
440 | ### v1.32.0
441 |
442 | * Use @kadira/storybook-ui as the manager UI with the implemented provider for React. See `client/manager` for more info.
443 |
444 | ### v1.31.0
445 |
446 | * Pass a `context` argument to stories [PR250](https://github.com/kadirahq/react-storybook/pull/250)
447 |
448 | ### v1.30.0
449 |
450 | * Fuzzy search kinds [PR247](https://github.com/kadirahq/react-storybook/pull/247)
451 |
452 | ### v1.29.5
453 |
454 | * Update dependency version to fix filter crash [PR246](https://github.com/kadirahq/react-storybook/pull/246)
455 |
456 | ### v1.29.4
457 |
458 | * Protect index.html/iframe.html from being overwritten [PR243](https://github.com/kadirahq/react-storybook/pull/243)
459 |
460 | ### v1.29.3
461 |
462 | * Update @kadira/storybook-core version [PR241](https://github.com/kadirahq/react-storybook/pull/241)
463 | * Add es6-shim by default [PR238](https://github.com/kadirahq/react-storybook/pull/238)
464 |
465 | ### v1.29.2
466 |
467 | * Use url.resolve instead of path.join [PR240](https://github.com/kadirahq/react-storybook/pull/240)
468 |
469 | ### v1.29.1
470 |
471 | * Copy missed manager.js.map file on static build [PR236](https://github.com/kadirahq/react-storybook/pull/236)
472 |
473 | ### v1.29.0
474 |
475 | * Multiple static dirs (comma separated) [PR229](https://github.com/kadirahq/react-storybook/pull/229)
476 |
477 | ### v1.28.5
478 |
479 | * Support ECMAScript stage-0 [PR228](https://github.com/kadirahq/react-storybook/pull/228) to fix [Issue #227](https://github.com/kadirahq/react-storybook/issues/227)
480 |
481 | ### v1.28.4
482 |
483 | * Support custom webpack public path for dev-server and static build started by [PR226](https://github.com/kadirahq/react-storybook/pull/226)
484 |
485 | ### v1.28.3
486 |
487 | * Revert [PR226](https://github.com/kadirahq/react-storybook/pull/226)
488 |
489 | ### v1.28.2
490 |
491 | * Support custom webpack publicPath [PR226](https://github.com/kadirahq/react-storybook/pull/226)
492 |
493 | ### v1.28.1
494 |
495 | * Add charset meta tags to HTML heads [PR216](https://github.com/kadirahq/react-storybook/pull/216)
496 |
497 | ### v1.28.0
498 |
499 | * Moved storybook serving code into a middleware to support more advanced use cases.
500 | * Refactored dev server to use storybook middleware [PR211](https://github.com/kadirahq/react-storybook/pull/211)
501 |
502 | ### v1.27.0
503 |
504 | * Move modules to storybook-core repo. [PR196](https://github.com/kadirahq/react-storybook/pull/196)
505 | * Add stack-source-map again only for Chrome to get better error stacks.
506 | * Add ability to control the hostname. See [PR195](https://github.com/kadirahq/react-storybook/pull/195) and [PR198](https://github.com/kadirahq/react-storybook/pull/198)
507 |
508 | ### v1.26.0
509 | 12-May-2016
510 |
511 | * Ensure asset directory exists in the static-builder.
512 |
513 | ### v1.25.0
514 | 11-May-2016
515 |
516 | * Fix several publishing related issues. See: [#188](https://github.com/kadirahq/react-storybook/pull/188).
517 | * Fix babel extends issue. See: [PR185](https://github.com/kadirahq/react-storybook/pull/185).
518 | * Fix issue with removing a preset from users babelrc.
519 | * Fixes: [#183](https://github.com/kadirahq/react-storybook/issues/183).
520 | * [PR184](https://github.com/kadirahq/react-storybook/pull/184)
521 | * Make left panel scrollable with keeping the filterbox always. See: [PR182](https://github.com/kadirahq/react-storybook/pull/182).
522 | * Add `qs` as a direct dependency as it's used in preview.
523 |
524 | ### v1.24.0
525 | 10-May-2016
526 |
527 | * Add a potential fix for the double scrollbar issue. See: [179](https://github.com/kadirahq/react-storybook/issues/179).
528 | * Add scrolling support to the left panel. Fixes [#177](https://github.com/kadirahq/react-storybook/issues/177).
529 | * Remove NODE_ENV=production flag. Fixes [#158](https://github.com/kadirahq/react-storybook/issues/158)
530 |
531 | ### v1.23.0
532 | 09-May-2016
533 |
534 | * Add shortcuts to jump to previous and next stories. See [PR176](https://github.com/kadirahq/react-storybook/pull/176)
535 | * Fix loader concatenation bug specially when custom config doesn't have a loaders section. [PR173](https://github.com/kadirahq/react-storybook/pull/173)
536 |
537 | ### v1.22.1
538 | 06-May-2016
539 |
540 | * Add a potential fix for [#167](https://github.com/kadirahq/react-storybook/issues/167)
541 | * basically, this moved back babel-packages required by webpack.
542 |
543 | ### v1.22.0
544 | 06-May-2016
545 |
546 | * Improve the static builder time.
547 |
548 | ### v1.21.0
549 | 06-May-2016
550 |
551 | * Add configType argument to custom config function. See: [PR169](https://github.com/kadirahq/react-storybook/pull/169)
552 | * Add the unicode version of the Keyboard Shortcut Icon. See: [PR170](https://github.com/kadirahq/react-storybook/pull/170)
553 |
554 | ### v1.20.0
555 | 05-May-2016
556 |
557 | * Allow to configure webpack as the user wants. See [PR160](https://github.com/kadirahq/react-storybook/pull/160)
558 | * Add typescript typings support for the core API. See [PR157](https://github.com/kadirahq/react-storybook/pull/157)
559 | * Implement Mantra architecture and some new features including permalinks, full screen support. See: [PR165](https://github.com/kadirahq/react-storybook/pull/165)
560 | * Remove some typo in docs. See: [PR154](https://github.com/kadirahq/react-storybook/pull/154)
561 | * Move UI testing libraries to devDependencies. See: [PR153](https://github.com/kadirahq/react-storybook/pull/153)
562 |
563 | ### v1.19.0
564 | 27-April-2016
565 |
566 | * Add airbnb-js-shims to client-side JS. See: [PR147](https://github.com/kadirahq/react-storybook/pull/147)
567 | * Remove self-closing div tag, which is invalid HTML. See: [PR148](https://github.com/kadirahq/react-storybook/pull/148)
568 | * Search for a .babelrc in the storybook config directory first, then the project root. See: [PR149](https://github.com/kadirahq/react-storybook/pull/149)
569 |
570 | ### v1.18.0
571 | 26-April-2016
572 |
573 | * Link React Storybook menu to the repo. See: [PR137](https://github.com/kadirahq/react-storybook/pull/137)
574 | * Implement keyboard shortcuts and fuzzy search. See: [PR141](https://github.com/kadirahq/react-storybook/pull/141)
575 |
576 | ### v1.17.2
577 | 25-April-2016
578 |
579 | * Fix an error which only occurs on Firefox. See: [PR144](https://github.com/kadirahq/react-storybook/pull/144)
580 |
581 | ### v1.17.1
582 | 21-April-2016
583 |
584 | * Fix a regression introduce by `v1.17.0`. See: [PR133](https://github.com/kadirahq/react-storybook/pull/133)
585 |
586 | ### v1.17.0
587 | 21-April-2016
588 |
589 | * Check all the arguments passed to action for events. See: [PR132](https://github.com/kadirahq/react-storybook/pull/132)
590 |
591 | ### v1.16.1
592 | 21-April-2016
593 |
594 | * Fix action logs highlighting issue, which comes as a regression of [PR126](https://github.com/kadirahq/react-storybook/pull/126).
595 |
596 | ### v1.16.0
597 | 20-April-2016
598 |
599 | * Prevent re-rendering the preview iframe when there is an action.
600 | * Related issue: [#116](https://github.com/kadirahq/react-storybook/issues/116)
601 | * Related PR: [PR126](https://github.com/kadirahq/react-storybook/pull/126)
602 |
603 | ### v1.15.0
604 | 20-April-2016
605 |
606 | * Improve action logger UI and increase max log count to 10. See [PR123](https://github.com/kadirahq/react-storybook/pull/123)
607 |
608 | ### v1.14.0
609 | 19-April-2016
610 |
611 | * Add syntax highlights to the logger. See: [PR118](https://github.com/kadirahq/react-storybook/pull/118)
612 |
613 | ### v1.13.0
614 |
615 | * Add some UI test cases. See [PR103](https://github.com/kadirahq/react-storybook/pull/103)
616 | * Implement `.addDecorator()` API. See [PR115](https://github.com/kadirahq/react-storybook/pull/115)
617 | * Add code folding support. See [PR111](https://github.com/kadirahq/react-storybook/pull/111)
618 |
619 | ### v1.12.0
620 | 14-April-2016
621 |
622 | * Add support for webpack module preLoaders. See: [PR107](https://github.com/kadirahq/react-storybook/pull/107)
623 |
624 | ### v1.11.0
625 | 13-April-2016
626 |
627 | * Add support for React DevTools. See: [PR104](https://github.com/kadirahq/react-storybook/pull/104)
628 |
629 | ### v1.10.2
630 | 12-April-2016
631 |
632 | Fix various issues related to static bundling.
633 |
634 | * Add custom head generation to static build as well.
635 | * Use relative urls so, static sites can be host with paths (GH Pages)
636 | * Identify SyntheticEvent using feature detection. UglifyJS mangal class names, so we can't use classnames to detect a SyntheticEvent in the static build.
637 |
638 | ### v1.10.1
639 |
640 | * Don't serve index.html in static directory as a site index. See [PR100](https://github.com/kadirahq/react-storybook/pull/100)
641 | * Use cjson for parsing .babelrc files (support comments). See [PR98](https://github.com/kadirahq/react-storybook/pull/98)
642 | * Remove the dist directory before running babel to avoid older code. See [PR101](https://github.com/kadirahq/react-storybook/pull/101)
643 |
644 | ### v1.10.0
645 |
646 | * Add custom head support inside the iframe. See [PR77](https://github.com/kadirahq/react-storybook/pull/77)
647 | * Unmount components before rendering into DOM node. Fix: [#81](https://github.com/kadirahq/react-storybook/issues/81)
648 | * Add a static file builder. See [PR88](https://github.com/kadirahq/react-storybook/pull/88)
649 | * Fix search box's lineHeight to work with all the browsers. See: [PR94](https://github.com/kadirahq/react-storybook/pull/94)
650 | * Add the search box. See: [PR91](https://github.com/kadirahq/react-storybook/pull/91).
651 |
652 | ### v1.9.0
653 |
654 | Add some minor improvements.
655 |
656 | * Avoid deprecated warning in Chrome Canary. See: [PR85](https://github.com/kadirahq/react-storybook/pull/85)
657 | * Fix the React Warning about CSS property. See: [PR84](https://github.com/kadirahq/react-storybook/pull/84)
658 | * Transition on latest logged action. See: [PR80](https://github.com/kadirahq/react-storybook/pull/80)
659 |
660 | ### v1.8.0
661 |
662 | * Add story linking functionality.
663 | * [Documentation](https://github.com/kadirahq/react-storybook/blob/master/docs/api.md#linking-stories).
664 | * Original feature request: [#50](https://github.com/kadirahq/react-storybook/issues/50)
665 | * Implementation: [PR86](https://github.com/kadirahq/react-storybook/pull/86)
666 |
667 | ### v1.7.0
668 |
669 | * Add support to React v15.0.0.
670 |
671 | ### v1.6.0
672 |
673 | * Make scrollable layout. See: [PR](https://github.com/kadirahq/react-storybook/pull/70)
674 | * Add npm3 requirement to the `package.json`.
675 | * Add `react` and `react-dom` to devDependencies.
676 |
677 | ### v1.5.0
678 |
679 | * Add support for most of the custom webpack configuration. See [PR64](https://github.com/kadirahq/react-storybook/pull/64)
680 |
681 | ### v1.4.0
682 |
683 | * Add CLI option to specify the config dir. See [PR52](https://github.com/kadirahq/react-storybook/pull/52).
684 |
685 | ### v1.3.0
686 |
687 | * Load the `.babelrc` manually. Fixed: [#41](https://github.com/kadirahq/react-storybook/issues/41)
688 | * Add a better contributing guide. See [CONTRIBUTING.md](https://github.com/kadirahq/react-storybook/blob/master/CONTRIBUTING.md)
689 | * Add a development utility `npm run dev` which watches "src" directory and run `npm run prepublish`.
690 |
691 | ### v1.2.0
692 |
693 | * Add a button to clear logs in the ActionLogger. This is requested in [PR21](https://github.com/kadirahq/react-storybook/issues/21).
694 | * Remove navigation list order hijacking. See [commit](https://github.com/kadirahq/react-storybook/commit/166365fd38f51f79e69e028a1c11e2620eddcb99).
695 | * Fix a typo in .gitignore. See [PR31](https://github.com/kadirahq/react-storybook/pull/31).
696 | * Add support for JSX. See [PR18](https://github.com/kadirahq/react-storybook/pull/18).
697 |
698 | ### v1.1.0
699 |
700 | * v1.0.0 was a mistake and it contains very old code. That's why we had to do a 1.1.0 release.
701 |
702 | ### v1.0.0
703 |
704 | * Yeah!
705 |
--------------------------------------------------------------------------------