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

Renderer React: first example of multiframework support using decorators #6826

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
821ac04
Addon React: POC of multiframework support using decorators
Hypnosphi May 19, 2019
3b90941
Update teamcity artifact rules
Hypnosphi May 19, 2019
b613b16
Commit new storyshots
Hypnosphi May 19, 2019
c053196
Merge branch 'next' into addon-react
Hypnosphi May 28, 2019
027c7df
Raise versions
Hypnosphi May 28, 2019
013dee3
Include all `src/server` paths into babelrc node override
Hypnosphi May 28, 2019
babca9c
Use string as "framework" parameter value
Hypnosphi May 28, 2019
5c46ee1
Merge branch 'preview-hooks' into addon-react
Hypnosphi Jun 1, 2019
f9c872e
Merge branch 'preview-hooks' into addon-react
Hypnosphi Jun 1, 2019
8251203
Use preview hooks
Hypnosphi Jun 1, 2019
cf30b8f
Move more stuff from @storybook/react to @storybook/addon-react
Hypnosphi Jun 2, 2019
c159fc8
Add docs
Hypnosphi Jun 2, 2019
33ccea0
Merge remote-tracking branch 'origin/next' into addon-react
Hypnosphi Jul 14, 2019
23d7134
Integrate changes from next
Hypnosphi Jul 14, 2019
a309dfc
Fix stories with theming
Hypnosphi Jul 14, 2019
6f47998
Rename to renderer-react
Hypnosphi Jul 14, 2019
e74e2a3
Update CircleCI cache paths
Hypnosphi Jul 14, 2019
de53897
Merge branch 'next' into pr/Hypnosphi/6826
ndelangen Jul 30, 2019
1780d6e
CLEANUP duplicate snapshots
ndelangen Jul 30, 2019
a4cb580
FIX linting
ndelangen Jul 30, 2019
3bc3952
Merge next
Hypnosphi Aug 17, 2019
6ff74dc
Merge branch 'trigger-effects-after-render' into addon-react
Hypnosphi Aug 17, 2019
85cc081
Support React hooks on story level
Hypnosphi Aug 17, 2019
67cf02f
Merge branch 'trigger-effects-after-render' into addon-react
Hypnosphi Aug 17, 2019
4cd95d3
Fix tests
Hypnosphi Aug 17, 2019
b3afa3b
Merge next
Hypnosphi Oct 13, 2019
0e42647
Update lockfile
Hypnosphi Oct 14, 2019
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
35 changes: 35 additions & 0 deletions addons/frameworks/react/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "@storybook/addon-react",
"version": "5.1.0-beta.1",
"description": "React addon for Storybook",
"keywords": [
"storybook",
"react"
],
"homepage": "https://github.com/storybooks/storybook/tree/master/addons/app/react",
"bugs": {
"url": "https://github.com/storybooks/storybook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/storybooks/storybook.git",
"directory": "app/react"
},
"license": "MIT",
"main": "dist/client/index.js",
"types": "dist/client/index.d.ts",
"scripts": {
"prepare": "node ../../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.1.0-beta.1",
"@storybook/core-events": "5.1.0-beta.1",
"global": "^4.3.2"
},
"peerDependencies": {
"react-dom": "*"
},
"publishConfig": {
"access": "public"
}
}
36 changes: 36 additions & 0 deletions addons/frameworks/react/src/client/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import ReactDOM from 'react-dom';
import addons, { makeDecorator } from '@storybook/addons';
import { REGISTER_SUBSCRIPTION, STORY_CHANGED } from '@storybook/core-events';
import { document } from 'global';

let node = document.createElement('div');

const unmount = () => {
ReactDOM.unmountComponentAtNode(node);
node = document.createElement('div');
};

const subscription = () => {
const channel = addons.getChannel();
channel.on(STORY_CHANGED, unmount);
return () => {
unmount();
channel.removeListener(STORY_CHANGED, unmount);
};
};

export default makeDecorator({
name: 'withReact',
parameterName: 'framework',
wrapper: (getStory, context, { parameters }) => {
const story = getStory(context);
if (!parameters || !parameters.react) {
return story;
}

const channel = addons.getChannel();
channel.emit(REGISTER_SUBSCRIPTION, subscription);
Hypnosphi marked this conversation as resolved.
Show resolved Hide resolved
ReactDOM.render(story, node);
return node;
},
});
1 change: 1 addition & 0 deletions addons/frameworks/react/src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module 'global';
10 changes: 10 additions & 0 deletions addons/frameworks/react/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"types": ["webpack-env"]
},
"include": [
"src/**/*"
]
}
2 changes: 1 addition & 1 deletion addons/links/src/react/components/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ interface State {
}

export default class LinkTo extends PureComponent<Props, State> {
defaultProps: Props = {
static defaultProps: Props = {
kind: null,
story: null,
children: undefined,
Expand Down
1 change: 1 addition & 0 deletions app/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@babel/plugin-transform-react-constant-elements": "^7.2.0",
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@storybook/addon-react": "5.1.0-beta.1",
"@storybook/core": "5.1.0-beta.1",
"@storybook/node-logger": "5.1.0-beta.1",
"@svgr/webpack": "^4.0.3",
Expand Down
4 changes: 2 additions & 2 deletions app/react/src/server/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import packageJson from '../../package.json';
export default {
packageJson,
frameworkPresets: [
require.resolve('./framework-preset-react.js'),
require.resolve('@storybook/addon-react/dist/server/framework-preset-react.js'),
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 it's better to have the presets more accessible, something like:

import { presets } from '@storybook/addon-react';

/* blah blah blah */

frameworkPresets: [
  presets.react,
  require.resolve('./framework-preset-cra.js'),
  presets.reactDocgen,
]

also it will be more formatted way for frameworks to expose their presets.

Another option will be hide the particular presets, for example:

import { createPresets } from '@storybook/addon-react';

/* blah blah blah */

frameworkPresets: [
  ...createPresets({
     afterReact: [require.resolve('./framework-preset-cra.js')],
  });
]

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think we can mix preview and server code in one endpoint, so it probably can't be just @storybook/addon-react.

I think I'll make it just @storybook/addon-react/preset after we merge #6828

require.resolve('./framework-preset-cra.js'),
require.resolve('./framework-preset-react-docgen.js'),
require.resolve('@storybook/addon-react/dist/server/framework-preset-react-docgen.js'),
],
};
14 changes: 14 additions & 0 deletions examples/html-kitchen-sink/.storybook/config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import { configure, addParameters, addDecorator } from '@storybook/html';
import { withA11y } from '@storybook/addon-a11y';
import withReact from '@storybook/addon-react';
Hypnosphi marked this conversation as resolved.
Show resolved Hide resolved
import { ThemeProvider, themes, convert } from '@storybook/theming';
import React from 'react';

addDecorator(withA11y);

addDecorator((getStory, context) => {
const story = getStory(context);
const { framework } = context.parameters;
if (!framework || !framework.react) {
return story;
}

return <ThemeProvider theme={convert(themes.light)}>{story}</ThemeProvider>;
});
addDecorator(withReact);

addParameters({
a11y: {
config: {},
Expand Down
4 changes: 4 additions & 0 deletions examples/html-kitchen-sink/.storybook/presets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = [
'@storybook/addon-react/dist/server/framework-preset-react',
'@storybook/addon-react/dist/server/framework-preset-react-docgen',
];
8 changes: 7 additions & 1 deletion examples/html-kitchen-sink/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,21 @@
"@storybook/addon-links": "5.1.0-beta.1",
"@storybook/addon-notes": "5.1.0-beta.1",
"@storybook/addon-options": "5.1.0-beta.1",
"@storybook/addon-react": "5.1.0-beta.1",
"@storybook/addon-storyshots": "5.1.0-beta.1",
"@storybook/addon-storysource": "5.1.0-beta.1",
"@storybook/addon-viewport": "5.1.0-beta.1",
"@storybook/addons": "5.1.0-beta.1",
"@storybook/core": "5.1.0-beta.1",
"@storybook/core-events": "5.1.0-beta.1",
"@storybook/html": "5.1.0-beta.1",
"@storybook/theming": "5.1.0-beta.1",
"eventemitter3": "^3.1.0",
"format-json": "^1.0.3",
"global": "^4.3.2"
"global": "^4.3.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"prop-types": "^15.7.2",
"uuid": "^3.3.2"
}
}
76 changes: 76 additions & 0 deletions examples/html-kitchen-sink/stories/react/Logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { Component } from 'react';
import json from 'format-json';
import PropTypes from 'prop-types';

import { styled } from '@storybook/theming';
import EventEmitter from 'eventemitter3';
import uuid from 'uuid/v4';

const Wrapper = styled.div({
padding: 20,
});
const Title = styled.h1({
margin: 0,
});
const Item = styled.div({
listStyle: 'none',
marginBottom: 10,
});

export default class Logger extends Component {
static LOG_EVENT = 'Logger:log';

static propTypes = {
emitter: PropTypes.instanceOf(EventEmitter).isRequired,
title: PropTypes.string,
};

static defaultProps = {
title: 'Logger',
};

state = {
events: [],
};

componentDidMount() {
const { emitter } = this.props;

emitter.on(Logger.LOG_EVENT, this.onEventHandler);
}

componentWillUnmount() {
const { emitter } = this.props;

emitter.removeListener(Logger.LOG_EVENT, this.onEventHandler);
}

onEventHandler = ({ name, payload }) => {
this.setState(({ events }) => ({
events: [...events, { name, id: uuid(), payload }],
}));
};

render() {
const { events } = this.state;
const { title } = this.props;

return (
<Wrapper>
<Title>{title}</Title>
<dl>
{events.map(({ id, name, payload }) => (
<Item key={id}>
<dt>
<b>Event name:</b> {name}
</dt>
<dd>
<b>Event payload:</b> {json.plain(payload)}
</dd>
</Item>
))}
</dl>
</Wrapper>
);
}
}
101 changes: 101 additions & 0 deletions examples/html-kitchen-sink/stories/react/addon-a11y.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, { Fragment } from 'react';
import { storiesOf } from '@storybook/html';

import { Form } from '@storybook/components';
import BaseButton from './components/BaseButton';
import DelayedRender from './components/DelayedRender';
import Button from './components/addon-a11y/Button';

const text = 'Testing the a11y addon';
const image = 'http://placehold.it/350x150';
// eslint-disable-next-line no-script-url
const href = 'javascript:void 0';

storiesOf('React|A11y/BaseButton', module)
.addParameters({
framework: { react: true },
Hypnosphi marked this conversation as resolved.
Show resolved Hide resolved
options: { selectedPanel: 'storybook/a11y/panel' },
})
.add('Default', () => <BaseButton label="" />)
.add('Label', () => <BaseButton label={text} />)
.add('Disabled', () => <BaseButton disabled label={text} />)
.add('Invalid contrast', () => (
// FIXME: has no effect on score
<BaseButton style={{ color: 'black', backgroundColor: 'black' }} label={text} />
))
.add('delayed render', () => (
<DelayedRender>
<BaseButton label="This button has a delayed render of 1s" />
</DelayedRender>
));

storiesOf('React|A11y/Button', module)
.addParameters({
framework: { react: true },
options: { selectedPanel: 'storybook/a11y/panel' },
})
.add('Default', () => <Button />)
.add('Content', () => <Button content={text} />)
.add('Label', () => <Button label={text} />)
.add('Disabled', () => <Button disabled content={text} />)
.add('Invalid contrast', () => <Button contrast="wrong" content={text} />);

storiesOf('React|A11y/Form', module)
.addParameters({
framework: { react: true },
options: { selectedPanel: 'storybook/a11y/panel' },
})
.add('Without Label', () => (
<Form.Field label="">
<Form.Input />
</Form.Field>
))
.add('With label', () => (
<Form.Field label={text}>
<Form.Input id="1" />
</Form.Field>
))
.add('With placeholder', () => (
<Form.Field label="">
<Form.Input id="1" placeholder={text} />
</Form.Field>
));

storiesOf('React|A11y/Image', module)
.addParameters({
framework: { react: true },
options: { selectedPanel: 'storybook/a11y/panel' },
})
/* eslint-disable jsx-a11y/alt-text */
.add('Without alt', () => <img src={image} />)
.add('Without alt but unchecked', () => <img src={image} />, {
a11y: {
config: {
disableOtherRules: true,
rules: [],
},
options: {},
},
})
.add('With alt', () => <img src={image} alt={text} />)
.add('Presentation', () => <img role="presentation" src={image} />);

storiesOf('React|A11y/Typography', module)
.addParameters({
framework: { react: true },
options: { selectedPanel: 'storybook/a11y/panel' },
})
.add('Correct', () => (
<Fragment>
<h1>{text}</h1>
<p>{text}</p>
<a href={href}>{`${text}...`}</a>
</Fragment>
))
/* eslint-disable jsx-a11y/heading-has-content */
.add('Empty Heading', () => <h1 />)
.add('Empty Paragraph', () => <p />)
/* eslint-disable jsx-a11y/anchor-has-content */
.add('Empty Link', () => <a href={href} />)
/* eslint-disable jsx-a11y/anchor-is-valid */
.add('Link without href', () => <a>{`${text}...`}</a>);
Loading