diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000000..c2d2a91d0e89 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Your Name. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/addon-notes/.babelrc b/packages/addon-notes/.babelrc new file mode 100644 index 000000000000..e68d2fea1136 --- /dev/null +++ b/packages/addon-notes/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015", "stage-2", "react"] +} diff --git a/packages/addon-notes/.scripts/get_gh_pages_url.js b/packages/addon-notes/.scripts/get_gh_pages_url.js new file mode 100644 index 000000000000..062c690d8bea --- /dev/null +++ b/packages/addon-notes/.scripts/get_gh_pages_url.js @@ -0,0 +1,11 @@ +// IMPORTANT +// --------- +// This is an auto generated file with React CDK. +// Do not modify this file. + +const parse = require('git-url-parse'); +var ghUrl = process.argv[2]; +const parsedUrl = parse(ghUrl); + +const ghPagesUrl = 'https://' + parsedUrl.owner + '.github.io/' + parsedUrl.name; +console.log(ghPagesUrl); diff --git a/packages/addon-notes/.scripts/mocha_runner.js b/packages/addon-notes/.scripts/mocha_runner.js new file mode 100644 index 000000000000..87d88197cbc9 --- /dev/null +++ b/packages/addon-notes/.scripts/mocha_runner.js @@ -0,0 +1,33 @@ +// IMPORTANT +// --------- +// This is an auto generated file with React CDK. +// Do not modify this file. +// Use `.scripts/user/pretest.js instead`. + +require('babel-core/register'); +require('babel-polyfill'); + +// Add jsdom support, which is required for enzyme. +var jsdom = require('jsdom').jsdom; + +var exposedProperties = ['window', 'navigator', 'document']; + +global.document = jsdom(''); +global.window = document.defaultView; +Object.keys(document.defaultView).forEach((property) => { + if (typeof global[property] === 'undefined') { + exposedProperties.push(property); + global[property] = document.defaultView[property]; + } +}); + +global.navigator = { + userAgent: 'node.js' +}; + +process.on('unhandledRejection', function (error) { + console.error('Unhandled Promise Rejection:'); + console.error(error && error.stack || error); +}); + +require('./user/pretest.js'); diff --git a/packages/addon-notes/.scripts/prepublish.sh b/packages/addon-notes/.scripts/prepublish.sh new file mode 100644 index 000000000000..a25d40dee4ed --- /dev/null +++ b/packages/addon-notes/.scripts/prepublish.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# IMPORTANT +# --------- +# This is an auto generated file with React CDK. +# Do not modify this file. +# Use `.scripts/user/prepublish.sh instead`. + +echo "=> Transpiling 'src' into ES5 ..." +echo "" +rm -rf ./dist +./node_modules/.bin/babel --ignore tests,stories --plugins "transform-runtime" ./src --out-dir ./dist +echo "" +echo "=> Transpiling completed." + +. .scripts/user/prepublish.sh diff --git a/packages/addon-notes/.scripts/publish_storybook.sh b/packages/addon-notes/.scripts/publish_storybook.sh new file mode 100644 index 000000000000..f8457308528e --- /dev/null +++ b/packages/addon-notes/.scripts/publish_storybook.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# IMPORTANT +# --------- +# This is an auto generated file with React CDK. +# Do not modify this file. + +set -e # exit with nonzero exit code if anything fails + +# get GIT url + +GIT_URL=`git config --get remote.origin.url` +if [[ $GIT_URL == "" ]]; then + echo "This project is not configured with a remote git repo". + exit 1 +fi + +# clear and re-create the out directory +rm -rf .out || exit 0; +mkdir .out; + +# run our compile script, discussed above +build-storybook -o .out + +# go to the out directory and create a *new* Git repo +cd .out +git init + +# inside this git repo we'll pretend to be a new user +git config user.name "GH Pages Bot" +git config user.email "hello@ghbot.com" + +# The first and only commit to this new Git repo contains all the +# files present with the commit message "Deploy to GitHub Pages". +git add . +git commit -m "Deploy Storybook to GitHub Pages" + +# Force push from the current repo's master branch to the remote +# repo's gh-pages branch. (All previous history on the gh-pages branch +# will be lost, since we are overwriting it.) We redirect any output to +# /dev/null to hide any sensitive credential data that might otherwise be exposed. +git push --force --quiet $GIT_URL master:gh-pages > /dev/null 2>&1 +cd .. +rm -rf .out + +echo "" +echo "=> Storybook deployed to: `node .scripts/get_gh_pages_url.js $GIT_URL`" diff --git a/packages/addon-notes/.scripts/user/prepublish.sh b/packages/addon-notes/.scripts/user/prepublish.sh new file mode 100644 index 000000000000..fdb64997963e --- /dev/null +++ b/packages/addon-notes/.scripts/user/prepublish.sh @@ -0,0 +1 @@ +# Use this file to your own code to run at NPM `prepublish` event. diff --git a/packages/addon-notes/.scripts/user/pretest.js b/packages/addon-notes/.scripts/user/pretest.js new file mode 100644 index 000000000000..a81bd36c2255 --- /dev/null +++ b/packages/addon-notes/.scripts/user/pretest.js @@ -0,0 +1 @@ +// Use this file to setup any test utilities. diff --git a/packages/addon-notes/.storybook/config.js b/packages/addon-notes/.storybook/config.js new file mode 100644 index 000000000000..d279dbf59011 --- /dev/null +++ b/packages/addon-notes/.storybook/config.js @@ -0,0 +1,12 @@ +// IMPORTANT +// --------- +// This is an auto generated file with React CDK. +// Do not modify this file. + +import { configure } from '@kadira/storybook'; + +function loadStories() { + require('../src/stories'); +} + +configure(loadStories, module); diff --git a/packages/addon-notes/CHANGELOG.md b/packages/addon-notes/CHANGELOG.md new file mode 100644 index 000000000000..ac02677abb34 --- /dev/null +++ b/packages/addon-notes/CHANGELOG.md @@ -0,0 +1,11 @@ +# ChangeLog + +### v1.0.1 +30-August-2016 + +* Update docs. + +### v1.0.0 +30-August-2016 + +* Initial version diff --git a/packages/addon-notes/CONTRIBUTING.md b/packages/addon-notes/CONTRIBUTING.md new file mode 100644 index 000000000000..3b228bda329f --- /dev/null +++ b/packages/addon-notes/CONTRIBUTING.md @@ -0,0 +1,7 @@ +# Contributing to Storybook Addon Notes Component + +We welcome your help to make this component better. This document will help to streamline the contributing process and save everyone's precious time. + +## Development Setup + +This component has been setup with [React CDK](https://github.com/kadirahq/react-cdk). Refer [React CDK documentation](https://github.com/kadirahq/react-cdk)) to get started with the development. diff --git a/packages/addon-notes/README.md b/packages/addon-notes/README.md new file mode 100644 index 000000000000..12f2699a02a5 --- /dev/null +++ b/packages/addon-notes/README.md @@ -0,0 +1,42 @@ +# Storybook Addon Notes + +This [Storybook](https://getstorybook.io) addon allows you to write notes for your stories. + +![Storybook Addon Notes Demo](docs/demo.png) + +### Getting Started +**note: addons require @kadira/storybook 2.x or greater* + +```sh +npm i --save-dev @kadira/storybook-addon-notes +``` + +Then create a file called `addons.js` in your storybook config. + +Add following content to it: + +```js +import '@kadira/storybook/addons'; +import '@kadira/storybook-addon-notes/register'; +``` + +Then write your stories like this: + +```js +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import Button from './Button'; +import { WithNotes } from '@kadira/storybook-addon-notes'; + +storiesOf('Button', module) + .add('with text', () => ( + + + + )) + .add('with some emoji', () => ( + + + + )); +``` diff --git a/packages/addon-notes/docs/demo.png b/packages/addon-notes/docs/demo.png new file mode 100644 index 000000000000..1a1d1226df93 Binary files /dev/null and b/packages/addon-notes/docs/demo.png differ diff --git a/packages/addon-notes/package.json b/packages/addon-notes/package.json new file mode 100644 index 000000000000..7bd7b691d61c --- /dev/null +++ b/packages/addon-notes/package.json @@ -0,0 +1,44 @@ +{ + "name": "@kadira/storybook-addon-notes", + "version": "1.0.1", + "description": "Write notes for your Storybook stories.", + "repository": { + "type": "git", + "url": "https://github.com/kadirahq/storybook-addon-notes.git" + }, + "license": "MIT", + "scripts": { + "prepublish": ". ./.scripts/prepublish.sh", + "test": "echo \"Error: no test specified\" && exit 0", + "storybook": "start-storybook -p 9010", + "publish-storybook": "bash .scripts/publish_storybook.sh" + }, + "devDependencies": { + "react": "^15.3.2", + "react-dom": "^15.3.2", + "babel-core": "^6.5.0", + "babel-loader": "^6.2.4", + "babel-polyfill": "^6.5.0", + "babel-preset-es2015": "^6.5.0", + "babel-preset-react": "^6.5.0", + "babel-preset-stage-2": "^6.5.0", + "babel-plugin-transform-runtime": "^6.5.0", + "babel-cli": "^6.5.0", + "react-addons-test-utils": "^15.3.2", + "@kadira/storybook": "^2.20.1", + "git-url-parse": "^6.0.1" + }, + "peerDependencies": { + "react": "^0.14.7 || ^15.0.0", + "@kadira/storybook-addons": "^v1.3.1" + }, + "dependencies": { + "babel-runtime": "^6.5.0" + }, + "main": "dist/index.js", + "keywords": [ + "react", + "storybook", + "addon" + ] +} diff --git a/packages/addon-notes/register.js b/packages/addon-notes/register.js new file mode 100644 index 000000000000..18cdafda57c4 --- /dev/null +++ b/packages/addon-notes/register.js @@ -0,0 +1 @@ +require('./dist/register.js'); diff --git a/packages/addon-notes/src/index.js b/packages/addon-notes/src/index.js new file mode 100644 index 000000000000..318effe60a8a --- /dev/null +++ b/packages/addon-notes/src/index.js @@ -0,0 +1,19 @@ +import React from 'react'; +import addons from '@kadira/storybook-addons'; + +export class WithNotes extends React.Component { + render() { + const { children, notes } = this.props; + const channel = addons.getChannel(); + + // send the notes to the channel. + channel.emit('kadira/notes/add_notes', notes); + // return children elements. + return children; + } +} + +WithNotes.propTypes = { + children: React.PropTypes.node, + notes: React.PropTypes.string, +}; diff --git a/packages/addon-notes/src/register.js b/packages/addon-notes/src/register.js new file mode 100644 index 000000000000..5257c94db88a --- /dev/null +++ b/packages/addon-notes/src/register.js @@ -0,0 +1,72 @@ +import React from 'react'; +import addons from '@kadira/storybook-addons'; + +const styles = { + notesPanel: { + margin: 10, + fontFamily: 'Arial', + fontSize: 14, + color: '#444', + width: '100%', + overflow: 'auto', + }, +}; + +export class Notes extends React.Component { + constructor(...args) { + super(...args); + this.state = { text: '' }; + this.onAddNotes = this.onAddNotes.bind(this); + } + + componentDidMount() { + const { channel, api } = this.props; + // Listen to the notes and render it. + channel.on('kadira/notes/add_notes', this.onAddNotes); + + // Clear the current notes on every story change. + this.stopListeningOnStory = api.onStory(() => { + this.onAddNotes(''); + }); + } + + // This is some cleanup tasks when the Notes panel is unmounting. + componentWillUnmount() { + if (this.stopListeningOnStory) { + this.stopListeningOnStory(); + } + + this.unmounted = true; + const { channel } = this.props; + channel.removeListener('kadira/notes/add_notes', this.onAddNotes); + } + + onAddNotes(text) { + this.setState({ text }); + } + + render() { + const { text } = this.state; + const textAfterFormatted = text ? text.trim().replace(/\n/g, '
') : ''; + + return ( +
+
+
+ ); + } +} + +Notes.propTypes = { + channel: React.PropTypes.object, + api: React.PropTypes.object, +}; + +// Register the addon with a unique name. +addons.register('kadira/notes', api => { + // Also need to set a unique name to the panel. + addons.addPanel('kadira/notes/panel', { + title: 'Notes', + render: () => , + }); +}); diff --git a/packages/addon-notes/src/stories/index.js b/packages/addon-notes/src/stories/index.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/addon-notes/src/tests/index.js b/packages/addon-notes/src/tests/index.js new file mode 100644 index 000000000000..d06c3dd08746 --- /dev/null +++ b/packages/addon-notes/src/tests/index.js @@ -0,0 +1,5 @@ +const { describe, it } = global; + +describe('Storybook Addon Notes', () => { + it('should have some tests'); +}); diff --git a/packages/channel-postmessage/.babelrc b/packages/channel-postmessage/.babelrc new file mode 100644 index 000000000000..c14b2828d168 --- /dev/null +++ b/packages/channel-postmessage/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["react-app"] +} diff --git a/packages/channel-postmessage/.scripts/npm-prepublish.js b/packages/channel-postmessage/.scripts/npm-prepublish.js new file mode 100644 index 000000000000..af186127d93a --- /dev/null +++ b/packages/channel-postmessage/.scripts/npm-prepublish.js @@ -0,0 +1,9 @@ +var path = require('path'); +var shell = require('shelljs'); +var babel = ['node_modules', '.bin', 'babel'].join(path.sep); + +// required for react-app preset +process.env.NODE_ENV = 'production'; + +shell.rm('-rf', 'dist') +shell.exec(babel + ' --ignore __tests__ src --out-dir dist') diff --git a/packages/channel-postmessage/CHANGELOG.md b/packages/channel-postmessage/CHANGELOG.md new file mode 100644 index 000000000000..ce23646e2edf --- /dev/null +++ b/packages/channel-postmessage/CHANGELOG.md @@ -0,0 +1,41 @@ +## Changelog + +### v2.0.1 +24-Nov-2016 + +* Fix empty story list when loading in an iframe [PR8](https://github.com/kadirahq/storybook-channel-postmsg/pull/8) + +### v2.0.0 +17-Nov-2016 + +* Identify the page with a parameter [PR7](https://github.com/kadirahq/storybook-channel-postmsg/pull/7) + +### v1.1.0 +28-Oct-2016 + +* Use a constant key to identify relavent messages [PR6](https://github.com/kadirahq/storybook-channel-postmsg/pull/6) + +### v1.0.4 +28-Oct-2016 + +* Allow message handlers to throw errors [PR4](https://github.com/kadirahq/storybook-channel-postmsg/pull/4) + +### v1.0.3 +28-Sep-2016 + +* Serialize data because some objects throw error when sent over postMessage [PR3](https://github.com/kadirahq/storybook-channel-postmsg/pull/3) + +### v1.0.2 +28-Sep-2016 + +* Do not serialize data because objects can be sent over postMessage [PR1](https://github.com/kadirahq/storybook-channel-postmsg/pull/1) + +### v1.0.1 +26-Sep-2016 + +* Select the iframe by id (getElementById) + +### v1.0.0 +26-Sep-2016 + +* Implement send and receive functions diff --git a/packages/channel-postmessage/README.md b/packages/channel-postmessage/README.md new file mode 100644 index 000000000000..cdd7b75a9de7 --- /dev/null +++ b/packages/channel-postmessage/README.md @@ -0,0 +1,8 @@ +# Post Message Channel + +Post Message channel for Kadira Storybooks. This channel can be used when the Storybook Renderer runs inside an iframe or a child window. A channel can be created using the `createChannel` function. + +```js +import createChannel from '@kadira/storybook-channel-postmsg' +const channel = createChannel({ key: 'postmsg-key' }) +``` diff --git a/packages/channel-postmessage/package.json b/packages/channel-postmessage/package.json new file mode 100644 index 000000000000..0521c2204cb0 --- /dev/null +++ b/packages/channel-postmessage/package.json @@ -0,0 +1,20 @@ +{ + "name": "@kadira/storybook-channel-postmsg", + "version": "2.0.1", + "description": "", + "main": "dist/index.js", + "scripts": { + "prepublish": "node .scripts/npm-prepublish.js", + "test": "echo \"Error: no test specified\" && exit 0" + }, + "license": "MIT", + "devDependencies": { + "babel-cli": "^6.11.4", + "babel-preset-react-app": "^0.2.1", + "shelljs": "^0.7.3" + }, + "dependencies": { + "@kadira/storybook-channel": "^1.1.0", + "json-stringify-safe": "^5.0.1" + } +} diff --git a/packages/channel-postmessage/src/index.js b/packages/channel-postmessage/src/index.js new file mode 100644 index 000000000000..d3a73b82db33 --- /dev/null +++ b/packages/channel-postmessage/src/index.js @@ -0,0 +1,79 @@ +import Channel from '@kadira/storybook-channel'; +import stringify from 'json-stringify-safe'; + +export const KEY = 'storybook-channel'; + +export default function createChannel({ page }) { + const transport = new PostmsgTransport({ page }); + return new Channel({ transport }); +} + +export class PostmsgTransport { + constructor(config) { + this._config = config; + this._buffer = []; + this._handler = null; + window.addEventListener('message', this._handleEvent.bind(this), false); + document.addEventListener('DOMContentLoaded', () => this._flush()); + // Check whether the config.page parameter has a valid value + if (config.page !== 'manager' && config.page !== 'preview') { + throw new Error(`postmsg-channel: "config.page" cannot be "${config.page}"`); + } + } + + setHandler(handler) { + this._handler = handler; + } + + send(event) { + const iframeWindow = this._getWindow(); + if (!iframeWindow) { + return new Promise((resolve, reject) => { + this._buffer.push({ event, resolve, reject }); + }); + } + const data = stringify({ key: KEY, event }); + iframeWindow.postMessage(data, '*'); + return Promise.resolve(null); + } + + _flush() { + const buffer = this._buffer; + this._buffer = []; + buffer.forEach(item => { + this.send(item.event).then(item.resolve).catch(item.reject); + }); + } + + _getWindow() { + if (this._config.page === 'manager') { + // FIXME this is a really bad idea! use a better way to do this. + // This finds the storybook preview iframe to send messages to. + const iframe = document.getElementById('storybook-preview-iframe'); + if (!iframe) { + return null; + } + return iframe.contentWindow; + } + return window.parent; + } + + _handleEvent(e) { + if (!e.data || typeof e.data !== 'string') { + return; + } + let data; + try { + data = JSON.parse(e.data); + } catch (e) { + return null; + } + if (!data || typeof data !== 'object') { + return null; + } + const { key, event } = data; + if (key === KEY) { + this._handler(event); + } + } +}