Skip to content
This repository has been archived by the owner on Nov 10, 2017. It is now read-only.

Add option to use storybook with more than one user #132

Merged
merged 7 commits into from
Mar 28, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"shelljs": "^0.7.3",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"uuid": "^3.0.1",
"webpack": "^1.13.2",
"webpack-dev-middleware": "^1.8.3",
"webpack-hot-middleware": "^2.12.2"
Expand Down
21 changes: 14 additions & 7 deletions src/bin/storybook-start.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ program
.option('-h, --host <host>', 'host to listen on')
.option('-p, --port <port>', 'port to listen on')
.option('-c, --config-dir [dir-name]', 'storybook config directory')
.option('-r, --reset-cache', 'reset react native packager')
.option('-s, --skip-packager', 'run only storybook server')
.option('-i, --manual-id', 'allow multiple users to work with same storybook')
.parse(process.argv);

const projectDir = path.resolve();
Expand All @@ -18,7 +21,7 @@ if (program.host) {
listenAddr.push(program.host);
}

const server = new Server({projectDir, configDir});
const server = new Server({projectDir, configDir, manualId: program.manualId});
server.listen(...listenAddr, function (err) {
if (err) {
throw err;
Expand All @@ -27,11 +30,15 @@ server.listen(...listenAddr, function (err) {
console.info(`\nReact Native Storybook started on => ${address}\n`);
});

const projectRoots = configDir === projectDir ? [configDir] : [configDir, projectDir];
if (!program.skipPackager) {
const projectRoots = configDir === projectDir ? [configDir] : [configDir, projectDir];

// RN packager
shelljs.exec([
'node node_modules/react-native/local-cli/cli.js start',
`--projectRoots ${projectRoots.join(',')}`,
`--root ${projectDir}`,
].join(' '), {async: true});
shelljs.exec([
'node node_modules/react-native/local-cli/cli.js start',
`--projectRoots ${projectRoots.join(',')}`,
`--root ${projectDir}`,
program.resetCache && '--reset-cache'
].filter(x => x).join(' '), {async: true});

}
2 changes: 1 addition & 1 deletion src/manager/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import renderStorybookUI from '@kadira/storybook-ui';
import Provider from './provider';

const rootEl = document.getElementById('root');
renderStorybookUI(rootEl, new Provider({ url: `ws://${location.host}` }));
renderStorybookUI(rootEl, new Provider({ url: `ws://${location.host}`, options: window.storybookOptions}));
28 changes: 24 additions & 4 deletions src/manager/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@ import React from 'react';
import { Provider } from '@kadira/storybook-ui';
import createChannel from '@kadira/storybook-channel-websocket';
import addons from '@kadira/storybook-addons';
import uuid from 'uuid';

export default class ReactProvider extends Provider {
constructor({ url }) {
constructor({ url: domain, options }) {
super();
this.options = options;
this.selection = null;
this.channel = addons.getChannel();

let url = domain;
if (options.manualId) {
const pairedId = uuid().substr(-6);

this.pairedId = pairedId;
url = domain + '/pairedId=' + this.pairedId;
}

if (!this.channel) {
this.channel = createChannel({ url });
addons.setChannel(this.channel);
Expand All @@ -22,10 +33,19 @@ export default class ReactProvider extends Provider {
this.selection = { kind, story };
this.channel.emit('setCurrentStory', { kind, story });
const renderPreview = addons.getPreview();
if (renderPreview) {
return renderPreview(kind, story);

const innerPreview = renderPreview ? renderPreview(kind, story) : null;

if (this.options.manualId) {
return (
<div>
Your ID: { this.pairedId }
{ innerPreview }
</div>
);
}
return null;

return innerPreview;
}

handleAPI(api) {
Expand Down
9 changes: 8 additions & 1 deletion src/preview/components/StoryView/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ export default class StoryView extends Component {
constructor(props, ...args) {
super(props, ...args);
this.state = {storyFn: null, selection: {}};
this.props.events.on('story', this.selectStory.bind(this));

this.storyHandler = this.selectStory.bind(this);

this.props.events.on('story', this.storyHandler);
}

componentWillUnmount() {
this.props.events.removeListener('story', this.storyHandler);
}

selectStory(storyFn, selection) {
Expand Down
4 changes: 2 additions & 2 deletions src/preview/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ export default class Preview {
return () => {
let webUrl = null;
let channel = addons.getChannel();
if (!channel) {
if (params.resetStorybook || !channel) {
const host = params.host || 'localhost';
const port = params.port || 7007;
const url = `ws://${host}:${port}`;
const url = `ws://${host}:${port}/${params.query}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If params.query is not set, the url will have /undefined in the end. Better set it to an empty string if it's not set.

const query = params.query || '';
const url = `ws://${host}:${port}/${query}`;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, thanks :)

webUrl = `http://${host}:${port}`;
channel = createChannel({ url });
addons.setChannel(channel);
Expand Down
5 changes: 4 additions & 1 deletion src/server/index.html.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import url from 'url';

export default function (publicPath, settings) {
export default function (publicPath, options) {
return `
<!DOCTYPE html>
<html>
Expand Down Expand Up @@ -40,6 +40,9 @@ export default function (publicPath, settings) {
</head>
<body style="margin: 0;">
<div id="root"></div>
<script>
window.storybookOptions = ${JSON.stringify(options)};
</script>
<script src="${url.resolve(publicPath, 'static/manager.bundle.js')}"></script>
</body>
</html>
Expand Down
20 changes: 18 additions & 2 deletions src/server/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import express from 'express';
import querystring from 'querystring';
import http from 'http';
import ws from 'ws';
import storybook from './middleware';
Expand All @@ -8,15 +9,30 @@ export default class Server {
this.options = options;
this.httpServer = http.createServer();
this.expressApp = express();
this.expressApp.use(storybook(options.projectDir, options.configDir));
this.expressApp.use(storybook(options));
this.httpServer.on('request', this.expressApp);
this.wsServer = ws.Server({server: this.httpServer});
this.wsServer.on('connection', s => this.handleWS(s));
}

handleWS(socket) {

if (this.options.manualId) {
const params = socket.upgradeReq && socket.upgradeReq.url
? querystring.parse(socket.upgradeReq.url.substr(1))
: {};

if (params.pairedId) {
socket.pairedId = params.pairedId;
}
}

socket.on('message', data => {
this.wsServer.clients.forEach(c => c.send(data));
this.wsServer.clients.forEach(c => {
if (!this.options.manualId || (socket.pairedId && socket.pairedId === c.pairedId)) {
return c.send(data);
}
});
});
}

Expand Down
6 changes: 4 additions & 2 deletions src/server/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function getMiddleware(configDir) {
return function () {};
}

export default function (projectDir, configDir) {
export default function ({projectDir, configDir, ...options}) {
// Build the webpack configuration using the `baseConfig`
// custom `.babelrc` file and `webpack.config.js` files
const config = loadConfig('DEVELOPMENT', baseConfig, projectDir, configDir);
Expand All @@ -46,7 +46,9 @@ export default function (projectDir, configDir) {
router.use(webpackHotMiddleware(compiler));

router.get('/', function (req, res) {
res.send(getIndexHtml(publicPath));
res.send(getIndexHtml(publicPath, {
manualId: options.manualId,
}));
});

return router;
Expand Down