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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Addons composition #1473

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 6 additions & 0 deletions addons/notes/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export const addonNotes = ({ notes }) => {
};
};

export const withNotes = (storyFn, context, notes) => {
const channel = addons.getChannel();
channel.emit('storybook/notes/add_notes', notes);
return storyFn(context);
};

Object.defineProperty(exports, 'WithNotes', {
configurable: true,
enumerable: true,
Expand Down
47 changes: 47 additions & 0 deletions examples/cra-kitchen-sink/.storybook/addon-composition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';

export default {
getAddons(...addons) {
this._add = this.add;

this.add = (name, storyFn) => {

const apiFn = (context) => {
let writtingStory = null;

const apiContext = {
...context,
cleanStory: null,
};

apiContext.storyOf = (component) => {
if (typeof component === 'function') {
writtingStory = component(apiContext.cleanStory);
} else {
writtingStory = component;
}
apiContext.cleanStory = writtingStory;
return apiContext;
};

addons.forEach((addonFn, ind) => {
if (typeof addonFn === 'function') {
const name = addonFn.name || `with${ind}`;
apiContext[name] = (...props) => {
writtingStory = addonFn(() => writtingStory, context, ...props);
return apiContext;
}
}
});

const result = storyFn(apiContext);
if (result === apiContext) {
return writtingStory;
} else {
return result;
}
};
return this._add(name, apiFn)
}
}
};
2 changes: 2 additions & 0 deletions examples/cra-kitchen-sink/.storybook/config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { configure, setAddon } from '@storybook/react';
import { setOptions } from '@storybook/addon-options';
import infoAddon from '@storybook/addon-info';
import getAddons from './addon-composition';

setOptions({
name: 'CRA Kitchen Sink',
Expand All @@ -15,6 +16,7 @@ setOptions({
});

setAddon(infoAddon);
setAddon(getAddons);

function loadStories() {
require('../src/stories');
Expand Down
82 changes: 19 additions & 63 deletions examples/cra-kitchen-sink/src/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import EventEmiter from 'eventemitter3';

import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { addonNotes, WithNotes } from '@storybook/addon-notes';
import { addonNotes, WithNotes, withNotes } from '@storybook/addon-notes';
import { linkTo } from '@storybook/addon-links';
import WithEvents from '@storybook/addon-events';
import {
Expand All @@ -19,7 +19,7 @@ import {
object,
} from '@storybook/addon-knobs';
import centered from '@storybook/addon-centered';
import { withInfo } from '@storybook/addon-info';
import { withInfo, addInfo } from '@storybook/addon-info';

import { Button, Welcome } from '@storybook/react/demo';

Expand Down Expand Up @@ -54,75 +54,31 @@ const InfoButton = () =>
{' '}Show Info{' '}
</span>;

const withBorder = (storyFn, context, bcolor) =>
<div style={{ padding: 6, border: `${bcolor} solid 2px` }}>
{storyFn()}
</div>;

storiesOf('Button', module)
.addDecorator(withKnobs)
.getAddons(addInfo, withNotes, withBorder)
.add('with addons', context =>
context
Copy link
Member

Choose a reason for hiding this comment

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

This is not really an api I'm really enthusiastic about to be honest.

I don't like it because the interface is modified by addons. I fear that this is too flexible, and can lead us to all sorts of problems.

.storyOf(
<Button>
Context is a key. Do you want to know more about "{context.kind}/{context.story}"?
</Button>
)
.withNotes('Addons composition')
.withBorder('red')
.addInfo('Addons composition', { inline: false })
)
.add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
.add('with some emoji', () => <Button onClick={action('clicked')}>馃榾 馃槑 馃憤 馃挴</Button>)
.add('with notes', () =>
<WithNotes notes={'A very simple button'}>
<Button>Check my notes in the notes panel</Button>
</WithNotes>
)
.add('with knobs', () => {
const name = text('Name', 'Storyteller');
const age = number('Age', 70, { range: true, min: 0, max: 90, step: 5 });
const fruits = {
apple: 'Apple',
banana: 'Banana',
cherry: 'Cherry',
};
const fruit = select('Fruit', fruits, 'apple');
const dollars = number('Dollars', 12.5);

// NOTE: color picker is currently broken
const backgroundColor = color('background', '#ffff00');
const items = array('Items', ['Laptop', 'Book', 'Whiskey']);
const otherStyles = object('Styles', {
border: '3px solid #ff00ff',
padding: '10px',
});
const nice = boolean('Nice', true);

// NOTE: put this last because it currently breaks everything after it :D
const birthday = date('Birthday', new Date('Jan 20 2017'));

const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}.`;
const style = { backgroundColor, ...otherStyles };
const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!';

return (
<div style={style}>
<p>
{intro}
</p>
<p>
My birthday is: {new Date(birthday).toLocaleDateString()}
</p>
<p>
My wallet contains: ${dollars.toFixed(2)}
</p>
<p>In my backpack, I have:</p>
<ul>
{items.map(item =>
<li key={item}>
{item}
</li>
)}
</ul>
<p>
{salutation}
</p>
</div>
);
})
.addWithInfo(
'with some info',
'Use the [info addon](https://github.com/storybooks/storybook/tree/master/addons/info) with its painful API.',
context =>
<div>
click the <InfoButton /> label in top right for info about "{context.story}"
</div>
)
.add(
'with new info',
withInfo(
Expand Down