Skip to content

Commit

Permalink
addon info: make propTypes table better
Browse files Browse the repository at this point in the history
* more details for arrayOf, objectOf, shape, oneOf, oneOfType
* refactored into cleaner code + many smaller components
  • Loading branch information
billyvg committed Aug 23, 2017
1 parent 093dd01 commit 7ce5673
Show file tree
Hide file tree
Showing 12 changed files with 401 additions and 128 deletions.
15 changes: 15 additions & 0 deletions addons/info/example/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ Object.assign(Button, {
style: PropTypes.object,
disabled: PropTypes.bool,
onClick: PropTypes.func,
array: PropTypes.array,
arrayOf: PropTypes.arrayOf(PropTypes.string),
oneOf: PropTypes.oneOf(['foo', 'bar']),
shape: PropTypes.shape({
foo: PropTypes.string,
bar: PropTypes.number,
}),
nestedArrayOf: PropTypes.arrayOf(PropTypes.shape({
foo: PropTypes.shape({
baz: PropTypes.string,
bar: PropTypes.arrayOf({
PropTypes.string
}),
}),
})),
},
});

Expand Down
148 changes: 20 additions & 128 deletions addons/info/src/components/PropTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import PropTypes from 'prop-types';
import React from 'react';

import styles from './styles';
import PropVal from './PropVal';
import PrettyPropType from './types/PrettyPropType';

const PropTypesMap = new Map();

Expand All @@ -13,117 +16,10 @@ Object.keys(PropTypes).forEach(typeName => {
PropTypesMap.set(type.isRequired, typeName);
});

const stylesheet = {
hasProperty: {
marginLeft: 10,
},
code: {
fontFamily: 'Monaco, Consolas, "Courier New", monospace',
},
block: {
display: 'block',
},
propTable: {
marginTop: 10,
borderCollapse: 'collapse',
},
propTableCell: {
border: '1px solid #ccc',
padding: '2px 6px',
},
};

const isNotEmpty = obj => obj && obj.props && Object.keys(obj.props).length > 0;

const renderDocgenPropType = propType => {
if (!propType) {
return 'unknown';
}

const name = propType.name;

switch (name) {
case 'arrayOf':
return `${propType.value.name}[]`;
case 'instanceOf':
return propType.value;
case 'union':
return propType.raw;
case 'signature':
return propType.raw;
default:
return name;
}
};

const formatType = ({ propType, type, property, required }) => {
let result;

if (type) {
const PropertyLabel =
property &&
<span style={stylesheet.hasProperty}>
{property}:{' '}
</span>;

if (propType === 'other') {
return (result = type.name);
} else if (type && propType === 'arrayOf') {
return (
<span style={property ? { ...stylesheet.block, ...stylesheet.hasProperty } : {}}>
{PropertyLabel}
<span>[</span>
<span>
{formatType({
parentType: propType,
type: type.value,
propType: type.value.name,
})}
</span>
<span>]</span>
</span>
);
} else if (propType === 'enum') {
return (
<div>
{type.value.map(({ value }) => value).join(' | ')}
</div>
);
} else if (propType === 'shape') {
const values = Object.keys(type.value).map(property =>
formatType({
property,
parentType: propType,
type: type.value[property],
propType: type.value[property].name,
required: type.value[property].required,
})
);

return (
<span style={property ? { ...stylesheet.block, ...stylesheet.hasProperty } : {}}>
{PropertyLabel}
<span>
{'{'}
</span>
{values.map(value =>
<span>
{value}
</span>
)}
<span>
{'}'}
</span>
</span>
);
}
}
};

const hasDocgen = type => isNotEmpty(type.__docgenInfo);

const boolToString = value => (value ? 'yes' : 'no');

const propsFromDocgen = type => {
const props = {};
const docgenInfoProps = type.__docgenInfo.props;
Expand All @@ -135,8 +31,8 @@ const propsFromDocgen = type => {

props[property] = {
property,
propType: renderDocgenPropType(propType),
required: boolToString(docgenInfoProp.required),
propType,
required: docgenInfoProp.required,
description: docgenInfoProp.description,
defaultValue: defaultValueDesc.value,
};
Expand All @@ -151,21 +47,17 @@ const propsFromPropTypes = type => {
if (type.propTypes) {
Object.keys(type.propTypes).forEach(property => {
const typeInfo = type.propTypes[property];
const required = typeInfo.isRequired === undefined ? 'yes' : 'no';
const required = typeInfo.isRequired === undefined;
const docgenInfo =
type.__docgenInfo && type.__docgenInfo.props && type.__docgenInfo.props[property];
const description = docgenInfo ? docgenInfo.description : null;
let propType = PropTypesMap.get(typeInfo) || 'other';

if (propType === 'other') {
if (docgenInfo && docgenInfo.type) {
propType = type.__docgenInfo.props[property].type.name;
propType = docgenInfo.type.name;
}
}
// const propType = formatType({
// propType: docgenInfo && docgenInfo.type && docgenInfo.type.name,
// type: (docgenInfo && docgenInfo.type) || {}
// });

props[property] = { property, propType, required, description };
});
Expand Down Expand Up @@ -211,34 +103,34 @@ export default function PropTable(props) {
};

return (
<table style={stylesheet.propTable}>
<table style={styles.propTable}>
<thead>
<tr>
<th style={stylesheet.propTableCell}>property</th>
<th style={stylesheet.propTableCell}>propType</th>
<th style={stylesheet.propTableCell}>required</th>
<th style={stylesheet.propTableCell}>default</th>
<th style={stylesheet.propTableCell}>description</th>
<th style={styles.propTableCell}>property</th>
<th style={styles.propTableCell}>propType</th>
<th style={styles.propTableCell}>required</th>
<th style={styles.propTableCell}>default</th>
<th style={styles.propTableCell}>description</th>
</tr>
</thead>
<tbody>
{array.map(row =>
<tr key={row.property}>
<td style={stylesheet.propTableCell}>
<td style={{ ...styles.propTableCell, ...styles.code }}>
{row.property}
</td>
<td style={{ ...stylesheet.propTableCell, ...stylesheet.code }}>
{row.propType}
<td style={{ ...styles.propTableCell, ...styles.code }}>
<PrettyPropType propType={row.propType} />
</td>
<td style={stylesheet.propTableCell}>
{row.required}
<td style={styles.propTableCell}>
{row.required ? 'yes' : '-'}
</td>
<td style={stylesheet.propTableCell}>
<td style={styles.propTableCell}>
{row.defaultValue === undefined
? '-'
: <PropVal val={row.defaultValue} {...propValProps} />}
</td>
<td style={stylesheet.propTableCell}>
<td style={styles.propTableCell}>
{row.description}
</td>
</tr>
Expand Down
17 changes: 17 additions & 0 deletions addons/info/src/components/styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export default {
hasProperty: {
whiteSpace: 'nowrap',
},
code: {
whiteSpace: 'nowrap',
fontFamily: 'Monaco, Consolas, "Courier New", monospace',
},
propTable: {
marginTop: 10,
borderCollapse: 'collapse',
},
propTableCell: {
border: '1px solid #ccc',
padding: '2px 6px',
},
};
19 changes: 19 additions & 0 deletions addons/info/src/components/types/ArrayOf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';

import PrettyPropType from './PrettyPropType';
import { TypeInfo } from './proptypes';

const ArrayOf = ({ propType }) =>
<span>
<span>[</span>
<span>
<PrettyPropType propType={propType.value} />
</span>
<span>]</span>
</span>;

ArrayOf.propTypes = {
propType: TypeInfo.isRequired,
};

export default ArrayOf;
56 changes: 56 additions & 0 deletions addons/info/src/components/types/HighlightButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import PropTypes from 'prop-types';
import React from 'react';

export default class HighlightButton extends React.Component {
static propTypes = {
children: PropTypes.node.isRequired,
highlight: PropTypes.bool,
};

static defaultProps = {
highlight: false,
};

constructor(props) {
super(props);
this.state = {
hover: false,
};
}

handleMouseEnter = () => {
this.setState({ hover: true });
};

handleMouseLeave = () => {
this.setState({ hover: false });
};

render() {
const { children, highlight, ...otherProps } = this.props;
const style =
highlight || this.state.hover
? {
backgroundColor: 'rgba(0, 0, 0, 0.05)',
border: '1px solid #ccc',
}
: {};
return (
<span
role="button"
tabIndex={-1}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
style={{
...style,
...{
cursor: 'pointer',
},
}}
{...otherProps}
>
{children}
</span>
);
}
}
28 changes: 28 additions & 0 deletions addons/info/src/components/types/ObjectOf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';

import PrettyPropType from './PrettyPropType';
import { TypeInfo } from './proptypes';

const ObjectOf = ({ propType }) =>
<span>
<span>
{'{'}
</span>
<span />
<span>
{'[<key>]:'}
</span>
<span />
<span>
<PrettyPropType propType={propType.value} />
</span>
<span>
{'}'}
</span>
</span>;

ObjectOf.propTypes = {
propType: TypeInfo.isRequired,
};

export default ObjectOf;
Loading

0 comments on commit 7ce5673

Please sign in to comment.