diff --git a/.circleci/config.yml b/.circleci/config.yml index 1eb5a6b1b4a4..143eadfd667f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -41,6 +41,7 @@ jobs: - dev-kits - app - lib + - renderers chromatic: <<: *defaults steps: @@ -157,6 +158,7 @@ jobs: - addons - app - lib + - renderers smoke-tests: <<: *defaults steps: diff --git a/addons/storyshots/storyshots-core/src/frameworks/react/getChildren.ts b/addons/storyshots/storyshots-core/src/frameworks/react/getChildren.ts new file mode 100644 index 000000000000..be695f43c5d7 --- /dev/null +++ b/addons/storyshots/storyshots-core/src/frameworks/react/getChildren.ts @@ -0,0 +1,8 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import shallow from 'react-test-renderer/shallow'; + +export default (element: any) => { + const renderer = shallow.createRenderer(); + renderer.render(element); + return renderer.getRenderOutput(); +}; diff --git a/addons/storyshots/storyshots-core/src/frameworks/react/renderShallowTree.ts b/addons/storyshots/storyshots-core/src/frameworks/react/renderShallowTree.ts index ac534c1490bc..fa07344de946 100644 --- a/addons/storyshots/storyshots-core/src/frameworks/react/renderShallowTree.ts +++ b/addons/storyshots/storyshots-core/src/frameworks/react/renderShallowTree.ts @@ -1,8 +1,10 @@ // eslint-disable-next-line import/no-extraneous-dependencies import shallow from 'react-test-renderer/shallow'; +import getChildren from './getChildren'; + function getRenderedTree(story: any, context: any, { renderer, serializer }: any) { - const storyElement = story.render(); + const storyElement = getChildren(story.render()); const shallowRenderer = renderer || shallow.createRenderer(); const tree = shallowRenderer.render(storyElement); return serializer ? serializer(tree) : tree; diff --git a/addons/storyshots/storyshots-core/src/frameworks/react/renderTree.ts b/addons/storyshots/storyshots-core/src/frameworks/react/renderTree.ts index 2d989a6e44a4..3f90ff88a456 100644 --- a/addons/storyshots/storyshots-core/src/frameworks/react/renderTree.ts +++ b/addons/storyshots/storyshots-core/src/frameworks/react/renderTree.ts @@ -1,8 +1,10 @@ // eslint-disable-next-line import/no-extraneous-dependencies import reactTestRenderer from 'react-test-renderer'; +import getChildren from './getChildren'; + function getRenderedTree(story: any, context: any, { renderer, ...rendererOptions }: any) { - const storyElement = story.render(); + const storyElement = getChildren(story.render()); const currentRenderer = renderer || reactTestRenderer.create; const tree = currentRenderer(storyElement, rendererOptions); diff --git a/addons/storyshots/storyshots-core/stories/storyshot.async.test.js b/addons/storyshots/storyshots-core/stories/storyshot.async.test.js index eb80b195496a..8303e76ec536 100644 --- a/addons/storyshots/storyshots-core/stories/storyshot.async.test.js +++ b/addons/storyshots/storyshots-core/stories/storyshot.async.test.js @@ -34,7 +34,7 @@ initStoryshots({ // Assert the expected value and the corresponding snapshot expect(wrapper.find('AsyncTestComponent').contains(EXPECTED_VALUE)).toBe(true); - expect(toJson(wrapper)).toMatchSpecificSnapshot(snapshotFilename); + expect(toJson(wrapper.children())).toMatchSpecificSnapshot(snapshotFilename); // finally mark test as done done(); diff --git a/app/html/src/client/preview/render.ts b/app/html/src/client/preview/render.ts index 103ecf65bf5b..9f632528bbda 100644 --- a/app/html/src/client/preview/render.ts +++ b/app/html/src/client/preview/render.ts @@ -4,7 +4,7 @@ import { RenderMainArgs } from './types'; const rootElement = document.getElementById('root'); -export default function renderMain({ +export default async function renderMain({ storyFn, selectedKind, selectedStory, @@ -12,7 +12,7 @@ export default function renderMain({ showError, forceRender, }: RenderMainArgs) { - const element = storyFn(); + const element = await storyFn(); showMain(); if (typeof element === 'string') { diff --git a/app/react/package.json b/app/react/package.json index 72b17abb4cc2..7a5c7dc96269 100644 --- a/app/react/package.json +++ b/app/react/package.json @@ -39,11 +39,11 @@ "@storybook/addons": "5.3.0-alpha.17", "@storybook/core": "5.3.0-alpha.17", "@storybook/node-logger": "5.3.0-alpha.17", + "@storybook/renderer-react": "5.3.0-alpha.17", "@svgr/webpack": "^4.0.3", "@types/webpack-env": "^1.13.7", "babel-plugin-add-react-displayname": "^0.0.5", "babel-plugin-named-asset-import": "^0.3.1", - "babel-plugin-react-docgen": "^3.0.0", "babel-preset-react-app": "^9.0.0", "core-js": "^3.0.1", "global": "^4.3.2", diff --git a/app/react/src/client/preview/globals.ts b/app/react/src/client/preview/globals.ts index 2e6e560945af..4088a9af674d 100644 --- a/app/react/src/client/preview/globals.ts +++ b/app/react/src/client/preview/globals.ts @@ -1,3 +1,4 @@ +import '@storybook/renderer-react/dist/client/globals'; import { window } from 'global'; if (window) { diff --git a/app/react/src/client/preview/render.ts b/app/react/src/client/preview/render.ts new file mode 100644 index 000000000000..0b8b8ddad68a --- /dev/null +++ b/app/react/src/client/preview/render.ts @@ -0,0 +1,31 @@ +import { document } from 'global'; +import ReactDOM from 'react-dom'; +import { render, register } from '@storybook/renderer-react'; + +import { RenderMainArgs } from './types'; + +register(true); + +const rootEl = document ? document.getElementById('root') : null; + +export default async function renderMain({ + storyFn, + selectedKind, + selectedStory, + showMain, + forceRender, +}: RenderMainArgs) { + const element = storyFn(); + + // We need to unmount the existing set of components in the DOM node. + // Otherwise, React may not recreate instances for every story run. + // This could leads to issues like below: + // https://github.com/storybookjs/react-storybook/issues/81 + // But forceRender means that it's the same story, so we want too keep the state in that case. + if (!forceRender) { + ReactDOM.unmountComponentAtNode(rootEl); + } + + await render(element, rootEl, { name: selectedStory, kind: selectedKind }); + showMain(); +} diff --git a/app/react/src/client/preview/render.tsx b/app/react/src/client/preview/render.tsx deleted file mode 100644 index e4d935246bfa..000000000000 --- a/app/react/src/client/preview/render.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { document } from 'global'; -import React from 'react'; -import ReactDOM from 'react-dom'; -import { RenderMainArgs } from './types'; - -const rootEl = document ? document.getElementById('root') : null; - -const render = (node: React.ReactElement, el: Element) => - new Promise(resolve => { - ReactDOM.render( - process.env.STORYBOOK_EXAMPLE_APP ? {node} : node, - el, - resolve - ); - }); - -class ErrorBoundary extends React.Component<{ - showException: (err: Error) => void; - showMain: () => void; -}> { - state = { hasError: false }; - - static getDerivedStateFromError() { - return { hasError: true }; - } - - componentDidMount() { - const { hasError } = this.state; - const { showMain } = this.props; - if (!hasError) { - showMain(); - } - } - - componentDidCatch(err: Error) { - const { showException } = this.props; - // message partially duplicates stack, strip it - showException(err); - } - - render() { - const { hasError } = this.state; - const { children } = this.props; - - return hasError ? null : children; - } -} - -export default async function renderMain({ - storyFn: StoryFn, - showMain, - showException, - forceRender, -}: RenderMainArgs) { - const element = ( - - - - ); - - // We need to unmount the existing set of components in the DOM node. - // Otherwise, React may not recreate instances for every story run. - // This could leads to issues like below: - // https://github.com/storybookjs/react-storybook/issues/81 - // But forceRender means that it's the same story, so we want too keep the state in that case. - if (!forceRender) { - ReactDOM.unmountComponentAtNode(rootEl); - } - - await render(element, rootEl); -} diff --git a/app/react/src/client/preview/types.ts b/app/react/src/client/preview/types.ts index 3c69ae2579d6..73f303ea200a 100644 --- a/app/react/src/client/preview/types.ts +++ b/app/react/src/client/preview/types.ts @@ -6,7 +6,7 @@ export interface ShowErrorArgs { } export interface RenderMainArgs { - storyFn: React.FunctionComponent; + storyFn: () => React.ReactElement | null; selectedKind: string; selectedStory: string; showMain: () => void; diff --git a/app/react/src/server/options.ts b/app/react/src/server/options.ts index 04dc76219b38..b88d3339b8cb 100644 --- a/app/react/src/server/options.ts +++ b/app/react/src/server/options.ts @@ -3,8 +3,8 @@ const packageJson = require('../../package.json'); export default { packageJson, frameworkPresets: [ - require.resolve('./framework-preset-react.js'), + require.resolve('@storybook/renderer-react/dist/server/framework-preset-react.js'), require.resolve('./framework-preset-cra.js'), - require.resolve('./framework-preset-react-docgen.js'), + require.resolve('@storybook/renderer-react/dist/server/framework-preset-react-docgen.js'), ], }; diff --git a/examples/html-kitchen-sink/.storybook/config.js b/examples/html-kitchen-sink/.storybook/config.js index 2dfefa732c76..3df974cee9cf 100644 --- a/examples/html-kitchen-sink/.storybook/config.js +++ b/examples/html-kitchen-sink/.storybook/config.js @@ -1,8 +1,22 @@ +import '@storybook/renderer-react/register'; import { configure, addParameters, addDecorator } from '@storybook/html'; import { withA11y } from '@storybook/addon-a11y'; +import { ThemeProvider, themes, convert } from '@storybook/theming'; +import { useParameter } from '@storybook/client-api'; +import React from 'react'; addDecorator(withA11y); +addDecorator(getStory => { + const story = getStory(); + const framework = useParameter('framework'); + if (framework !== 'react') { + return story; + } + + return {story}; +}); + addParameters({ a11y: { config: {}, diff --git a/examples/html-kitchen-sink/.storybook/presets.js b/examples/html-kitchen-sink/.storybook/presets.js index 7c4d1835bcfa..de511b412c39 100644 --- a/examples/html-kitchen-sink/.storybook/presets.js +++ b/examples/html-kitchen-sink/.storybook/presets.js @@ -1 +1,5 @@ -module.exports = ['@storybook/addon-docs/html/preset']; +module.exports = [ + '@storybook/renderer-react/dist/server/framework-preset-react', + '@storybook/renderer-react/dist/server/framework-preset-react-docgen', + '@storybook/addon-docs/html/preset', +]; diff --git a/examples/html-kitchen-sink/package.json b/examples/html-kitchen-sink/package.json index 063781ab2138..d079ddcd619f 100644 --- a/examples/html-kitchen-sink/package.json +++ b/examples/html-kitchen-sink/package.json @@ -32,9 +32,15 @@ "@storybook/core": "5.3.0-alpha.17", "@storybook/core-events": "5.3.0-alpha.17", "@storybook/html": "5.3.0-alpha.17", + "@storybook/renderer-react": "5.3.0-alpha.17", "@storybook/source-loader": "5.3.0-alpha.17", + "@storybook/theming": "5.3.0-alpha.17", "eventemitter3": "^4.0.0", "format-json": "^1.0.3", - "global": "^4.3.2" + "global": "^4.3.2", + "prop-types": "^15.7.2", + "react": "^16.8.6", + "react-dom": "^16.8.6", + "uuid": "^3.3.2" } } diff --git a/examples/html-kitchen-sink/stories/react/Logger.js b/examples/html-kitchen-sink/stories/react/Logger.js new file mode 100644 index 000000000000..3d5a03c447f7 --- /dev/null +++ b/examples/html-kitchen-sink/stories/react/Logger.js @@ -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 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 }], + })); + }; + + static LOG_EVENT = 'Logger:log'; + + render() { + const { events } = this.state; + const { title } = this.props; + + return ( + + {title} +
+ {events.map(({ id, name, payload }) => ( + +
+ Event name: {name} +
+
+ Event payload: {json.plain(payload)} +
+
+ ))} +
+
+ ); + } +} diff --git a/examples/html-kitchen-sink/stories/react/addon-a11y.stories.js b/examples/html-kitchen-sink/stories/react/addon-a11y.stories.js new file mode 100644 index 000000000000..0fc9a117edeb --- /dev/null +++ b/examples/html-kitchen-sink/stories/react/addon-a11y.stories.js @@ -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', + options: { selectedPanel: 'storybook/a11y/panel' }, + }) + .add('Default', () => ) + .add('Label', () => ) + .add('Disabled', () => ) + .add('Invalid contrast', () => ( + // FIXME: has no effect on score + + )) + .add('delayed render', () => ( + + + + )); + +storiesOf('React|A11y/Button', module) + .addParameters({ + framework: 'react', + options: { selectedPanel: 'storybook/a11y/panel' }, + }) + .add('Default', () => ) + .add('Multiple actions', () => ( + + )) + .add('Multiple actions + config', () => ( + + )) + .add('Multiple actions as object', () => ( + + )) + .add('Multiple actions, object + config', () => ( + + )) + .add('Decorated action', () => ( + + )) + .add('Decorated action + config', () => ( + + )) + .add('Decorated actions', () => ( + + )) + .add('Decorated actions + config', () => ( + + )) + .add('Circular Payload', () => { + const circular = { foo: {} }; + circular.foo.circular = circular; + return ; + }) + .add('Reserved keyword as name', () => ) + .add('All types', () => { + function A() {} + function B() {} + + const bound = B.bind({}); + + let file; + try { + file = new File([''], 'filename.txt', { type: 'text/plain', lastModified: new Date() }); + } catch (error) { + file = error; + } + const reg = /fooBar/g; + + return ( + + + + + + + + + + + + + + + + + + + + + + + ); + }) + + .add('configureActionsDepth', () => { + configureActions({ + depth: 2, + }); + + return ( + + ); + }) + .add('Persisting the action logger', () => ( + +

Moving away from this story will persist the action logger

+ +
+ )) + .add('Limit Action Output', () => { + configureActions({ + limit: 2, + }); + + return ( + + + + + ); + }); + +storiesOf('React|Actions.deprecated', module) + .addParameters({ framework: 'react' }) + .add('Decorated Action', () => ( + + )); diff --git a/examples/html-kitchen-sink/stories/react/addon-backgrounds.stories.js b/examples/html-kitchen-sink/stories/react/addon-backgrounds.stories.js new file mode 100644 index 000000000000..54c99b3457d2 --- /dev/null +++ b/examples/html-kitchen-sink/stories/react/addon-backgrounds.stories.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { storiesOf } from '@storybook/html'; + +import BaseButton from './components/BaseButton'; + +storiesOf('React|Backgrounds', module) + .addParameters({ + framework: 'react', + backgrounds: [ + { name: 'white', value: '#ffffff' }, + { name: 'light', value: '#eeeeee' }, + { name: 'gray', value: '#cccccc' }, + { name: 'dark', value: '#222222', default: true }, + { name: 'black', value: '#000000' }, + ], + }) + .add('story 1', () => ( + + )) + .add('story 2', () => ) + .add('overriden', () => , { + backgrounds: [ + { name: 'pink', value: 'hotpink' }, + { name: 'blue', value: 'deepskyblue', default: true }, + ], + }) + .add('disabled via []', () => , { + backgrounds: [], + }) + .add( + 'skipped via disable:true', + () => , + { + backgrounds: { disable: true }, + } + ); diff --git a/examples/html-kitchen-sink/stories/react/addon-centered.stories.js b/examples/html-kitchen-sink/stories/react/addon-centered.stories.js new file mode 100644 index 000000000000..5c241fd3fb1b --- /dev/null +++ b/examples/html-kitchen-sink/stories/react/addon-centered.stories.js @@ -0,0 +1,10 @@ +import React from 'react'; +import { storiesOf } from '@storybook/html'; +import centered from '@storybook/addon-centered/react'; + +import BaseButton from './components/BaseButton'; + +storiesOf('React|Centered', module) + .addParameters({ framework: 'react' }) + .addDecorator(centered) + .add('story 1', () => ); diff --git a/examples/html-kitchen-sink/stories/react/addon-contexts.stories.js b/examples/html-kitchen-sink/stories/react/addon-contexts.stories.js new file mode 100644 index 000000000000..0fa0e8744d72 --- /dev/null +++ b/examples/html-kitchen-sink/stories/react/addon-contexts.stories.js @@ -0,0 +1,100 @@ +import React from 'react'; +import { storiesOf } from '@storybook/html'; +import { withContexts } from '@storybook/addon-contexts/react'; + +// Example A: Simple CSS Theming +const topLevelContexts = [ + { + icon: 'sidebaralt', + title: 'CSS Themes', + components: ['div'], + params: [ + { + name: 'Desert', + props: { + style: { color: 'brown', background: '#F4A261', height: '100vh', padding: '10px' }, + }, + }, + { + name: 'Ocean', + props: { + style: { color: 'white', background: '#173F5F', height: '100vh', padding: '10px' }, + }, + default: true, + }, + ], + }, +]; + +const storyLevelContexts = [ + { + title: 'CSS Themes', + params: [ + { + name: 'Forest', + props: { + style: { color: 'teal', background: '#00b894', height: '100vh', padding: '10px' }, + }, + }, + ], + }, +]; + +const stories = storiesOf('React|Contexts', module) + .addParameters({ framework: 'react' }) + .addDecorator(withContexts(topLevelContexts)); + +stories.add( + 'Simple CSS Theming', + () => <>I'm a children of the injected 'div' (where provides a theming context)., + { + contexts: storyLevelContexts, + } +); + +// Example B: Language (React Contexts API) +const NaiveIntlContext = React.createContext({ + locale: 'unknown', + greeting: 'NULL', +}); + +stories.add( + 'Languages', + () => ( + + {({ locale, greeting }) => `Your locale is "${locale}", so I say "${greeting}"!`} + + ), + { + contexts: [ + { + icon: 'globe', + title: 'Languages', + components: [NaiveIntlContext.Provider], + params: [ + { + name: 'English', + props: { + value: { locale: 'en', greeting: 'Hello' }, + }, + }, + { + name: 'French', + props: { + value: { locale: 'fr', greeting: 'Bonjour' }, + }, + }, + { + name: 'Chinese', + props: { + value: { locale: 'cn', greeting: '你好' }, + }, + }, + ], + options: { + cancelable: true, + }, + }, + ], + } +); diff --git a/examples/html-kitchen-sink/stories/react/addon-cssresources.stories.js b/examples/html-kitchen-sink/stories/react/addon-cssresources.stories.js new file mode 100644 index 000000000000..5d8671b047a1 --- /dev/null +++ b/examples/html-kitchen-sink/stories/react/addon-cssresources.stories.js @@ -0,0 +1,49 @@ +import React from 'react'; +import { storiesOf } from '@storybook/html'; + +storiesOf('React|Cssresources', module) + .addParameters({ + framework: 'react', + cssresources: [ + { + id: `bootstrap v4.1.3`, + code: ``, + picked: true, + }, + { + id: `bootstrap v3.3.5`, + code: ``, + picked: false, + }, + ], + options: { + selectedPanel: 'storybook/cssresources/panel', + }, + }) + .add('Primary Large Button', () => ( + + )); + +storiesOf('React|Cssresources', module) + .addParameters({ + framework: 'react', + cssresources: [ + { + id: `fontawesome`, + code: ``, + picked: true, + }, + { + id: `whitetheme`, + code: ``, + picked: false, + }, + ], + options: { + selectedPanel: 'storybook/cssresources/panel', + }, + }) + + .add('Camera Icon', () => Camera Icon); diff --git a/examples/html-kitchen-sink/stories/react/addon-events.stories.js b/examples/html-kitchen-sink/stories/react/addon-events.stories.js new file mode 100644 index 000000000000..45cf1c3465d0 --- /dev/null +++ b/examples/html-kitchen-sink/stories/react/addon-events.stories.js @@ -0,0 +1,93 @@ +import React from 'react'; +import EventEmitter from 'eventemitter3'; +import { storiesOf } from '@storybook/html'; + +import withEvents from '@storybook/addon-events'; +import Logger from './Logger'; + +const EVENTS = { + TEST_EVENT_1: 'test-event-1', + TEST_EVENT_2: 'test-event-2', + TEST_EVENT_3: 'test-event-3', + TEST_EVENT_4: 'test-event-4', +}; + +const emitter = new EventEmitter(); +const emit = emitter.emit.bind(emitter); + +const eventHandler = name => payload => emit(Logger.LOG_EVENT, { name, payload }); + +Object.keys(EVENTS).forEach(event => emitter.on(EVENTS[event], eventHandler(EVENTS[event]))); + +const events = [ + { + name: EVENTS.TEST_EVENT_1, + title: 'Test event 1', + payload: 0, + }, + { + name: EVENTS.TEST_EVENT_2, + title: 'Test event 2', + payload: 'Test event 2', + }, + { + name: EVENTS.TEST_EVENT_3, + title: 'Test event 3', + payload: { + string: 'value', + number: 123, + array: [1, 2, 3], + object: { + string: 'value', + number: 123, + array: [1, 2, 3], + }, + }, + }, + { + name: EVENTS.TEST_EVENT_4, + title: 'Test event 4', + payload: [ + { + string: 'value', + number: 123, + array: [1, 2, 3], + }, + { + string: 'value', + number: 123, + array: [1, 2, 3], + }, + { + string: 'value', + number: 123, + array: [1, 2, 3], + }, + ], + }, +]; + +storiesOf('React|Events', module) + .addParameters({ + framework: 'react', + options: { + selectedPanel: 'storybook/events/panel', + }, + }) + .addDecorator(withEvents({ emit, events })) + .add('Logger', () => ); + +const WithEvents = withEvents; +storiesOf('React|Events.deprecated', module) + .addParameters({ + framework: 'react', + options: { + selectedPanel: 'storybook/events/panel', + }, + }) + .addDecorator(storyFn => ( + + {storyFn()} + + )) + .add('Logger', () => ); diff --git a/examples/html-kitchen-sink/stories/react/addon-graphql.stories.js b/examples/html-kitchen-sink/stories/react/addon-graphql.stories.js new file mode 100644 index 000000000000..234e192c54e2 --- /dev/null +++ b/examples/html-kitchen-sink/stories/react/addon-graphql.stories.js @@ -0,0 +1,44 @@ +import React from 'react'; +import { storiesOf } from '@storybook/html'; +// import { setupGraphiQL } from '@storybook/addon-graphql'; + +// const graphiql = setupGraphiQL({ +// url: 'https://graphql-pokemon.now.sh/?', +// }); + +storiesOf('React|GraphQL', module).add('get Pickachu', () =>
hello
, { + framework: 'react', + graphiql: { + query: `{ + pokemon(name: "Pikachu") { + id + number + name + attacks { + special { + name + type + damage + } + } + evolutions { + id + number + name + weight { + minimum + maximum + } + attacks { + fast { + name + type + damage + } + } + } + } + }`, + url: 'https://graphql-pokemon.now.sh/?', + }, +}); diff --git a/examples/html-kitchen-sink/stories/react/addon-info-resources/EXAMPLE.md b/examples/html-kitchen-sink/stories/react/addon-info-resources/EXAMPLE.md new file mode 100644 index 000000000000..fc965844dda0 --- /dev/null +++ b/examples/html-kitchen-sink/stories/react/addon-info-resources/EXAMPLE.md @@ -0,0 +1,3 @@ +# external +## markdown +file diff --git a/examples/html-kitchen-sink/stories/react/addon-info.stories.js b/examples/html-kitchen-sink/stories/react/addon-info.stories.js new file mode 100644 index 000000000000..799aec27fcb6 --- /dev/null +++ b/examples/html-kitchen-sink/stories/react/addon-info.stories.js @@ -0,0 +1,405 @@ +import React from 'react'; +import { storiesOf } from '@storybook/html'; +import { withInfo } from '@storybook/addon-info'; +import { action } from '@storybook/addon-actions'; + +import DocgenButton from './components/DocgenButton'; +import FlowTypeButton from './components/FlowTypeButton'; +import BaseButton from './components/BaseButton'; +import ForwardedRefButton from './components/ForwardedRefButton'; +import ForwardedRefButtonWDisplayName from './components/ForwardedRefButtonWDisplayName'; +import { NamedExportButton } from './components/NamedExportButton'; +import TableComponent from './components/TableComponent'; +import externalMdDocs from './addon-info-resources/EXAMPLE.md'; + +storiesOf('React|Info/React Docgen', module) + .addParameters({ framework: 'react' }) + .addDecorator(withInfo) + .add( + 'Comments from PropType declarations', + () => ( + {}, + }} + arrayOf={[1, 2, 3]} + /> + ), + { + info: + 'Comments above the PropType declarations should be extracted from the React component file itself and rendered in the Info Addon prop table', + } + ) + .add( + 'Comments from Flow declarations', + () => , + { + info: + 'Comments above the Flow declarations should be extracted from the React component file itself and rendered in the Info Addon prop table', + } + ) + .add( + 'Comments from component declaration', + () => , + { + info: + 'Comments above the component declaration should be extracted from the React component file itself and rendered below the Info Addon heading', + } + ) + .add( + 'Comments from named export component declaration', + () => , + { + info: + 'Comments above the component declaration should be extracted from the React component file itself and rendered below the Info Addon heading', + } + ); + +const markdownDescription = ` +#### You can use markdown in your withInfo description. + +Sometimes you might want to manually include some \`code\` examples: + +~~~js +const Button = () =>