Skip to content
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

perf(docs): optimize ComponentProps #2012

Merged
merged 5 commits into from
Sep 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 }) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The limit was added because of the Icon names and Flag names. However, those props do not show enums as they are {custom}. I almost want to just show all enums and do away with the state.

At the least, let's increase the limit to 50. Most examples have much less than 50 enums, and 50 will still fit OK. Here are a few examples of our larger lists that show we can easily work with a limit of 50:

Grid.Row
color is especially funny as it a very common prop and we hide only 3 of them 😂
http://g.recordit.co/27LVhsq9Rj.gif

Transition
http://g.recordit.co/JYMjLC3xIP.gif

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reminding, customPropTypes.suggest should be also parsed as enum, I will update it separate PR.

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