Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - add vue support #1267

Merged
merged 69 commits into from
Jul 2, 2017
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
c3b35e6
WIP
ndelangen Jun 12, 2017
fcd6283
WIP, webpack succesful
ndelangen Jun 12, 2017
7b53666
testing
ndelangen Jun 12, 2017
b1470eb
Implement basic working version, Cannot handle extension and global c…
alexandrebodin Jun 13, 2017
23aadf4
:shirt: refactor(client): update preview rendering
kazupon Jun 13, 2017
c23caa9
:shirt: refactor(examples): update stories
kazupon Jun 13, 2017
ccbbf6e
Publish alpha.0
ndelangen Jun 14, 2017
55aa3ff
Make global examples work if config.js bootstrap components and exten…
alexandrebodin Jun 14, 2017
e7b9b0a
Make working examples for functions, Vue extensions and Vue global co…
alexandrebodin Jun 14, 2017
6b21da8
fix dep for lerna bootstrap
alexandrebodin Jun 14, 2017
cc546c5
HMR works!
ndelangen Jun 14, 2017
d51f83e
CHANGE favicon
ndelangen Jun 14, 2017
9454fa7
ADD example with actions and links
ndelangen Jun 14, 2017
d0dc873
Update the dep value to work with next releases of @storybook/vue
alexandrebodin Jun 14, 2017
385611b
Merge branch 'master' into add-app-vue
ndelangen Jun 14, 2017
9974a3b
Add support for vue in addon-notes
alexandrebodin Jun 14, 2017
bf00a9a
Make addon notes agnostic
alexandrebodin Jun 14, 2017
514f4b7
deprecate old WithNotes but keep backward compatible
alexandrebodin Jun 14, 2017
5a6472d
Cleaning up deps and comments
alexandrebodin Jun 14, 2017
a93eba3
Merge pull request #1278 from storybooks/addon-notes-vue
ndelangen Jun 14, 2017
b41fda4
:construction: wip: add story template for vue app project
kazupon Jun 15, 2017
ff56861
:up: update(cli): getstorybook vue supporting
kazupon Jun 15, 2017
765d394
Merge branch 'master' into add-app-vue
ndelangen Jun 16, 2017
9bd55bc
:up: update(cli): stories creating for vue
kazupon Jun 16, 2017
82d44e8
Merge branch 'add-app-vue' into getstorybook-support-for-vue
kazupon Jun 16, 2017
19dcfd9
Change docs to contains Vue info
ndelangen Jun 16, 2017
db319e4
Merge branch 'master' into add-app-vue
ndelangen Jun 16, 2017
d9a3da5
Make a working example
alexandrebodin Jun 14, 2017
5e66224
Cleaning up
alexandrebodin Jun 14, 2017
aef1f01
less ugly working example
alexandrebodin Jun 15, 2017
5784a79
Comment unused to make PR clear
alexandrebodin Jun 15, 2017
bac1905
Add support for addonKnobs, rename withX to addonX to avoid name coll…
alexandrebodin Jun 15, 2017
6255569
Finish cleanup of addon knobs
alexandrebodin Jun 16, 2017
4c1daab
More clean up
alexandrebodin Jun 16, 2017
3e12ba1
:shirt: refactor: tweak style guide
kazupon Jun 17, 2017
b355060
Fix example using a function as a component to object component
alexandrebodin Jun 17, 2017
4dd6b78
Make example a proper vue app
alexandrebodin Jun 17, 2017
3260f7d
Readme fix
alexandrebodin Jun 17, 2017
1e91907
Wording
alexandrebodin Jun 17, 2017
838730f
:up: update: show the error at storybook/vue
kazupon Jun 17, 2017
2e3170d
:pencil: docs: update vue guide
kazupon Jun 17, 2017
040f4e0
Merge pull request #1287 from kazupon/getstorybook-support-for-vue
alexandrebodin Jun 18, 2017
68d237c
Remove unwatend packe-lock.json
alexandrebodin Jun 18, 2017
bc84ee6
Rename vue example to vue-kitchen-sink
alexandrebodin Jun 18, 2017
8145ac3
Merge branch 'master' into add-app-vue
alexandrebodin Jun 18, 2017
d3c53c1
:up: update: fix error display
kazupon Jun 18, 2017
70a043f
add more examples with knobs fields
alexandrebodin Jun 18, 2017
8178ec1
Merge branch 'add-app-vue' of github.com:storybooks/storybook into ad…
alexandrebodin Jun 18, 2017
043b5f1
Merge branch 'master' into add-app-vue
ndelangen Jun 19, 2017
5daa769
Merge branch 'master' into add-app-vue
alexandrebodin Jun 20, 2017
6c95ac9
add tests
alexandrebodin Jun 20, 2017
6e8f869
fix dependency
alexandrebodin Jun 20, 2017
c7efe5a
bumping version
alexandrebodin Jun 20, 2017
654be24
bumping version
alexandrebodin Jun 20, 2017
e35aeee
Merge branch 'add-app-vue' of github.com:storybooks/storybook into ad…
alexandrebodin Jun 20, 2017
7be65d4
fix resolve in webpack
alexandrebodin Jun 20, 2017
96031cd
Merge branch 'master' into add-app-vue
ndelangen Jun 23, 2017
d806c3f
WIP
ndelangen Jun 23, 2017
d41655a
Merge branch 'master' of github.com:storybooks/storybook into add-app…
alexandrebodin Jun 23, 2017
c7ddd09
clean up build and wording
alexandrebodin Jun 23, 2017
f2926f4
bump to alpha 4
alexandrebodin Jun 23, 2017
cafa395
FIX multiple version react in vue app
ndelangen Jun 23, 2017
0605107
CLEANUP
ndelangen Jun 23, 2017
997f3d9
FIX multiple versions of react & vue && CLEANUP
ndelangen Jun 23, 2017
ec67cc5
:up: update(server): tweak consoleoutputing
kazupon Jun 27, 2017
d2f259b
Merge branch 'add-app-vue' of github.com:storybooks/storybook into ad…
alexandrebodin Jul 1, 2017
929eb38
Merge branch 'master' of github.com:storybooks/storybook into add-app…
alexandrebodin Jul 1, 2017
864e97e
fix version in package.json
alexandrebodin Jul 1, 2017
83bb874
Merge branch 'release/3.2' into add-app-vue
shilman Jul 2, 2017
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
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ node_modules
app/**/demo/**
docs/public

vue

*.bundle.js
*.js.map

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ packages/examples/automated-*
yarn.lock
/**/LICENSE
docs/public
package-lock.json
7 changes: 4 additions & 3 deletions addons/knobs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -52,14 +52,15 @@ stories.add('with a button', () => (
</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 (<div>{content}</div>);
});
}));
```

You can see your Knobs in a Storybook panel as shown below.
Expand Down
6 changes: 4 additions & 2 deletions addons/knobs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-knobs",
"version": "3.1.2",
"version": "3.2.0-alpha.0",
"description": "Storybook Addon Prop Editor Component",
"license": "MIT",
"main": "dist/index.js",
Expand All @@ -26,9 +26,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",
Copy link
Member

Choose a reason for hiding this comment

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

does this mean that all react projects will load vue in development mode?

Copy link
Member

Choose a reason for hiding this comment

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

No, just that vue is included when developing the package (i.e. running the tests).

"@types/node": "^7.0.12",
"@types/react": "^15.0.21",
"git-url-parse": "^6.2.2",
Expand Down
25 changes: 3 additions & 22 deletions addons/knobs/src/KnobManager.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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 <WrapStory {...props} />;
}

_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
Expand Down
21 changes: 0 additions & 21 deletions addons/knobs/src/KnobManager.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { shallow } from 'enzyme'; // eslint-disable-line
import KnobManager from './KnobManager';

Expand Down Expand Up @@ -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 = () => <div id="test-story">Test Content</div>;
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);
});
});
});
7 changes: 7 additions & 0 deletions addons/knobs/src/components/Panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) {
Expand Down Expand Up @@ -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,
Expand Down
15 changes: 14 additions & 1 deletion addons/knobs/src/components/__tests__/Panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,17 @@ import Panel from '../Panel';
describe('Panel', () => {
it('should subscribe to setKnobs event of channel', () => {
const testChannel = { on: jest.fn() };
shallow(<Panel channel={testChannel} />);
const testApi = { onStory: jest.fn() };
shallow(<Panel channel={testChannel} api={testApi} />);
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(<Panel channel={testChannel} api={testApi} />);

expect(testApi.onStory).toHaveBeenCalled();
expect(testChannel.on).toHaveBeenCalledWith('addon:knobs:setKnobs', jasmine.any(Function));
});

Expand All @@ -28,6 +38,7 @@ describe('Panel', () => {
const testApi = {
getQueryParam: key => testQueryParams[key],
setQueryParams: jest.fn(),
onStory: jest.fn(),
};

shallow(<Panel channel={testChannel} api={testApi} />);
Expand Down Expand Up @@ -74,6 +85,7 @@ describe('Panel', () => {
const testApi = {
getQueryParam: key => testQueryParams[key],
setQueryParams: jest.fn(),
onStory: jest.fn(),
};

const wrapper = shallow(<Panel channel={testChannel} api={testApi} />);
Expand Down Expand Up @@ -115,6 +127,7 @@ describe('Panel', () => {
const testApi = {
getQueryParam: jest.fn(),
setQueryParams: jest.fn(),
onStory: jest.fn(),
};

const wrapper = shallow(<Panel channel={testChannel} api={testApi} />);
Expand Down
51 changes: 44 additions & 7 deletions addons/knobs/src/index.js
Original file line number Diff line number Diff line change
@@ -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);
Expand Down Expand Up @@ -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);
}
}
}
11 changes: 11 additions & 0 deletions addons/knobs/src/react/index.js
Original file line number Diff line number Diff line change
@@ -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 <WrapStory {...props} />;
};
27 changes: 27 additions & 0 deletions addons/knobs/src/react/index.test.js
Original file line number Diff line number Diff line change
@@ -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 = () => <div id="test-story">Test Content</div>;
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);
});
});
});
Copy link
Member

Choose a reason for hiding this comment

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

It's great to have some tests for knobs! Even better would be more tests that fully exercised it. I think we can do this later: #1355

37 changes: 37 additions & 0 deletions addons/knobs/src/vue/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export const vueHandler = (channel, knobStore) => getStory => context => ({
render(h) {
return h(getStory(context));
},

methods: {
onKnobChange(change) {
const { name, value } = change;
Copy link
Member

Choose a reason for hiding this comment

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

spacing??

// 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);
}
});
39 changes: 39 additions & 0 deletions addons/knobs/src/vue/index.test.js
Original file line number Diff line number Diff line change
@@ -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: '<div> testStory </div>' });
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));
});
});
Loading