diff --git a/README.md b/README.md index 1f44ac2fb888..1c34d800abd9 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,25 @@ storiesOf('Button') > Have a look at [this example](example/story.js) stories to learn more about the `addWithInfo` API. +## Use with Docgen +To add a prop description, we can use a single line or multiple lines comment as below example: +```js +Object.assign(Button, { + displayName: 'Button', + propTypes: { + /** Single line comment: This is label description */ + label: React.PropTypes.string.isRequired, + /* + * Multiple lines comment: This is style description + * Must be in object + */ + style: React.PropTypes.object, + disabled: React.PropTypes.bool, + onClick: React.PropTypes.func, + }, +}); +``` + ## The FAQ **Components lose their names on static build** diff --git a/example/Button.js b/example/Button.js index 68ea5323adea..3a5f4a82f4fe 100644 --- a/example/Button.js +++ b/example/Button.js @@ -9,7 +9,12 @@ const Button = ({ disabled, label, style, onClick }) => ( Object.assign(Button, { displayName: 'Button', propTypes: { + /** Single line comment: This is label description */ label: React.PropTypes.string.isRequired, + /* + * Multiple lines comment: This is style description + * Must be in object + */ style: React.PropTypes.object, disabled: React.PropTypes.bool, onClick: React.PropTypes.func, diff --git a/src/components/PropTable.js b/src/components/PropTable.js index e65c05ce0ef4..b210af148d61 100644 --- a/src/components/PropTable.js +++ b/src/components/PropTable.js @@ -1,6 +1,18 @@ import React from 'react'; import PropVal from './PropVal'; +const stylesheet = { + propTable: { + marginLeft: -10, + borderSpacing: '10px 5px', + borderCollapse: 'separate', + }, +}; + +function isNotEmpty(obj) { + return obj && Object.keys(obj).length > 0; +} + const PropTypesMap = new Map(); for (const typeName in React.PropTypes) { if (!React.PropTypes.hasOwnProperty(typeName)) { @@ -11,82 +23,129 @@ for (const typeName in React.PropTypes) { PropTypesMap.set(type.isRequired, typeName); } -const stylesheet = { - propTable: { - marginLeft: -10, - borderSpacing: '10px 5px', - borderCollapse: 'separate', - }, -}; +function renderDocgenPropType(propType) { + if (!propType) { + return 'unknown'; + } -export default class PropTable extends React.Component { - render() { - const type = this.props.type; + const name = propType.name; - if (!type) { - return null; + switch (name) { + case 'arrayOf': + return `${propType.value.name}[]`; + case 'instanceOf': + return propType.value; + case 'union': + return propType.raw; + default: + return name; + } +} + +function hasDocgen(type) { + return isNotEmpty(type.__docgenInfo); +} + +function propsFromDocgen(type) { + let props = null; + + const docgenInfo = type.__docgenInfo || {}; + const docgenInfoProps = docgenInfo.props; + + if (docgenInfoProps) { + props = {}; + for (const propName in docgenInfoProps) { + if (!docgenInfoProps.hasOwnProperty(propName)) { + continue; + } + const docgenInfoProp = docgenInfoProps[propName]; + const defaultValueDesc = docgenInfoProp.defaultValue || {}; + const propType = docgenInfoProp.flowType || docgenInfoProp.type || 'other'; + + props[propName] = { + property: propName, + propType: renderDocgenPropType(propType), + required: docgenInfoProp.required, + description: docgenInfoProp.description, + defaultValue: defaultValueDesc.value, + }; } + } + return props; +} + +function propsFromPropTypes(type) { + let props = null; - const props = {}; - - if (type.propTypes) { - for (const property in type.propTypes) { - if (!type.propTypes.hasOwnProperty(property)) { - continue; - } - const typeInfo = type.propTypes[property]; - const propType = PropTypesMap.get(typeInfo) || 'other'; - const required = typeInfo.isRequired === undefined ? 'yes' : 'no'; - props[property] = { property, propType, required }; + if (type.propTypes) { + props = {}; + for (const property in type.propTypes) { + if (!type.propTypes.hasOwnProperty(property)) { + continue; } + const typeInfo = type.propTypes[property]; + const propType = PropTypesMap.get(typeInfo) || 'other'; + const required = typeInfo.isRequired === undefined ? 'yes' : 'no'; + props[property] = { property, propType, required }; } + } - if (type.defaultProps) { - for (const property in type.defaultProps) { - if (!type.defaultProps.hasOwnProperty(property)) { - continue; - } - const value = type.defaultProps[property]; - if (value === undefined) { - continue; - } - if (!props[property]) { - props[property] = { property }; - } - props[property].defaultValue = value; + if (type.defaultProps) { + for (const property in type.defaultProps) { + if (!type.defaultProps.hasOwnProperty(property)) { + continue; } + const value = type.defaultProps[property]; + if (value === undefined) { + continue; + } + if (!props[property]) { + props[property] = { property }; + } + props[property].defaultValue = value; } + } + return props; +} - const array = Object.values(props); - if (!array.length) { - return No propTypes defined!; +export default class PropTable extends React.Component { + render() { + const type = this.props.type; + + if (!type) { + return null; } - array.sort(function (a, b) { - return a.property > b.property; - }); - - return ( - - - - - - - - - - - {array.map(row => ( - - - - - + + const props = hasDocgen(type) ? propsFromDocgen(type) : propsFromPropTypes(type); + + if (isNotEmpty(props)) { + return ( +
propertypropTyperequireddefault
{row.property}{row.propType}{row.required}{row.defaultValue === undefined ? '-' : }
+ + + + + + + - ))} - -
propertypropTyperequireddefaultdescription
- ); + + + {Object.values(props).sort((a, b) => a.property > b.property).map(row => ( + + {row.property} + {row.propType} + {row.required} + {row.defaultValue === undefined ? '-' : } + {row.description} + + ))} + + + ); + } else { + return No propTypes defined!; + } } }