-
-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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
More detailed props table #1485
Changes from 10 commits
bc63adf
1d54f15
aa44f18
ba35434
bd7b139
093dd01
7ce5673
2f62f90
eda55b9
e5152f5
40a229e
a54f714
34333bd
ff6d332
2fdeff5
4c1ce1f
81dae0b
738a8c5
c90b09e
691771f
05e43d7
569d3a2
e43d49b
f2a9b66
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
"scripts": { | ||
"prepublish": "node ../../scripts/prepublish.js", | ||
"publish-storybook": "bash .scripts/publish_storybook.sh", | ||
"dev": "babel --watch --ignore tests,__tests__,test.js,stories/,story.jsx --plugins transform-runtime ./src --out-dir ./dist --copy-files", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't want to have development script/setup on every individual package in the monorepo. If you bootstrap the codebase and run |
||
"storybook": "start-storybook -p 9010" | ||
}, | ||
"dependencies": { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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(); | ||
|
||
|
@@ -13,41 +16,10 @@ Object.keys(PropTypes).forEach(typeName => { | |
PropTypesMap.set(type.isRequired, typeName); | ||
}); | ||
|
||
const stylesheet = { | ||
propTable: { | ||
marginLeft: -10, | ||
borderSpacing: '10px 5px', | ||
borderCollapse: 'separate', | ||
}, | ||
}; | ||
|
||
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 hasDocgen = type => isNotEmpty(type.__docgenInfo); | ||
|
||
const boolToString = value => (value ? 'yes' : 'no'); | ||
|
||
const propsFromDocgen = type => { | ||
const props = {}; | ||
const docgenInfoProps = type.__docgenInfo.props; | ||
|
@@ -59,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, | ||
}; | ||
|
@@ -75,21 +47,15 @@ const propsFromPropTypes = type => { | |
if (type.propTypes) { | ||
Object.keys(type.propTypes).forEach(property => { | ||
const typeInfo = type.propTypes[property]; | ||
const required = boolToString(typeInfo.isRequired === undefined); | ||
const description = | ||
type.__docgenInfo && type.__docgenInfo.props && type.__docgenInfo.props[property] | ||
? type.__docgenInfo.props[property].description | ||
: null; | ||
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 ( | ||
type.__docgenInfo && | ||
type.__docgenInfo.props && | ||
type.__docgenInfo.props[property] && | ||
type.__docgenInfo.props[property].type | ||
) { | ||
propType = type.__docgenInfo.props[property].type.name; | ||
if (docgenInfo && docgenInfo.type) { | ||
propType = docgenInfo.type.name; | ||
} | ||
} | ||
|
||
|
@@ -137,34 +103,34 @@ export default function PropTable(props) { | |
}; | ||
|
||
return ( | ||
<table style={stylesheet.propTable}> | ||
<table style={styles.propTable}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a requirement for this PR, but I'd really really like it if we could move the styling components into Again: not a requirement to get this merged, just a head's up if we don't do this in this PR, it will be done in a refactor later. |
||
<thead> | ||
<tr> | ||
<th>property</th> | ||
<th>propType</th> | ||
<th>required</th> | ||
<th>default</th> | ||
<th>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> | ||
<td style={{ ...styles.propTableCell, ...styles.code }}> | ||
{row.property} | ||
</td> | ||
<td> | ||
{row.propType} | ||
<td style={{ ...styles.propTableCell, ...styles.code }}> | ||
<PrettyPropType propType={row.propType} /> | ||
</td> | ||
<td> | ||
{row.required} | ||
<td style={styles.propTableCell}> | ||
{row.required ? 'yes' : '-'} | ||
</td> | ||
<td> | ||
<td style={styles.propTableCell}> | ||
{row.defaultValue === undefined | ||
? '-' | ||
: <PropVal val={row.defaultValue} {...propValProps} />} | ||
</td> | ||
<td> | ||
<td style={styles.propTableCell}> | ||
{row.description} | ||
</td> | ||
</tr> | ||
|
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', | ||
}, | ||
}; |
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; |
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 = () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding state to a component to add a hover state.. This calls for glamorous component styling too. |
||
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> | ||
); | ||
} | ||
} |
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> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ |
||
<span> | ||
{'{'} | ||
</span> | ||
<span /> | ||
<span> | ||
{'[<key>]:'} | ||
</span> | ||
<span /> | ||
<span> | ||
<PrettyPropType propType={propType.value} /> | ||
</span> | ||
<span> | ||
{'}'} | ||
</span> | ||
</span>; | ||
|
||
ObjectOf.propTypes = { | ||
propType: TypeInfo.isRequired, | ||
}; | ||
|
||
export default ObjectOf; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import PropTypes from 'prop-types'; | ||
import React from 'react'; | ||
|
||
import PrettyPropType from './PrettyPropType'; | ||
import PropertyLabel from './PropertyLabel'; | ||
import HighlightButton from './HighlightButton'; | ||
|
||
import { TypeInfo } from './proptypes'; | ||
|
||
const MARGIN_SIZE = 15; | ||
|
||
export default class ObjectType extends React.Component { | ||
static propTypes = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems inconsistent with the rest of the codebase. I would prefer if we discuss adding proptypes like this overall before actually doing it. If you'd like you can start a discussion about it on slack in #maintenance. For this PR, I'd refer it if we can stick to what we do everywhere else. |
||
propType: TypeInfo, | ||
depth: PropTypes.number.isRequired, | ||
}; | ||
|
||
static defaultProps = { | ||
propType: null, | ||
}; | ||
|
||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
minimized: false, | ||
}; | ||
} | ||
|
||
handleToggle = () => { | ||
this.setState({ | ||
minimized: !this.state.minimized, | ||
}); | ||
}; | ||
|
||
handleMouseEnter = () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please convert to glamorous component to do hover styling. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case, the hover state is required because I highlight the matching bracket. |
||
this.setState({ hover: true }); | ||
}; | ||
|
||
handleMouseLeave = () => { | ||
this.setState({ hover: false }); | ||
}; | ||
|
||
render() { | ||
const { propType, depth } = this.props; | ||
return ( | ||
<span> | ||
<HighlightButton | ||
onMouseEnter={this.handleMouseEnter} | ||
onMouseLeave={this.handleMouseLeave} | ||
highlight={this.state.hover} | ||
onClick={this.handleToggle} | ||
> | ||
{'{'} | ||
</HighlightButton> | ||
<HighlightButton onClick={this.handleToggle}>...</HighlightButton> | ||
{!this.state.minimized && | ||
Object.keys(propType.value).map(childProperty => | ||
<div key={childProperty} style={{ marginLeft: depth * MARGIN_SIZE }}> | ||
<PropertyLabel | ||
property={childProperty} | ||
required={propType.value[childProperty].required} | ||
/> | ||
<PrettyPropType depth={depth + 1} propType={propType.value[childProperty]} /> | ||
, | ||
</div> | ||
)} | ||
|
||
<HighlightButton | ||
onMouseEnter={this.handleMouseEnter} | ||
onMouseLeave={this.handleMouseLeave} | ||
highlight={this.state.hover} | ||
onClick={this.handleToggle} | ||
> | ||
{'}'} | ||
</HighlightButton> | ||
</span> | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import React from 'react'; | ||
|
||
import PrettyPropType from './PrettyPropType'; | ||
import { TypeInfo } from './proptypes'; | ||
|
||
const OneOfType = ({ propType }) => { | ||
const length = propType.value.length; | ||
return ( | ||
<span> | ||
{propType.value | ||
.map((value, i) => [ | ||
<PrettyPropType propType={value} />, | ||
i < length - 1 ? <span> | </span> : null, | ||
]) | ||
.reduce((acc, tuple) => acc.concat(tuple), [])} | ||
</span> | ||
); | ||
}; | ||
OneOfType.propTypes = { | ||
propType: TypeInfo.isRequired, | ||
}; | ||
export default OneOfType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer if we could add example usages to the examples/cra-kitchen-sink.
The examples inside the packages are unofficially deprecated..