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

Refactoring of withX wrappers withNotes and withInfo #1538

Merged
merged 4 commits into from
Jul 31, 2017
Merged
Show file tree
Hide file tree
Changes from 2 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
15 changes: 7 additions & 8 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
- [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 alpha*)
- [Angular](#angular) (*in development*)
- [Vue](#vue)
- [Angular](#angular)
- [Webcomponents](#webcomponents)
- [Polymer](#polymer)
- [Aurelia](#aurelia)
Expand All @@ -20,7 +20,7 @@
- [API for adding stories](#api-for-adding-stories)
- [Documentation](#documentation)
- [Better design](#better-design)
- [Record videos and write blog post on how to tweak storybook](#record-videos-and-write-blog-post-on-how-to-tweak-storybook)
- [Record videos and write blog post on how to use, tweak & develop storybook](#record-videos-and-write-blog-post-on-how-to-use-tweak--develop-storybook)

## New features

Expand Down Expand Up @@ -110,8 +110,7 @@ We have a new logo, so next step is a overhaul of our documentation site.

### Record videos and write blog post on how to use, tweak & develop storybook

- writing addons,
- choosing the right addons.
- how to start developing on our codebase.
- how to use storybook itself and the CLI.

- writing addons,
- choosing the right addons.
- how to start developing on our codebase.
- how to use storybook itself and the CLI.
80 changes: 61 additions & 19 deletions addons/info/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,74 @@ This addon works with Storybook for:

![Screenshot](docs/home-screenshot.png)

## Usage
## Installation

Install the following npm module:

```sh
npm i -D @storybook/addon-info
```

Then set the addon in the place you configure storybook like this:
## Basic usage

Then wrap your story with the `withInfo`, which is a function that takes either
documentation text or an options object:

```js
import { configure, setAddon } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';

storiesOf('Component', module)
.add('simple info',
withInfo('doc string about my component')(() =>
<Component>Click the "?" mark at top-right to view the info.</Component>
)
)
```

## Usage with options

`withInfo` can also take an options object in case you want to configure how
the info panel looks on a per-story basis:

```js
import { configure, setAddon } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';

storiesOf('Component', module)
.add('simple info',
withInfo({
text: 'doc string about my component',
maxPropsIntoLine: 1,
maxPropObjectKeys: 10,
maxPropArrayLength: 10,
)(() =>
<Component>Click the "?" mark at top-right to view the info.</Component>
)
)
```

## Global options

To configure default options for all usage of the info option, use `setDefaults` in `.storybook/config.js`:

```js
// config.js
import { setDefaults } from '@storybook/addon-info';

// addon-info
setDefaults({
inline: true,
maxPropsIntoLine: 1,
maxPropObjectKeys: 10,
maxPropArrayLength: 10,
maxPropStringLength: 100,
});
```

## Deprecated usage

There is also a deprecated API that is slated for removal in Storybook 4.0.

```js
import { configure, setAddon } from '@storybook/react';
Expand Down Expand Up @@ -55,23 +114,6 @@ storiesOf('Component')

> Have a look at [this example](example/story.js) stories to learn more about the `addWithInfo` API.

To customize your defaults:

```js
// config.js
import infoAddon, { setDefaults } from '@storybook/addon-info';

// addon-info
setDefaults({
inline: true,
maxPropsIntoLine: 1,
maxPropObjectKeys: 10,
maxPropArrayLength: 10,
maxPropStringLength: 100,
});
setAddon(infoAddon);
```

## The FAQ

**Components lose their names on static build**
Expand Down
45 changes: 21 additions & 24 deletions addons/info/src/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import React from 'react';
import deprecate from 'util-deprecate';
import _Story from './components/Story';
import Story from './components/Story';
import { H1, H2, H3, H4, H5, H6, Code, P, UL, A, LI } from './components/markdown';

function addonCompose(addonFn) {
return storyFn => context => addonFn(storyFn, context);
}

export const Story = _Story;

const defaultOptions = {
inline: false,
header: true,
Expand All @@ -34,20 +28,10 @@ const defaultMarksyConf = {
ul: UL,
};

export function addInfo(storyFn, context, info, _options) {
Copy link
Member

Choose a reason for hiding this comment

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

Is there any reason to not export this function to users? Maybe our documentation shouldn't encourage people to use it, but there could be some "advanced" use cases when this form is more convenient! Especially it makes sense after #1501

Copy link
Member Author

Choose a reason for hiding this comment

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

IMHO, we should never export a function to users if we don't need to, because then we are stuck supporting it unless we want to break the API.

if (typeof storyFn !== 'function') {
if (typeof info === 'function') {
_options = storyFn; // eslint-disable-line
storyFn = info; // eslint-disable-line
info = ''; // eslint-disable-line
} else {
throw new Error('No story defining function has been specified');
}
}

function addInfo(storyFn, context, infoOptions) {
const options = {
...defaultOptions,
..._options,
...infoOptions,
Copy link
Member

Choose a reason for hiding this comment

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

Also merge in:

if (!options.propTables) {
  options.propTables = null;
}

Copy link
Member Author

Choose a reason for hiding this comment

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

how would you merge this in? see the comment above in the code.

};

// props.propTables can only be either an array of components or null
Expand All @@ -62,7 +46,7 @@ export function addInfo(storyFn, context, info, _options) {
Object.assign(marksyConf, options.marksyConf);
}
const props = {
info,
info: options.text,
context,
showInline: Boolean(options.inline),
showHeader: Boolean(options.header),
Expand All @@ -83,12 +67,25 @@ export function addInfo(storyFn, context, info, _options) {
);
}

export const withInfo = (info, _options) =>
addonCompose((storyFn, context) => addInfo(storyFn, context, info, _options));
export const withInfo = textOrOptions => {
Copy link
Member

Choose a reason for hiding this comment

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

IMO, it would be nice to provide here such an option as well:

withInfo('text info', { options })

There are two reasons for that:

1.1. We might want to use different text info with the same other options:

.add('Button', withInfo('Default Button', widgetInfoOptions)(() => <Button />))

1.2. We might want to use multiline template string for `text info` and such syntax would be less verbose

  1. It's relevant to the previous behavior of .addWithInfo when we could pass (or not) text info as a separate argument

Copy link
Member Author

Choose a reason for hiding this comment

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

@usulpro the reason @tmeasday wanted to keep it to a single argument is for this API:

https://gist.github.com/shilman/792dc25550daa9c2bf37238f4ef7a398#options-types

The API described in the gist is obviously just a proposal, but we think having a single argument makes stuff like this simple to implement. If we have two arguments it gets complicated fast.

const options = typeof textOrOptions === 'string' ? { text: textOrOptions } : textOrOptions;
return storyFn => context => addInfo(storyFn, context, options);
};

export { Story };

export default {
addWithInfo: deprecate(function addWithInfo(storyName, info, storyFn, _options) {
return this.add(storyName, withInfo(info, _options)(storyFn));
addWithInfo: deprecate(function addWithInfo(storyName, text, storyFn, options) {
if (typeof storyFn !== 'function') {
Copy link
Member

Choose a reason for hiding this comment

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

Could use a bit of refactoring,
in some cases the const text is actually the storyFn. I find this confusing.

I'd prefer using abstract names or simply an argument array to do detection, and only assign to a const when we are sure what it is.

if (typeof text === 'function') {
options = storyFn; // eslint-disable-line
storyFn = text; // eslint-disable-line
text = ''; // eslint-disable-line
} else {
throw new Error('No story defining function has been specified');
}
}
return this.add(storyName, withInfo({ text, ...options })(storyFn));
}, '@storybook/addon-info .addWithInfo() addon is deprecated, use withInfo() from the same package instead. \nSee https://github.com/storybooks/storybook/tree/master/addons/info'),
};

Expand Down
9 changes: 7 additions & 2 deletions addons/info/src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React from 'react';
import ReactDOM from 'react-dom';
import AddonInfo, { withInfo, setDefaults, addInfo } from './';
import AddonInfo, { withInfo, setDefaults } from './';

/* eslint-disable */
const TestComponent = ({ func, obj, array, number, string, bool, empty }) =>
Expand Down Expand Up @@ -48,9 +48,14 @@ describe('addon Info', () => {
)(story);
ReactDOM.render(<Info />, document.createElement('div'));
});
it('should render with text options', () => {
const Info = withInfo({ text: 'some text here' })(story);
ReactDOM.render(<Info />, document.createElement('div'));
});
it('should render with missed info', () => {
setDefaults(testOptions);
addInfo(null, testContext, story, testOptions);
const Info = withInfo()(story);
ReactDOM.render(<Info />, document.createElement('div'));
});
it('should show deprecation warning', () => {
const addWithInfo = AddonInfo.addWithInfo.bind(api);
Expand Down
2 changes: 1 addition & 1 deletion addons/notes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ import { withNotes } from '@storybook/addon-notes';
import Component from './Component';

storiesOf('Component', module)
.add('with some emoji', withNotes({ notes: 'A very simple component'})(() => <Component></Component>));
.add('with some emoji', withNotes('A very simple component')(() => <Component></Component>));
```
5 changes: 3 additions & 2 deletions addons/notes/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import deprecate from 'util-deprecate';
import addons from '@storybook/addons';
import { WithNotes as ReactWithNotes } from './react';

export const withNotes = ({ notes }) => {
export const withNotes = textOrOptions => {
const channel = addons.getChannel();
const options = typeof textOrOptions === 'string' ? { text: textOrOptions } : textOrOptions;

return getStory => context => {
// send the notes to the channel before the story is rendered
channel.emit('storybook/notes/add_notes', notes);
channel.emit('storybook/notes/add_notes', options.text);
Copy link
Member

Choose a reason for hiding this comment

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

If we mean that this addon could have other options in the future (or even if we want to keep it as an addon example) why don't pass all 'options' object here?

Copy link
Member

Choose a reason for hiding this comment

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

I think we can emit those options once we handle them on the other end.

There may be "preview-side" options, so I don't think it's necessarily the case that we'd want to emit them all.

return getStory(context);
};
};
Expand Down
4 changes: 1 addition & 3 deletions app/vue/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ For more information visit: [storybook.js.org](https://storybook.js.org)
Storybook also comes with a lot of [addons](https://storybook.js.org/addons/introduction) and a great API to customize as you wish.
You can also build a [static version](https://storybook.js.org/basics/exporting-storybook) of your storybook and deploy it anywhere you want.


## Vue Notes

- When using global custom components or extension (e.g `Vue.use`). You will need to declare those in the `./storybook/config.js`.

- When using global custom components or extension (e.g `Vue.use`). You will need to declare those in the `./storybook/config.js`.
8 changes: 4 additions & 4 deletions examples/cra-kitchen-sink/src/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ storiesOf('Button', module)
.add(
'addons composition',
withInfo('see Notes panel for composition info')(
withNotes({ notes: 'Composition: Info(Notes())' })(context =>
withNotes('Composition: Info(Notes())')(context =>
<div>
click the <InfoButton /> label in top right for info about "{context.story}"
</div>
Expand Down Expand Up @@ -208,11 +208,11 @@ storiesOf('WithEvents', module)
.add('Logger', () => <Logger emiter={emiter} />);

storiesOf('withNotes', module)
.add('with some text', withNotes({ notes: 'Hello guys' })(() => <div>Hello guys</div>))
.add('with some emoji', withNotes({ notes: 'My notes on emojies' })(() => <p>🤔😳😯😮</p>))
.add('with some text', withNotes('Hello guys')(() => <div>Hello guys</div>))
.add('with some emoji', withNotes('My notes on emojies')(() => <p>🤔😳😯😮</p>))
.add(
'with a button and some emoji',
withNotes({ notes: 'My notes on a button with emojies' })(() =>
withNotes('My notes on a button with emojies')(() =>
<Button onClick={action('clicked')}>😀 😎 👍 💯</Button>
)
)
Expand Down
2 changes: 1 addition & 1 deletion examples/vue-kitchen-sink/src/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ storiesOf('Addon Actions', module)
storiesOf('Addon Notes', module)
.add(
'Simple note',
withNotes({ notes: 'My notes on some bold text' })(() => ({
withNotes({ text: 'My notes on some bold text' })(() => ({
template:
'<p><strong>Etiam vulputate elit eu venenatis eleifend. Duis nec lectus augue. Morbi egestas diam sed vulputate mollis. Fusce egestas pretium vehicula. Integer sed neque diam. Donec consectetur velit vitae enim varius, ut placerat arcu imperdiet. Praesent sed faucibus arcu. Nullam sit amet nibh a enim eleifend rhoncus. Donec pretium elementum leo at fermentum. Nulla sollicitudin, mauris quis semper tempus, sem metus tristique diam, efficitur pulvinar mi urna id urna.</strong></p>',
}))
Expand Down