Skip to content

Commit

Permalink
perf(docs): optimize ComponentProps (#2012)
Browse files Browse the repository at this point in the history
* perf(docs): optimize ComponentProps

* fix(HOC): use PureComponent for pure() HOC

* fix(ComponentProps): update `limit` value

* feat(ComponentProps): make ComponentProps functional component, move toggleEnums logic down
  • Loading branch information
layershifter authored and levithomason committed Sep 11, 2017
1 parent 3a39d64 commit 598212f
Show file tree
Hide file tree
Showing 15 changed files with 264 additions and 174 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -388,15 +388,15 @@ class ComponentExample extends Component {
onMouseLeave={this.handleMouseLeave}
style={exampleStyle}
>
<Grid.Row columns={2}>
<Grid.Column style={headerColumnStyle}>
<Grid.Row>
<Grid.Column style={headerColumnStyle} width={12}>
<ComponentExampleTitle
description={description}
title={title}
suiVersion={suiVersion}
/>
</Grid.Column>
<Grid.Column textAlign='right'>
<Grid.Column textAlign='right' width={4}>
<ComponentControls
anchorName={this.anchorName}
onCopyLink={this.handleDirectLinkClick}
Expand Down
180 changes: 26 additions & 154 deletions docs/app/Components/ComponentDoc/ComponentProps/ComponentProps.js
Original file line number Diff line number Diff line change
@@ -1,162 +1,34 @@
import _ from 'lodash'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import React from 'react'
import { Table } from 'semantic-ui-react'

import { Icon, Popup, Table } from 'src'
import ComponentPropsEnum from './ComponentPropsEnum'
import ComponentPropsExtra from './ComponentPropsExtra'

const getTagType = tag => (tag.type.type === 'AllLiteral' ? 'any' : tag.type.name)
import ComponentPropsHeader from './ComponentPropsHeader'
import ComponentPropsRow from './ComponentPropsRow'

/**
* Displays a table of a Component's PropTypes.
*/
export default class ComponentProps extends Component {
static propTypes = {
/**
* A single Component's prop info as generated by react-docgen.
* @type {object} Props info object where keys are prop names and values are prop definitions.
*/
props: PropTypes.object,
/**
* A single Component's meta info.
* @type {object} Meta info object where enum prop values are defined.
*/
meta: PropTypes.object,
}

state = {
showEnumsFor: {},
}

toggleEnumsFor = prop => () => {
this.setState({
showEnumsFor: {
...this.state.showEnumsFor,
[prop]: !this.state.showEnumsFor[prop],
},
})
}

renderName = item => <code>{item.name}</code>

renderRequired = item => item.required && (
<Popup
position='right center'
style={{ padding: '0.5em' }}
trigger={<Icon size='small' color='red' name='asterisk' />}
content='Required'
size='tiny'
inverted
/>
)

renderDefaultValue = (item) => {
const defaultValue = _.get(item, 'defaultValue.value')
if (_.isNil(defaultValue)) return null

return <code>{defaultValue}</code>
}

renderFunctionSignature = (item) => {
const params = _.filter(item.tags, { title: 'param' })
const returns = _.find(item.tags, { title: 'returns' })

// this doesn't look like a function propType doc block
// don't try to render a signature
if (_.isEmpty(params) && !returns) return

const paramSignature = params
.map(param => `${param.name}: ${getTagType(param)}`)
// prevent object properties from showing as individual params
.filter(p => !_.includes(p, '.'))
.join(', ')

const tagDescriptionRows = _.compact([...params, returns]).map((tag) => {
const name = tag.name || tag.title
return (
<div key={name} style={{ display: 'flex', flexDirection: 'row' }}>
<div style={{ flex: '2 2 0', padding: '0.1em 0' }}>
<code>{name}</code>
</div>
<div style={{ flex: '5 5 0', padding: '0.1em 0' }}>
{tag.description}
</div>
</div>
)
})

return (
<ComponentPropsExtra title={<pre>{item.name}({paramSignature}){returns ? `: ${getTagType(returns)}` : ''}</pre>}>
{tagDescriptionRows}
</ComponentPropsExtra>
)
}

renderEnums = ({ name, type, value }) => {
const { showEnumsFor } = this.state

if (type !== '{enum}' || !value) return
return (
<ComponentPropsEnum
showAll={showEnumsFor[name]}
toggle={this.toggleEnumsFor(name)}
values={value}
/>
)
}

renderRow = item => (
<Table.Row key={item.name}>
<Table.Cell collapsing>{this.renderName(item)}{this.renderRequired(item)}</Table.Cell>
<Table.Cell collapsing>{this.renderDefaultValue(item)}</Table.Cell>
<Table.Cell collapsing>{item.type}</Table.Cell>
<Table.Cell>
{item.description && <p>{item.description}</p>}
{this.renderFunctionSignature(item)}
{this.renderEnums(item)}
</Table.Cell>
</Table.Row>
)

render() {
const { props: propsDefinition } = this.props

const content = _.sortBy(_.map(propsDefinition, (config, name) => {
const value = _.get(config, 'type.value')
let type = _.get(config, 'type.name')
if (type === 'union') {
type = _.map(value, val => val.name).join('|')
}
type = type && `{${type}}`

const description = _.get(config, 'docBlock.description', '')

return {
name,
type,
value,
tags: _.get(config, 'docBlock.tags'),
required: config.required,
defaultValue: config.defaultValue,
description: description && description.split('\n').map(l => ([l, <br key={l} />])),
}
}), 'name')

return (
<Table compact='very' basic='very'>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Name</Table.HeaderCell>
<Table.HeaderCell>Default</Table.HeaderCell>
<Table.HeaderCell>Type</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{_.map(content, this.renderRow)}
</Table.Body>
</Table>
)
}
const ComponentProps = ({ props: propsDefinition }) => (
<Table compact='very' basic='very'>
<ComponentPropsHeader />
<Table.Body>
{_.map(propsDefinition, item => <ComponentPropsRow {...item} key={item.name} />)}
</Table.Body>
</Table>
)

ComponentProps.propTypes = {
/**
* A single Component's prop info as generated by react-docgen.
* @type {object} Props info object where keys are prop names and values are prop definitions.
*/
props: PropTypes.object,
/**
* A single Component's meta info.
* @type {object} Meta info object where enum prop values are defined.
*/
meta: PropTypes.object,
}

export default ComponentProps
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import _ from 'lodash'
import PropTypes from 'prop-types'
import React from 'react'

import { pure } from 'docs/app/HOC'

const ComponentPropsDefaultValue = ({ value }) => (_.isNil(value) ? null : <code>{value}</code>)

ComponentPropsDefaultValue.propTypes = {
value: PropTypes.node,
}

export default pure(ComponentPropsDefaultValue)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import _ from 'lodash'
import PropTypes from 'prop-types'
import React from 'react'

import { pure } from 'docs/app/HOC'

const ComponentPropsDescription = ({ description }) => (_.isNil(description) ? null : (
<p>
{_.map(description, line => [line, <br key={line} />])}
</p>
))

ComponentPropsDescription.propTypes = {
description: PropTypes.arrayOf(PropTypes.string),
}

export default pure(ComponentPropsDescription)
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import ComponentPropsExtra from './ComponentPropsExtra'
import ComponentPropsToggle from './ComponentPropsEnumToggle'
import ComponentPropsValue from './ComponentPropsEnumValue'

const ComponentPropsEnum = ({ limit, showAll, toggle, values }) => {
const ComponentPropsEnum = ({ limit, showAll, toggle, type, values }) => {
if (type !== 'enum' || !values) return null

const exceeds = values.length > limit
const sliced = showAll ? values : _.slice(values, 0, limit)

Expand All @@ -30,13 +32,14 @@ const ComponentPropsEnum = ({ limit, showAll, toggle, values }) => {
}

ComponentPropsEnum.defaultProps = {
limit: 10,
limit: 50,
}

ComponentPropsEnum.propTypes = {
limit: PropTypes.number,
showAll: PropTypes.bool,
toggle: PropTypes.func,
type: PropTypes.string,
values: PropTypes.array,
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import _ from 'lodash'
import PropTypes from 'prop-types'
import React from 'react'

import { neverUpdate } from 'docs/app/HOC'
import ComponentPropsExtra from './ComponentPropsExtra'

const descriptionStyle = {
flex: '5 5 0',
padding: '0.1em 0',
}

const nameStyle = {
flex: '2 2 0',
padding: '0.1em 0',
}

const rowStyle = {
display: 'flex',
flexDirection: 'row',
}

const getTagType = tag => (tag.type.type === 'AllLiteral' ? 'any' : tag.type.name)

const ComponentPropsFunctionSignature = ({ name, tags }) => {
const params = _.filter(tags, { title: 'param' })
const returns = _.find(tags, { title: 'returns' })

// this doesn't look like a function propType doc block
// don't try to render a signature
if (_.isEmpty(params) && !returns) return null

const paramSignature = params
.map(param => `${param.name}: ${getTagType(param)}`)
// prevent object properties from showing as individual params
.filter(p => !_.includes(p, '.'))
.join(', ')

const tagDescriptionRows = _.compact([...params, returns]).map((tag) => {
const title = tag.name || tag.title
return (
<div key={title} style={rowStyle}>
<div style={nameStyle}>
<code>{title}</code>
</div>
<div style={descriptionStyle}>
{tag.description}
</div>
</div>
)
})

return (
<ComponentPropsExtra title={<pre>{name}({paramSignature}){returns ? `: ${getTagType(returns)}` : ''}</pre>}>
{tagDescriptionRows}
</ComponentPropsExtra>
)
}

ComponentPropsFunctionSignature.propTypes = {
name: PropTypes.string,
tags: PropTypes.object,
}

export default neverUpdate(ComponentPropsFunctionSignature)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react'
import { Table } from 'semantic-ui-react'

import { neverUpdate } from 'docs/app/HOC'

const ComponentPropsHeader = () => (
<Table.Header>
<Table.Row>
<Table.HeaderCell>Name</Table.HeaderCell>
<Table.HeaderCell>Default</Table.HeaderCell>
<Table.HeaderCell>Type</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.Row>
</Table.Header>
)

export default neverUpdate(ComponentPropsHeader)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import PropTypes from 'prop-types'
import React from 'react'
import { Icon, Popup } from 'semantic-ui-react'

import { pure } from 'docs/app/HOC'

const popupStyle = { padding: '0.5em' }

const ComponentPropsName = ({ name, required }) => (
<div>
<code>{name}</code>
{required && (
<Popup
content='Required'
inverted
position='right center'
size='tiny'
style={popupStyle}
trigger={<Icon color='red' name='asterisk' size='small' />}
/>
)}
</div>
)

ComponentPropsName.propTypes = {
name: PropTypes.string,
required: PropTypes.bool,
}

export default pure(ComponentPropsName)
Loading

0 comments on commit 598212f

Please sign in to comment.