diff --git a/__mocks__/inject-decorator.stories.txt b/__mocks__/inject-decorator.stories.txt
index c4f0c4d53e5d..a8eb61d2f221 100644
--- a/__mocks__/inject-decorator.stories.txt
+++ b/__mocks__/inject-decorator.stories.txt
@@ -3,7 +3,7 @@ import { storiesOf } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
import { action } from '@storybook/addon-actions';
-import DocgenButton from '../components/DocgenButton';
+import { DocgenButton } from '../components/DocgenButton';
import FlowTypeButton from '../components/FlowTypeButton';
import BaseButton from '../components/BaseButton';
import TableComponent from '../components/TableComponent';
diff --git a/addons/docs/README.md b/addons/docs/README.md
index d46737f42cd3..323e5aeba01b 100644
--- a/addons/docs/README.md
+++ b/addons/docs/README.md
@@ -97,10 +97,10 @@ First add the package. Make sure that the versions for your `@storybook/*` packa
yarn add -D @storybook/addon-docs
```
-Docs has peer dependencies on `react` and `babel-loader`. If you want to write stories in MDX, you may need to add these dependencies as well:
+Docs has peer dependencies on `react`, `react-is`, and `babel-loader`. If you want to write stories in MDX, you may need to add these dependencies as well:
```sh
-yarn add -D react babel-loader
+yarn add -D react react-is babel-loader
```
Then add the following to your `.storybook/presets.js` exports:
diff --git a/addons/docs/common/index.js b/addons/docs/common/index.js
index ab2313695b6d..ac54d6be1af0 100644
--- a/addons/docs/common/index.js
+++ b/addons/docs/common/index.js
@@ -1,2 +1 @@
-// FIXME: move this to typescript and src/react folder
-module.exports = require('../dist/lib/getPropDefs');
+module.exports = require('../dist/frameworks/common');
diff --git a/addons/docs/package.json b/addons/docs/package.json
index b4a245fc254c..502f4b48de4c 100644
--- a/addons/docs/package.json
+++ b/addons/docs/package.json
@@ -61,7 +61,8 @@
},
"peerDependencies": {
"babel-loader": "^8.0.0",
- "react": "^16.8.0"
+ "react": "^16.8.0",
+ "react-is": "^16.8.0"
},
"publishConfig": {
"access": "public"
diff --git a/addons/docs/src/blocks/Props.tsx b/addons/docs/src/blocks/Props.tsx
index ab4769476adf..871c901cda65 100644
--- a/addons/docs/src/blocks/Props.tsx
+++ b/addons/docs/src/blocks/Props.tsx
@@ -1,19 +1,24 @@
import React, { FunctionComponent } from 'react';
-import { PropsTable, PropsTableError, PropsTableProps, PropDef } from '@storybook/components';
+import { PropsTable, PropsTableError, PropsTableProps } from '@storybook/components';
import { DocsContext, DocsContextProps } from './DocsContext';
import { Component, CURRENT_SELECTION } from './shared';
-import { getPropDefs as autoPropDefs, PropDefGetter } from '../lib/getPropDefs';
+
+import { PropsExtractor } from '../lib/propsUtils';
+import { extractProps as reactExtractProps } from '../frameworks/react/extractProps';
+import { extractProps as vueExtractProps } from '../frameworks/vue/extractProps';
interface PropsProps {
exclude?: string[];
of: '.' | Component;
}
-const inferPropDefs = (framework: string): PropDefGetter | null => {
+// FIXME: remove in SB6.0 & require config
+const inferPropsExtractor = (framework: string): PropsExtractor | null => {
switch (framework) {
case 'react':
+ return reactExtractProps;
case 'vue':
- return autoPropDefs;
+ return vueExtractProps;
default:
return null;
}
@@ -32,13 +37,11 @@ export const getPropsTableProps = (
throw new Error(PropsTableError.NO_COMPONENT);
}
- const { getPropDefs = inferPropDefs(framework) } = params.docs || {};
- if (!getPropDefs) {
+ const { extractProps = inferPropsExtractor(framework) } = params.docs || {};
+ if (!extractProps) {
throw new Error(PropsTableError.PROPS_UNSUPPORTED);
}
- const allRows = getPropDefs(target);
- const rows = !exclude ? allRows : allRows.filter((row: PropDef) => !exclude.includes(row.name));
- return { rows };
+ return extractProps(target, { exclude });
} catch (err) {
return { error: err.message };
}
diff --git a/addons/docs/src/frameworks/common/index.ts b/addons/docs/src/frameworks/common/index.ts
index 5a17df8d247b..d4a985d89fab 100644
--- a/addons/docs/src/frameworks/common/index.ts
+++ b/addons/docs/src/frameworks/common/index.ts
@@ -1 +1 @@
-export * from '../../lib/getPropDefs';
+export * from '../../lib/propsUtils';
diff --git a/addons/docs/src/frameworks/react/config.js b/addons/docs/src/frameworks/react/config.js
index 0fb8a2a764df..d8ba373774cd 100644
--- a/addons/docs/src/frameworks/react/config.js
+++ b/addons/docs/src/frameworks/react/config.js
@@ -1,6 +1,7 @@
/* eslint-disable import/no-extraneous-dependencies */
import { addParameters } from '@storybook/react';
import { DocsPage, DocsContainer } from '@storybook/addon-docs/blocks';
+import { extractProps } from './extractProps';
addParameters({
docs: {
@@ -9,5 +10,6 @@ addParameters({
// react is Storybook's "native" framework, so it's stories are inherently prepared to be rendered inline
// NOTE: that the result is a react element. Hooks support is provided by the outer code.
prepareForInline: storyFn => storyFn(),
+ extractProps,
},
});
diff --git a/addons/docs/src/lib/getPropDefs.ts b/addons/docs/src/frameworks/react/extractProps.ts
similarity index 52%
rename from addons/docs/src/lib/getPropDefs.ts
rename to addons/docs/src/frameworks/react/extractProps.ts
index 6d591d204df0..fac66c418b49 100644
--- a/addons/docs/src/lib/getPropDefs.ts
+++ b/addons/docs/src/frameworks/react/extractProps.ts
@@ -1,15 +1,13 @@
/* eslint-disable no-underscore-dangle,react/forbid-foreign-prop-types */
-
import PropTypes from 'prop-types';
+import { isForwardRef, isMemo } from 'react-is';
import { PropDef } from '@storybook/components';
-import { Component } from '../blocks/shared';
+import { PropDefGetter, PropsExtractor, propsFromDocgen, hasDocgen } from '../../lib/propsUtils';
-interface PropDefMap {
+export interface PropDefMap {
[p: string]: PropDef;
}
-export type PropDefGetter = (type: Component) => PropDef[] | null;
-
const propTypesMap = new Map();
Object.keys(PropTypes).forEach(typeName => {
@@ -20,30 +18,7 @@ Object.keys(PropTypes).forEach(typeName => {
propTypesMap.set(type.isRequired, typeName);
});
-const hasDocgen = (obj: any) => obj && obj.props && Object.keys(obj.props).length > 0;
-
-const propsFromDocgen: PropDefGetter = type => {
- const props: PropDefMap = {};
- const docgenInfoProps = type.__docgenInfo.props;
-
- Object.keys(docgenInfoProps).forEach(property => {
- const docgenInfoProp = docgenInfoProps[property];
- const defaultValueDesc = docgenInfoProp.defaultValue || {};
- const propType = docgenInfoProp.flowType || docgenInfoProp.type || 'other';
-
- props[property] = {
- name: property,
- type: propType,
- required: docgenInfoProp.required,
- description: docgenInfoProp.description,
- defaultValue: defaultValueDesc.value,
- };
- });
-
- return Object.values(props);
-};
-
-const propsFromPropTypes: PropDefGetter = type => {
+const propsFromPropTypes: PropDefGetter = (type, section) => {
const props: PropDefMap = {};
if (type.propTypes) {
@@ -51,7 +26,7 @@ const propsFromPropTypes: PropDefGetter = type => {
const typeInfo = type.propTypes[property];
const required = typeInfo.isRequired === undefined;
const docgenInfo =
- type.__docgenInfo && type.__docgenInfo.props && type.__docgenInfo.props[property];
+ type.__docgenInfo && type.__docgenInfo[section] && type.__docgenInfo[section][property];
const description = docgenInfo ? docgenInfo.description : null;
let propType = propTypesMap.get(typeInfo) || 'other';
@@ -84,15 +59,22 @@ const propsFromPropTypes: PropDefGetter = type => {
return Object.values(props);
};
-export const getPropDefs: PropDefGetter = type => {
+export const getPropDefs: PropDefGetter = (type, section) => {
let processedType = type;
- if (type.render) {
- processedType = type.render().type;
+ if (!hasDocgen(type)) {
+ if (isForwardRef(type) || type.render) {
+ processedType = type.render().type;
+ }
+ if (isMemo(type)) {
+ // (typeof type.type === 'function')?
+ processedType = type.type().type;
+ }
}
- if (typeof type.type === 'function') {
- processedType = type.type().type;
- }
- return hasDocgen(processedType.__docgenInfo)
- ? propsFromDocgen(processedType)
- : propsFromPropTypes(processedType);
+ return hasDocgen(processedType)
+ ? propsFromDocgen(processedType, section)
+ : propsFromPropTypes(processedType, section);
};
+
+export const extractProps: PropsExtractor = component => ({
+ rows: getPropDefs(component, 'props'),
+});
diff --git a/addons/docs/src/frameworks/vue/config.js b/addons/docs/src/frameworks/vue/config.js
index 6ca833d0e0d2..eab1a5836071 100644
--- a/addons/docs/src/frameworks/vue/config.js
+++ b/addons/docs/src/frameworks/vue/config.js
@@ -3,6 +3,7 @@ import React from 'react';
import toReact from '@egoist/vue-to-react';
import { addParameters } from '@storybook/vue';
import { DocsPage, DocsContainer } from '@storybook/addon-docs/blocks';
+import { extractProps } from './extractProps';
addParameters({
docs: {
@@ -12,5 +13,6 @@ addParameters({
const Story = toReact(storyFn());
return ;
},
+ extractProps,
},
});
diff --git a/addons/docs/src/frameworks/vue/extractProps.ts b/addons/docs/src/frameworks/vue/extractProps.ts
new file mode 100644
index 000000000000..b90d5b85f53e
--- /dev/null
+++ b/addons/docs/src/frameworks/vue/extractProps.ts
@@ -0,0 +1,15 @@
+import { PropDef } from '@storybook/components';
+import { PropsExtractor, propsFromDocgen, hasDocgen } from '../../lib/propsUtils';
+
+const SECTIONS = ['props', 'events', 'slots'];
+
+export const extractProps: PropsExtractor = component => {
+ if (!hasDocgen(component)) {
+ return null;
+ }
+ const sections: Record = {};
+ SECTIONS.forEach(section => {
+ sections[section] = propsFromDocgen(component, section);
+ });
+ return { sections };
+};
diff --git a/addons/docs/src/lib/propsUtils.ts b/addons/docs/src/lib/propsUtils.ts
new file mode 100644
index 000000000000..1f75438a5a64
--- /dev/null
+++ b/addons/docs/src/lib/propsUtils.ts
@@ -0,0 +1,38 @@
+/* eslint-disable no-underscore-dangle */
+import { PropDef, PropsTableProps } from '@storybook/components';
+import { Component } from '../blocks/shared';
+
+export type PropsExtractor = (component: Component) => PropsTableProps | null;
+
+export type PropDefGetter = (type: Component, section: string) => PropDef[] | null;
+
+export const hasDocgen = (obj: any) => !!obj.__docgenInfo;
+
+export const hasDocgenSection = (obj: any, section: string) =>
+ obj.__docgenInfo &&
+ obj.__docgenInfo[section] &&
+ Object.keys(obj.__docgenInfo[section]).length > 0;
+
+export const propsFromDocgen: PropDefGetter = (type, section) => {
+ const props: Record = {};
+ const docgenInfoProps = type.__docgenInfo[section];
+ if (!docgenInfoProps) {
+ return null;
+ }
+
+ Object.keys(docgenInfoProps).forEach(property => {
+ const docgenInfoProp = docgenInfoProps[property];
+ const defaultValueDesc = docgenInfoProp.defaultValue || {};
+ const propType = docgenInfoProp.flowType || docgenInfoProp.type || 'other';
+
+ props[property] = {
+ name: property,
+ type: propType,
+ required: docgenInfoProp.required,
+ description: docgenInfoProp.description,
+ defaultValue: defaultValueDesc.value,
+ };
+ });
+
+ return Object.values(props);
+};
diff --git a/addons/docs/src/typings.d.ts b/addons/docs/src/typings.d.ts
index ab6a2ebd1cca..7250049e8436 100644
--- a/addons/docs/src/typings.d.ts
+++ b/addons/docs/src/typings.d.ts
@@ -1,3 +1,4 @@
declare module '@mdx-js/react';
declare module '@storybook/addon-docs/mdx-compiler-plugin';
declare module 'global';
+declare module 'react-is';
diff --git a/examples/official-storybook/components/DocgenButton.js b/examples/official-storybook/components/DocgenButton.js
index 9267c473b11d..3abebb2bf3a9 100644
--- a/examples/official-storybook/components/DocgenButton.js
+++ b/examples/official-storybook/components/DocgenButton.js
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
/** DocgenButton component description imported from comments inside the component file */
-const DocgenButton = ({ disabled, label, onClick }) => (
+export const DocgenButton = ({ disabled, label, onClick }) => (
@@ -145,5 +145,3 @@ DocgenButton.propTypes = {
*/
optionalString: PropTypes.string,
};
-
-export default DocgenButton;
diff --git a/examples/official-storybook/components/ForwardRefButton.js b/examples/official-storybook/components/ForwardRefButton.js
new file mode 100644
index 000000000000..70b7ee88f555
--- /dev/null
+++ b/examples/official-storybook/components/ForwardRefButton.js
@@ -0,0 +1,6 @@
+import React from 'react';
+import { DocgenButton } from './DocgenButton';
+
+export const ForwardRefButton = React.forwardRef((props, ref) => (
+
+));
diff --git a/examples/official-storybook/components/ImportedPropsButton.js b/examples/official-storybook/components/ImportedPropsButton.js
index a394f33a777d..b8a1e7e88cd6 100644
--- a/examples/official-storybook/components/ImportedPropsButton.js
+++ b/examples/official-storybook/components/ImportedPropsButton.js
@@ -1,5 +1,5 @@
import React from 'react';
-import DocgenButton from './DocgenButton';
+import { DocgenButton } from './DocgenButton';
/** Button component description */
const ImportedPropsButton = ({ disabled, label, onClick }) => (
diff --git a/examples/official-storybook/components/MemoButton.js b/examples/official-storybook/components/MemoButton.js
new file mode 100644
index 000000000000..98288ec6ab6f
--- /dev/null
+++ b/examples/official-storybook/components/MemoButton.js
@@ -0,0 +1,4 @@
+import React from 'react';
+import { DocgenButton } from './DocgenButton';
+
+export const MemoButton = React.memo(props => );
diff --git a/examples/official-storybook/stories/addon-docs/addon-docs.stories.js b/examples/official-storybook/stories/addon-docs/addon-docs.stories.js
index 5cd3bd8892f2..6a2f2ae2e0fd 100644
--- a/examples/official-storybook/stories/addon-docs/addon-docs.stories.js
+++ b/examples/official-storybook/stories/addon-docs/addon-docs.stories.js
@@ -1,7 +1,7 @@
import React from 'react';
import notes from '../notes/notes.md';
import mdxNotes from '../notes/notes.mdx';
-import DocgenButton from '../../components/DocgenButton';
+import { DocgenButton } from '../../components/DocgenButton';
export default {
title: 'Addons|Docs/stories',
diff --git a/examples/official-storybook/stories/addon-docs/addon-docs.stories.mdx b/examples/official-storybook/stories/addon-docs/addon-docs.stories.mdx
index b0a0a94ca1ba..c5d7a313653d 100644
--- a/examples/official-storybook/stories/addon-docs/addon-docs.stories.mdx
+++ b/examples/official-storybook/stories/addon-docs/addon-docs.stories.mdx
@@ -11,7 +11,7 @@ import {
import { action } from '@storybook/addon-actions';
import { Button } from '@storybook/react/demo';
import FlowTypeButton from '../../components/FlowTypeButton';
-import DocgenButton from '../../components/DocgenButton';
+import { DocgenButton } from '../../components/DocgenButton';
);
-
-export default {
- title: 'Addons|Docs/ForwardRef',
- component: ForwardedButton,
-};
-
-export const displaysCorrectly = () => Hello World!;
-displaysCorrectly.story = { name: 'Displays forwarded ref components correctly' };
diff --git a/examples/official-storybook/stories/addon-docs/props.stories.mdx b/examples/official-storybook/stories/addon-docs/props.stories.mdx
new file mode 100644
index 000000000000..d6a03b4ee0ac
--- /dev/null
+++ b/examples/official-storybook/stories/addon-docs/props.stories.mdx
@@ -0,0 +1,18 @@
+import { Meta, Props } from '@storybook/addon-docs/blocks';
+import { DocgenButton } from '../../components/DocgenButton';
+import { ForwardRefButton } from '../../components/ForwardRefButton';
+import { MemoButton } from '../../components/MemoButton';
+
+
+
+## Docgen
+
+
+
+## React.forwardRef
+
+
+
+## React.memo
+
+
diff --git a/examples/official-storybook/stories/addon-docs/react-memo.stories.js b/examples/official-storybook/stories/addon-docs/react-memo.stories.js
deleted file mode 100644
index 2acf981529e1..000000000000
--- a/examples/official-storybook/stories/addon-docs/react-memo.stories.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import DocgenButton from '../../components/DocgenButton';
-
-const ButtonWithMemo = React.memo(props => );
-
-export default {
- title: 'Addons|Docs/ButtonWithMemo',
- component: ButtonWithMemo,
-};
-
-export const displaysCorrectly = () => Hello World!;
-displaysCorrectly.story = { name: 'Displays components with memo correctly' };
diff --git a/examples/official-storybook/stories/addon-info/react-docgen.stories.js b/examples/official-storybook/stories/addon-info/react-docgen.stories.js
index 91400f9e18a4..a3002b07df2c 100644
--- a/examples/official-storybook/stories/addon-info/react-docgen.stories.js
+++ b/examples/official-storybook/stories/addon-info/react-docgen.stories.js
@@ -2,7 +2,7 @@ import React from 'react';
import { withInfo } from '@storybook/addon-info';
import { action } from '@storybook/addon-actions';
-import DocgenButton from '../../components/DocgenButton';
+import { DocgenButton } from '../../components/DocgenButton';
import FlowTypeButton from '../../components/FlowTypeButton';
import BaseButton from '../../components/BaseButton';
import { NamedExportButton } from '../../components/NamedExportButton';
diff --git a/examples/vue-kitchen-sink/.storybook/config.js b/examples/vue-kitchen-sink/.storybook/config.js
index daf145445510..a62e64d421e5 100644
--- a/examples/vue-kitchen-sink/.storybook/config.js
+++ b/examples/vue-kitchen-sink/.storybook/config.js
@@ -14,7 +14,7 @@ addParameters({
hierarchyRootSeparator: /\|/,
},
docs: {
- // inlineStories: true,
+ inlineStories: true,
iframeHeight: '60px',
},
});
diff --git a/examples/vue-kitchen-sink/src/stories/Button.vue b/examples/vue-kitchen-sink/src/stories/Button.vue
index 940ce7b11004..eb637acd722e 100644
--- a/examples/vue-kitchen-sink/src/stories/Button.vue
+++ b/examples/vue-kitchen-sink/src/stories/Button.vue
@@ -1,21 +1,42 @@
-
+
@@ -31,5 +52,4 @@
background-color: white;
outline: none;
}
-
\ No newline at end of file
diff --git a/examples/vue-kitchen-sink/src/stories/Welcome.vue b/examples/vue-kitchen-sink/src/stories/Welcome.vue
index 6c5258652043..d3ec62eac551 100644
--- a/examples/vue-kitchen-sink/src/stories/Welcome.vue
+++ b/examples/vue-kitchen-sink/src/stories/Welcome.vue
@@ -16,7 +16,7 @@
See these sample
- stories
+ stories
for a component called
Button
.
@@ -55,9 +55,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vue-kitchen-sink/src/stories/components/__snapshots__/info-button.stories.storyshot b/examples/vue-kitchen-sink/src/stories/components/__snapshots__/info-button.stories.storyshot
new file mode 100644
index 000000000000..edf9087332f7
--- /dev/null
+++ b/examples/vue-kitchen-sink/src/stories/components/__snapshots__/info-button.stories.storyshot
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Storyshots InfoButton Simple 1`] = `
+
+`;
diff --git a/examples/vue-kitchen-sink/src/stories/components/info-button.stories.js b/examples/vue-kitchen-sink/src/stories/components/info-button.stories.js
new file mode 100644
index 000000000000..fb62af728e0b
--- /dev/null
+++ b/examples/vue-kitchen-sink/src/stories/components/info-button.stories.js
@@ -0,0 +1,11 @@
+import InfoButton from './InfoButton.vue';
+
+export default {
+ title: 'InfoButton',
+ component: InfoButton,
+};
+
+export const simple = () => ({
+ components: { InfoButton },
+ template: '',
+});
diff --git a/examples/vue-kitchen-sink/src/stories/components/welcome.stories.js b/examples/vue-kitchen-sink/src/stories/components/welcome.stories.js
index d9eebaed48be..0f8a76a70bb0 100644
--- a/examples/vue-kitchen-sink/src/stories/components/welcome.stories.js
+++ b/examples/vue-kitchen-sink/src/stories/components/welcome.stories.js
@@ -9,6 +9,6 @@ export default {
export const welcome = () => {
return {
- render: h => h(Welcome, { props: { goToButton: linkTo('Button') } }),
+ render: h => h(Welcome, { listeners: { buttonRequested: linkTo('Button') } }),
};
};
diff --git a/examples/vue-kitchen-sink/src/stories/custom-rendering.stories.js b/examples/vue-kitchen-sink/src/stories/custom-rendering.stories.js
index e7facaa8b8bf..e84d885fc626 100644
--- a/examples/vue-kitchen-sink/src/stories/custom-rendering.stories.js
+++ b/examples/vue-kitchen-sink/src/stories/custom-rendering.stories.js
@@ -44,7 +44,7 @@ export const templateMethods = () => ({
template: `
Clicking the button will navigate to another story using the 'addon-links'
- MyButton rendered in a template + props & methods
+ MyButton rendered in a template + props & methods