diff --git a/.eslintignore b/.eslintignore index e2549766926e..5c6899227ef0 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,6 +6,8 @@ node_modules app/**/demo/** docs/public +vue + *.bundle.js *.js.map diff --git a/README.md b/README.md index 62fa53896c39..36f492830b07 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ It allows you to browse a component library, view the different states of each c Storybook runs outside of your app. This allows you to develop UI components in isolation, which can improve component reuse, testability, and development speed. You can build quickly without having to worry about application-specific dependencies. -Here are some featured examples that you can reference to see how Storybook works: https://storybook.js.org/examples/ +Here are some featured examples that you can reference to see how Storybook works: Storybook comes with a lot of [addons](https://storybook.js.org/addons/introduction/) for component design, documentation, testing, interactivity, and so on. Storybook's easy-to-use API makes it easy to configure and extend in various ways. It has even been extended to support React Native development for mobile. @@ -48,11 +48,13 @@ getstorybook Once it's installed, you can `npm run storybook` and it will run the development server on your local machine, and give you a URL to browse some sample stories. **Storybook v2.x migration note**: If you're using Storybook v2.x and want to shift to 3.x version the easiest way is: + ```sh npm i -g @storybook/cli cd my-storybook-v2-app getstorybook ``` + It runs a codemod to update all package names. Read all migration details in our [Migration Guide](MIGRATION.md) For full documentation on using Storybook visit: [storybook.js.org](https://storybook.js.org) diff --git a/ROADMAP.md b/ROADMAP.md index 225d6dc83e8b..ccd591e22396 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -10,7 +10,7 @@ - [See multiple (or all) stories in 1 preview.](#see-multiple-or-all-stories-in-1-preview) - [Deeper level hierarchy](#deeper-level-hierarchy) - [Supporting other frameworks and libraries](#supporting-other-frameworks-and-libraries) - - [Vue](#vue) (*in development*) + - [Vue](#vue) - [Angular](#angular) - [Webcomponents](#webcomponents) - [Polymer](#polymer) diff --git a/addons/knobs/README.md b/addons/knobs/README.md index f4d24e43eb00..9d5d53f8cd7c 100644 --- a/addons/knobs/README.md +++ b/addons/knobs/README.md @@ -37,7 +37,7 @@ Now, write your stories with knobs. ```js import { storiesOf } from '@storybook/react'; -import { withKnobs, text, boolean, number } from '@storybook/addon-knobs'; +import { addonKnobs, text, boolean, number } from '@storybook/addon-knobs'; const stories = storiesOf('Storybook Knobs', module); @@ -52,14 +52,15 @@ stories.add('with a button', () => ( )) +const options = {}; // Knobs as dynamic variables. -stories.add('as dynamic variables', () => { +stories.add('as dynamic variables', addonKnobs(options)(() => { const name = text('Name', 'Arunoda Susiripala'); const age = number('Age', 89); const content = `I am ${name} and I'm ${age} years old.`; return (
{content}
); -}); +})); ``` You can see your Knobs in a Storybook panel as shown below. diff --git a/addons/knobs/package.json b/addons/knobs/package.json index a310140f3fbb..9c153c2de80b 100644 --- a/addons/knobs/package.json +++ b/addons/knobs/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-knobs", - "version": "3.1.6", + "version": "3.2.0-alpha.0", "description": "Storybook Addon Prop Editor Component", "license": "MIT", "main": "dist/index.js", @@ -25,9 +25,11 @@ "prop-types": "^15.5.8", "react-color": "^2.11.4", "react-datetime": "^2.8.10", - "react-textarea-autosize": "^4.3.0" + "react-textarea-autosize": "^4.3.0", + "util-deprecate": "1.0.2" }, "devDependencies": { + "vue": "2.3.4", "@types/node": "^7.0.12", "@types/react": "^15.0.21", "git-url-parse": "^6.2.2", diff --git a/addons/knobs/src/KnobManager.js b/addons/knobs/src/KnobManager.js index b314b16c7166..73ae03687e07 100644 --- a/addons/knobs/src/KnobManager.js +++ b/addons/knobs/src/KnobManager.js @@ -1,17 +1,14 @@ /* eslint no-underscore-dangle: 0 */ - -import React from 'react'; import deepEqual from 'deep-equal'; -import WrapStory from './components/WrapStory'; import KnobStore from './KnobStore'; // This is used by _mayCallChannel to determine how long to wait to before triggering a panel update const PANEL_UPDATE_INTERVAL = 400; export default class KnobManager { - constructor() { - this.knobStore = null; - this.knobStoreMap = {}; + constructor(channel) { + this.channel = channel; + this.knobStore = new KnobStore(); } knob(name, options) { @@ -37,22 +34,6 @@ export default class KnobManager { return knobStore.get(name).value; } - wrapStory(channel, storyFn, context) { - this.channel = channel; - const key = `${context.kind}:::${context.story}`; - let knobStore = this.knobStoreMap[key]; - - if (!knobStore) { - knobStore = this.knobStoreMap[key] = new KnobStore(); // eslint-disable-line - } - - this.knobStore = knobStore; - knobStore.markAllUnused(); - const initialContent = storyFn(context); - const props = { context, storyFn, channel, knobStore, initialContent }; - return ; - } - _mayCallChannel() { // Re rendering of the story may cause changes to the knobStore. Some new knobs maybe added and // Some knobs may go unused. So we need to update the panel accordingly. For example remove the diff --git a/addons/knobs/src/KnobManager.test.js b/addons/knobs/src/KnobManager.test.js index 85337a73ad7f..5cf64a26a477 100644 --- a/addons/knobs/src/KnobManager.test.js +++ b/addons/knobs/src/KnobManager.test.js @@ -1,4 +1,3 @@ -import React from 'react'; import { shallow } from 'enzyme'; // eslint-disable-line import KnobManager from './KnobManager'; @@ -74,24 +73,4 @@ describe('KnobManager', () => { }); }); }); - - describe('wrapStory()', () => { - it('should contain the story and add correct props', () => { - const testManager = new KnobManager(); - - const testChannel = { emit: () => {} }; - const testStory = () =>
Test Content
; - const testContext = { - kind: 'Foo', - story: 'bar baz', - }; - const wrappedStory = testManager.wrapStory(testChannel, testStory, testContext); - const wrapper = shallow(wrappedStory); - expect(wrapper.find('#test-story').length).toBe(1); - - const storyWrapperProps = wrappedStory.props; - expect(storyWrapperProps.channel).toEqual(testChannel); - expect(storyWrapperProps.context).toEqual(testContext); - }); - }); }); diff --git a/addons/knobs/src/components/Panel.js b/addons/knobs/src/components/Panel.js index 5682dd30e523..f980644b646c 100644 --- a/addons/knobs/src/components/Panel.js +++ b/addons/knobs/src/components/Panel.js @@ -57,10 +57,16 @@ export default class Panel extends React.Component { this.loadedFromUrl = false; this.props.channel.on('addon:knobs:setKnobs', this.setKnobs); this.props.channel.on('addon:knobs:setOptions', this.setOptions); + + this.stopListeningOnStory = this.props.api.onStory(() => { + this.setState({ knobs: [] }); + this.props.channel.emit('addon:knobs:reset'); + }); } componentWillUnmount() { this.props.channel.removeListener('addon:knobs:setKnobs', this.setKnobs); + this.stopListeningOnStory(); } setOptions(options = { debounce: false, timestamps: false }) { @@ -155,6 +161,7 @@ Panel.propTypes = { }).isRequired, onReset: PropTypes.object, // eslint-disable-line api: PropTypes.shape({ + onStory: PropTypes.func, getQueryParam: PropTypes.func, setQueryParams: PropTypes.func, }).isRequired, diff --git a/addons/knobs/src/components/__tests__/Panel.js b/addons/knobs/src/components/__tests__/Panel.js index 05bf727945f7..6994937e810b 100644 --- a/addons/knobs/src/components/__tests__/Panel.js +++ b/addons/knobs/src/components/__tests__/Panel.js @@ -5,7 +5,17 @@ import Panel from '../Panel'; describe('Panel', () => { it('should subscribe to setKnobs event of channel', () => { const testChannel = { on: jest.fn() }; - shallow(); + const testApi = { onStory: jest.fn() }; + shallow(); + expect(testChannel.on).toHaveBeenCalledWith('addon:knobs:setKnobs', jasmine.any(Function)); + }); + + it('should subscribe to onStory event', () => { + const testChannel = { on: jest.fn() }; + const testApi = { onStory: jest.fn() }; + shallow(); + + expect(testApi.onStory).toHaveBeenCalled(); expect(testChannel.on).toHaveBeenCalledWith('addon:knobs:setKnobs', jasmine.any(Function)); }); @@ -28,6 +38,7 @@ describe('Panel', () => { const testApi = { getQueryParam: key => testQueryParams[key], setQueryParams: jest.fn(), + onStory: jest.fn(), }; shallow(); @@ -74,6 +85,7 @@ describe('Panel', () => { const testApi = { getQueryParam: key => testQueryParams[key], setQueryParams: jest.fn(), + onStory: jest.fn(), }; const wrapper = shallow(); @@ -115,6 +127,7 @@ describe('Panel', () => { const testApi = { getQueryParam: jest.fn(), setQueryParams: jest.fn(), + onStory: jest.fn(), }; const wrapper = shallow(); diff --git a/addons/knobs/src/index.js b/addons/knobs/src/index.js index 8e8239d244cd..a0a1448474a0 100644 --- a/addons/knobs/src/index.js +++ b/addons/knobs/src/index.js @@ -1,7 +1,12 @@ +import { window } from 'global'; +import deprecate from 'util-deprecate'; import addons from '@storybook/addons'; import KnobManager from './KnobManager'; +import { vueHandler } from './vue'; +import { reactHandler } from './react'; -const manager = new KnobManager(); +const channel = addons.getChannel(); +const manager = new KnobManager(channel); export function knob(name, options) { return manager.knob(name, options); @@ -55,16 +60,48 @@ export function date(name, value = new Date()) { return manager.knob(name, { type: 'date', value: proxyValue }); } -export function withKnobs(storyFn, context) { - const channel = addons.getChannel(); - return manager.wrapStory(channel, storyFn, context); +function oldKnobs(storyFn, context) { + return reactHandler(channel, manager.knobStore)(storyFn)(context); } -export function withKnobsOptions(options = {}) { +function oldKnobsWithOptions(options = {}) { return (...args) => { - const channel = addons.getChannel(); channel.emit('addon:knobs:setOptions', options); - return withKnobs(...args); + return oldKnobs(...args); }; } + +Object.defineProperty(exports, 'withKnobs', { + configurable: true, + enumerable: true, + get: deprecate( + () => oldKnobs, + '@storybook/addon-knobs withKnobs decorator is deprecated, use addonKnobs() instead. See https://github.com/storybooks/storybook/tree/master/addons/knobs' + ), +}); + +Object.defineProperty(exports, 'withKnobsOptions', { + configurable: true, + enumerable: true, + get: deprecate( + () => oldKnobsWithOptions, + '@storybook/addon-knobs withKnobsOptions decorator is deprecated, use addonKnobs() instead. See https://github.com/storybooks/storybook/tree/master/addons/knobs' + ), +}); + +export function addonKnobs(options) { + if (options) channel.emit('addon:knobs:setOptions', options); + + switch (window.STORYBOOK_ENV) { + case 'vue': { + return vueHandler(channel, manager.knobStore); + } + case 'react': { + return reactHandler(channel, manager.knobStore); + } + default: { + return reactHandler(channel, manager.knobStore); + } + } +} diff --git a/addons/knobs/src/components/WrapStory.js b/addons/knobs/src/react/WrapStory.js similarity index 100% rename from addons/knobs/src/components/WrapStory.js rename to addons/knobs/src/react/WrapStory.js diff --git a/addons/knobs/src/react/index.js b/addons/knobs/src/react/index.js new file mode 100644 index 000000000000..0b093d446ddc --- /dev/null +++ b/addons/knobs/src/react/index.js @@ -0,0 +1,11 @@ +import React from 'react'; +import WrapStory from './WrapStory'; + +/** + * Handles a react story + */ +export const reactHandler = (channel, knobStore) => getStory => context => { + const initialContent = getStory(context); + const props = { context, storyFn: getStory, channel, knobStore, initialContent }; + return ; +}; diff --git a/addons/knobs/src/react/index.test.js b/addons/knobs/src/react/index.test.js new file mode 100644 index 000000000000..298cf1379d79 --- /dev/null +++ b/addons/knobs/src/react/index.test.js @@ -0,0 +1,27 @@ +import React from 'react'; +import { reactHandler } from './index'; +import { shallow } from 'enzyme'; // eslint-disable-line +import KnobStore from '../KnobStore'; + +describe('React Handler', () => { + describe('wrapStory', () => { + it('should contain the story and add correct props', () => { + const testChannel = { emit: () => {} }; + const testStory = () =>
Test Content
; + const testContext = { + kind: 'Foo', + story: 'bar baz', + }; + + const testStore = new KnobStore(); + + const wrappedStory = reactHandler(testChannel, testStore)(testStory)(testContext); + const wrapper = shallow(wrappedStory); + expect(wrapper.find('#test-story').length).toBe(1); + + const storyWrapperProps = wrappedStory.props; + expect(storyWrapperProps.channel).toEqual(testChannel); + expect(storyWrapperProps.context).toEqual(testContext); + }); + }); +}); diff --git a/addons/knobs/src/vue/index.js b/addons/knobs/src/vue/index.js new file mode 100644 index 000000000000..1c76ff5fcb72 --- /dev/null +++ b/addons/knobs/src/vue/index.js @@ -0,0 +1,37 @@ +export const vueHandler = (channel, knobStore) => getStory => context => ({ + render(h) { + return h(getStory(context)); + }, + + methods: { + onKnobChange(change) { + const { name, value } = change; + // Update the related knob and it's value. + const knobOptions = knobStore.get(name); + knobOptions.value = value; + this.$forceUpdate(); + }, + + onKnobReset() { + knobStore.reset(); + this.setPaneKnobs(false); + this.$forceUpdate(); + }, + + setPaneKnobs(timestamp = +new Date()) { + channel.emit('addon:knobs:setKnobs', { knobs: knobStore.getAll(), timestamp }); + }, + }, + + created() { + channel.on('addon:knobs:reset', this.onKnobReset); + channel.on('addon:knobs:knobChange', this.onKnobChange); + knobStore.subscribe(this.setPaneKnobs); + }, + + beforeDestroy(){ + channel.removeListener('addon:knobs:reset', this.onKnobReset); + channel.removeListener('addon:knobs:knobChange', this.onKnobChange); + knobStore.unsubscribe(this.setPaneKnobs); + } +}); \ No newline at end of file diff --git a/addons/knobs/src/vue/index.test.js b/addons/knobs/src/vue/index.test.js new file mode 100644 index 000000000000..a43e1f4b9a76 --- /dev/null +++ b/addons/knobs/src/vue/index.test.js @@ -0,0 +1,39 @@ +import Vue from 'vue'; +import { vueHandler } from './index'; +import KnobStore from '../KnobStore'; + +describe('Vue handler', () => { + it('Returns a component with a created function', () => { + const testChannel = { emit: () => {} }; + const testStory = () => ({ template: '
testStory
' }); + const testContext = { + kind: 'Foo', + story: 'bar baz', + }; + + const testStore = new KnobStore(); + const component = vueHandler(testChannel, testStore)(testStory)(testContext); + + expect(component).toMatchObject({ + created: expect.any(Function), + beforeDestroy: expect.any(Function), + render: expect.any(Function), + }); + }); + + it('Subscribes to the channel on creation', () => { + const testChannel = { emit: () => {}, on: jest.fn() }; + const testStory = () => ({ render: (h) => h('div', ['testStory']) }); + const testContext = { + kind: 'Foo', + story: 'bar baz', + }; + + const testStore = new KnobStore(); + const component = new Vue(vueHandler(testChannel, testStore)(testStory)(testContext)).$mount(); + + expect(testChannel.on).toHaveBeenCalledTimes(2); + expect(testChannel.on).toHaveBeenCalledWith('addon:knobs:reset', expect.any(Function)); + expect(testChannel.on).toHaveBeenCalledWith('addon:knobs:knobChange', expect.any(Function)); + }); +}); diff --git a/addons/notes/README.md b/addons/notes/README.md index 94826bc20f08..fb8d896c28f0 100644 --- a/addons/notes/README.md +++ b/addons/notes/README.md @@ -31,17 +31,11 @@ import '@storybook/addon-notes/register'; Then write your stories like this: ```js -import React from 'react'; - import { storiesOf } from '@storybook/react'; -import { WithNotes } from '@storybook/addon-notes'; +import { addonNotes } from '@storybook/addon-notes'; import Component from './Component'; storiesOf('Component', module) - .add('with some emoji', () => ( - - - - )); + .add('with some emoji', addonNotes({ notes: 'A very simple component'})(() => )); ``` diff --git a/addons/notes/package.json b/addons/notes/package.json index 35f3c0d47d4b..cce0fe1e2535 100644 --- a/addons/notes/package.json +++ b/addons/notes/package.json @@ -1,11 +1,11 @@ { "name": "@storybook/addon-notes", - "version": "3.1.6", + "version": "3.2.0-alpha.0", "description": "Write notes for your Storybook stories.", "keywords": [ "addon", - "react", - "storybook" + "storybook", + "notes" ], "license": "MIT", "main": "dist/index.js", @@ -21,13 +21,11 @@ "dependencies": { "@storybook/addons": "^3.1.6", "babel-runtime": "^6.23.0", - "prop-types": "^15.5.10" + "util-deprecate": "^1.0.2" }, "devDependencies": { - "git-url-parse": "^6.2.2", - "react": "^15.5.4", - "react-addons-test-utils": "^15.5.1", - "react-dom": "^15.5.4" + "prop-types": "^15.5.10", + "react": "^15.5.4" }, "peerDependencies": { "react": "*" diff --git a/addons/notes/src/index.js b/addons/notes/src/index.js index 818e3ffb39f3..f825eac410d6 100644 --- a/addons/notes/src/index.js +++ b/addons/notes/src/index.js @@ -1,24 +1,22 @@ -import React from 'react'; -import PropTypes from 'prop-types'; +import deprecate from 'util-deprecate'; import addons from '@storybook/addons'; +import { WithNotes as ReactWithNotes } from './react'; -export class WithNotes extends React.Component { - render() { - const { children, notes } = this.props; - const channel = addons.getChannel(); +export const addonNotes = ({ notes }) => { + const channel = addons.getChannel(); - // send the notes to the channel. + return getStory => () => { + // send the notes to the channel before the story is rendered channel.emit('storybook/notes/add_notes', notes); - // return children elements. - return children; - } -} - -WithNotes.propTypes = { - children: PropTypes.node, - notes: PropTypes.string, -}; -WithNotes.defaultProps = { - children: null, - notes: '', + return getStory(); + }; }; + +Object.defineProperty(exports, 'WithNotes', { + configurable: true, + enumerable: true, + get: deprecate( + () => ReactWithNotes, + '@storybook/addon-notes WithNotes Component is deprecated, use withNotes() instead. See https://github.com/storybooks/storybook/tree/master/addons/notes' + ), +}); diff --git a/addons/notes/src/react.js b/addons/notes/src/react.js new file mode 100644 index 000000000000..818e3ffb39f3 --- /dev/null +++ b/addons/notes/src/react.js @@ -0,0 +1,24 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import addons from '@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('storybook/notes/add_notes', notes); + // return children elements. + return children; + } +} + +WithNotes.propTypes = { + children: PropTypes.node, + notes: PropTypes.string, +}; +WithNotes.defaultProps = { + children: null, + notes: '', +}; diff --git a/app/react/src/server/config/globals.js b/app/react/src/server/config/globals.js index ee4d9597bf2a..e95a663712b5 100644 --- a/app/react/src/server/config/globals.js +++ b/app/react/src/server/config/globals.js @@ -1,3 +1,4 @@ /* globals window */ window.STORYBOOK_REACT_CLASSES = {}; +window.STORYBOOK_ENV = 'react'; diff --git a/app/vue/.babelrc b/app/vue/.babelrc new file mode 100644 index 000000000000..845c3cf4d757 --- /dev/null +++ b/app/vue/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["env", "stage-0", "react"] +} diff --git a/app/vue/.npmignore b/app/vue/.npmignore new file mode 100644 index 000000000000..329fc8d67ad9 --- /dev/null +++ b/app/vue/.npmignore @@ -0,0 +1,3 @@ +docs +src +.babelrc diff --git a/app/vue/README.md b/app/vue/README.md new file mode 100644 index 000000000000..f4580184c646 --- /dev/null +++ b/app/vue/README.md @@ -0,0 +1,17 @@ +# Storybook for Vue + +[![Greenkeeper badge](https://badges.greenkeeper.io/storybooks/storybook.svg)](https://greenkeeper.io/) +[![Build Status](https://travis-ci.org/storybooks/storybook.svg?branch=master)](https://travis-ci.org/storybooks/storybook) +[![CodeFactor](https://www.codefactor.io/repository/github/storybooks/storybook/badge)](https://www.codefactor.io/repository/github/storybooks/storybook) +[![Known Vulnerabilities](https://snyk.io/test/github/storybooks/storybook/8f36abfd6697e58cd76df3526b52e4b9dc894847/badge.svg)](https://snyk.io/test/github/storybooks/storybook/8f36abfd6697e58cd76df3526b52e4b9dc894847) +[![BCH compliance](https://bettercodehub.com/edge/badge/storybooks/storybook)](https://bettercodehub.com/results/storybooks/storybook) [![codecov](https://codecov.io/gh/storybooks/storybook/branch/master/graph/badge.svg)](https://codecov.io/gh/storybooks/storybook) +[![Storybook Slack](https://storybooks-slackin.herokuapp.com/badge.svg)](https://storybooks-slackin.herokuapp.com/) + +Storybook for Vue is a UI development environment for your Vue components. +With it, you can visualize different states of your UI components and develop them interactively. + +## Notes + +- When using global custom components or extension (e.g `Vue.use`). You will need to declare those in the `./storybook/config.js`. + +♥️ diff --git a/app/vue/addons.js b/app/vue/addons.js new file mode 100644 index 000000000000..1c94a19459e0 --- /dev/null +++ b/app/vue/addons.js @@ -0,0 +1,8 @@ +// import deprecate from 'util-deprecate'; +// import '@storybook/addon-actions/register'; +// import '@storybook/addon-links/register'; + +// deprecate( +// () => {}, +// '@storybook/react/addons is deprecated. See https://storybook.js.org/addons/using-addons/' +// )(); diff --git a/app/vue/docs/demo.gif b/app/vue/docs/demo.gif new file mode 100644 index 000000000000..c8366f8534a6 Binary files /dev/null and b/app/vue/docs/demo.gif differ diff --git a/app/vue/docs/react_storybook_screenshot.png b/app/vue/docs/react_storybook_screenshot.png new file mode 100644 index 000000000000..9763382042b2 Binary files /dev/null and b/app/vue/docs/react_storybook_screenshot.png differ diff --git a/app/vue/docs/storybooks_io_logo.png b/app/vue/docs/storybooks_io_logo.png new file mode 100644 index 000000000000..3dd9b09f3a95 Binary files /dev/null and b/app/vue/docs/storybooks_io_logo.png differ diff --git a/app/vue/package.json b/app/vue/package.json new file mode 100644 index 000000000000..a35b5e23d4bd --- /dev/null +++ b/app/vue/package.json @@ -0,0 +1,83 @@ +{ + "name": "@storybook/vue", + "version": "3.0.0-alpha.4", + "description": "Storybook for Vue: Develop Vue Component in isolation with Hot Reloading.", + "homepage": "https://github.com/storybooks/storybook/tree/master/apps/vue", + "bugs": { + "url": "https://github.com/storybooks/storybook/issues" + }, + "license": "MIT", + "main": "dist/client/index.js", + "bin": { + "build-storybook": "./dist/server/build.js", + "start-storybook": "./dist/server/index.js", + "storybook-server": "./dist/server/index.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/storybooks/storybook.git" + }, + "scripts": { + "dev": "DEV_BUILD=1 nodemon --watch ./src --exec 'npm run prepublish'", + "prepublish": "node ../../scripts/prepublish.js" + }, + "dependencies": { + "@storybook/addon-actions": "^3.1.2", + "@storybook/addon-links": "^3.1.2", + "@storybook/addons": "^3.1.2", + "@storybook/channel-postmessage": "^3.1.2", + "@storybook/ui": "^3.1.3", + "airbnb-js-shims": "^1.1.1", + "autoprefixer": "^7.1.1", + "babel-core": "^6.24.1", + "babel-loader": "^7.0.0", + "babel-plugin-react-docgen": "^1.5.0", + "babel-preset-env": "1.5.2", + "babel-preset-react": "^6.24.1", + "babel-preset-react-app": "^3.0.0", + "babel-preset-stage-0": "^6.24.1", + "babel-runtime": "^6.23.0", + "case-sensitive-paths-webpack-plugin": "^2.0.0", + "chalk": "^1.1.3", + "commander": "^2.9.0", + "common-tags": "^1.4.0", + "configstore": "^3.1.0", + "css-loader": "^0.28.1", + "express": "^4.15.3", + "file-loader": "^0.11.1", + "find-cache-dir": "^1.0.0", + "global": "^4.3.2", + "json-loader": "^0.5.4", + "json-stringify-safe": "^5.0.1", + "json5": "^0.5.1", + "lodash.pick": "^4.4.0", + "postcss-flexbugs-fixes": "^3.0.0", + "postcss-loader": "^2.0.5", + "prop-types": "^15.5.10", + "qs": "^6.4.0", + "react": "^15.5.4", + "react-dom": "^15.5.4", + "react-modal": "^1.7.7", + "redux": "^3.6.0", + "request": "^2.81.0", + "serve-favicon": "^2.4.3", + "shelljs": "^0.7.7", + "style-loader": "^0.17.0", + "url-loader": "^0.5.8", + "util-deprecate": "^1.0.2", + "uuid": "^3.0.1", + "vue": "2.3.4", + "vue-hot-reload-api": "2.1.0", + "vue-loader": "^12.2.1", + "vue-style-loader": "3.0.1", + "vue-template-compiler": "^2.3.4", + "webpack": "^2.5.1", + "webpack-dev-middleware": "^1.10.2", + "webpack-hot-middleware": "^2.18.0" + }, + "devDependencies": { + "babel-cli": "^6.24.1", + "mock-fs": "^4.3.0", + "nodemon": "^1.11.0" + } +} diff --git a/app/vue/src/client/index.js b/app/vue/src/client/index.js new file mode 100644 index 000000000000..f9a446eea867 --- /dev/null +++ b/app/vue/src/client/index.js @@ -0,0 +1,23 @@ +import deprecate from 'util-deprecate'; + +// NOTE export these to keep backwards compatibility +// import { action as deprecatedAction } from '@storybook/addon-actions'; +// import { linkTo as deprecatedLinkTo } from '@storybook/addon-links'; + +import * as previewApi from './preview'; + +export const storiesOf = previewApi.storiesOf; +export const setAddon = previewApi.setAddon; +export const addDecorator = previewApi.addDecorator; +export const configure = previewApi.configure; +export const getStorybook = previewApi.getStorybook; + +// export const action = deprecate( +// deprecatedAction, +// '@storybook/react action is deprecated. See: https://github.com/storybooks/storybook/tree/master/addon/actions' +// ); + +// export const linkTo = deprecate( +// deprecatedLinkTo, +// '@storybook/react linkTo is deprecated. See: https://github.com/storybooks/storybook/tree/master/addon/links' +// ); diff --git a/app/vue/src/client/manager/index.js b/app/vue/src/client/manager/index.js new file mode 100644 index 000000000000..24082de7ca54 --- /dev/null +++ b/app/vue/src/client/manager/index.js @@ -0,0 +1,7 @@ +/* global document */ + +import renderStorybookUI from '@storybook/ui'; +import Provider from './provider'; + +const rootEl = document.getElementById('root'); +renderStorybookUI(rootEl, new Provider()); diff --git a/app/vue/src/client/manager/preview.js b/app/vue/src/client/manager/preview.js new file mode 100644 index 000000000000..4f8e792d9641 --- /dev/null +++ b/app/vue/src/client/manager/preview.js @@ -0,0 +1,39 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; + +const iframeStyle = { + width: '100%', + height: '100%', + border: 0, + margin: 0, + padding: 0, +}; + +class Preview extends Component { + shouldComponentUpdate() { + // When the manager is re-rendered, due to changes in the layout (going full screen / changing + // addon panel to right) Preview section will update. If its re-rendered the whole html page + // inside the html is re-rendered making the story to re-mount. + // We dont have to re-render this component for any reason since changes are communicated to + // story using the channel and necessary changes are done by it. + return false; + } + + render() { + return ( +