diff --git a/docs/components/ComponentProps.js b/docs/components/ComponentProps.js new file mode 100644 index 00000000000..e3a1d551df7 --- /dev/null +++ b/docs/components/ComponentProps.js @@ -0,0 +1,273 @@ +import React from 'react' +import styled from 'styled-components' +import Link from '../../src/Link' +import {H1, H2, H3, H4, H5, H6} from '@primer/gatsby-theme-doctocat/src/components/heading' +import BorderBox from '../../src/BorderBox' +import Button from '../../src/Button' +import Text from '../../src/Text' +import Details from '../../src/Details' +import InlineCode from '@primer/gatsby-theme-doctocat/src/components/inline-code' +import Paragraph from '@primer/gatsby-theme-doctocat/src/components/paragraph' +import Table from './Table' + +function getHeadingElement(headingLevel) { + switch (headingLevel) { + case 1: + return H1 + case 2: + return H2 + case 3: + return H3 + case 4: + return H4 + case 5: + return H5 + case 6: + return H6 + } +} + +const InheritedBox = styled(BorderBox)` + > :first-child { + margin-top: 0px !important; + } +` + +function collect(inherited, acc = {system: [], inherited: []}, seen = new Set()) { + for (const Comp of inherited) { + if (Comp.propTypes && Comp.propTypes.__doc_spec) { + const {system, inherited: nestedInherited} = Comp.propTypes.__doc_spec + for (const sys of system) { + if (!seen.has(sys)) { + acc.system.push(sys) + seen.add(sys) + } + } + for (const inh of nestedInherited) { + if (!seen.has(inh)) { + acc.inherited.push(inh) + seen.add(inh) + } + } + if (nestedInherited.length) { + collect(nestedInherited, acc, seen) + } + } + } + + return acc +} + +function ComponentProps({Component, name, headingLevel, showInherited, showSystem}) { + if (!Component.propTypes || !Component.propTypes.__doc_spec) { + return null + } + + const Heading = getHeadingElement(headingLevel) + + const {own} = Component.propTypes.__doc_spec + const {system, inherited} = collect([Component]) + + const output = [] + + if (own) { + output.push( + + ) + } + + const inheritedWithDocs = inherited.filter(Comp => Comp.propTypes && Comp.propTypes.__doc_spec) + if (inheritedWithDocs.length && showInherited) { + output.push() + output.push( + + Inherited props + + {name} inherits from the following components and thus receives their props: + + + ) + for (const Comp of inherited) { + output.push( +
+ {({open}) => ( + <> + + + + + + )} +
+ ) + } + } + + if (showSystem && system && system.length) { + output.push() + } + + return <>{output} +} + +ComponentProps.defaultProps = { + headingLevel: 3, + showInherited: true, + showSystem: true +} + +function getDefault(defaults, prop) { + const value = defaults[prop] + return getDisplayValue(value) +} + +function getDisplayValue(value, key) { + const type = typeof value + + if (type === 'object') { + return '(object)' + } + + if (type === 'string') { + return "{value}" + } + + if (type === 'number' || type === 'boolean') { + return {String(value)} + } + + if (type === 'function') { + return (function) + } + + return value +} + +const PropValueList = styled.ul` + margin-block-start: 0; + margin-block-end: 0; + margin-left: -20px; +` + +function getType(doc) { + switch (doc.name) { + case 'any': + return 'any' + case 'array': + return 'array' + case 'bool': + return 'boolean' + case 'func': + return 'function' + case 'number': + return 'number' + case 'node': + return 'node' + case 'object': + return 'object' + case 'string': + return 'string' + case 'symbol': + return 'symbol' + case 'element': + return 'element' + case 'elementType': + return 'element type' + + case 'instanceOf': + return `instance of ${doc.args.name}` + case 'arrayOf': + return `array of ${getType(doc.args)}s` + case 'oneOf': { + const items = doc.args.map((item, idx) => getDisplayValue(item, idx)) + return ( + <> + One of: + + {items.map((item, idx) => ( + // eslint-disable-next-line react/no-array-index-key +
  • {item}
  • + ))} +
    + + ) + } + case 'oneOfType': { + const items = doc.args.map(item => getType(item.doc)) + return ( + <> + One of type: + + {items.map((item, idx) => ( + // eslint-disable-next-line react/no-array-index-key +
  • {item}
  • + ))} +
    + + ) + } + case 'objectOf': { + return `object with values of type ${getType(doc.args.doc)}` + } + default: + return '(unknown type)' + } +} + +function SystemProps({name, systemProps, headingLevel}) { + const Heading = getHeadingElement(headingLevel) + return ( + <> + System props + + {name} components receive the following categories of system props. See our{' '} + System Props page for more information. + + [ + {s.systemPropsName}, +
    {Object.keys(s.propTypes).join(', ')}
    + ])} + /> + + ) +} + +function OwnProps({props, defaults, headingLevel}) { + const Heading = getHeadingElement(headingLevel) + const propsToShow = Object.keys(props).filter(key => !props[key].doc.hidden) + if (propsToShow.length === 0) { + return ( + <> + Component props + This component gets no additional component specific props. + + ) + } + + return ( + <> + Component props +
    [ + `${prop}${props[prop].doc.isRequired ? '*' : ''}`, + getType(props[prop].doc), + getDefault(defaults, prop), + props[prop].doc.desc + ])} + /> + + ) +} + +export default ComponentProps diff --git a/docs/components/Table.js b/docs/components/Table.js new file mode 100644 index 00000000000..07438ce4960 --- /dev/null +++ b/docs/components/Table.js @@ -0,0 +1,32 @@ +/* eslint-disable react/no-array-index-key */ +import React from 'react' +import DoctocatTable from '@primer/gatsby-theme-doctocat/src/components/table' + +function Table({columns, rows, ...rest}) { + return ( + + + + {columns.map((col, idx) => ( + + ))} + + + + {rows.map((row, idx) => ( + + {row.map((val, iidx) => ( + + ))} + + ))} + + + ) +} + +export default Table diff --git a/docs/content/BorderBox.md b/docs/content/BorderBox.md index 287115e210f..41352e17c24 100644 --- a/docs/content/BorderBox.md +++ b/docs/content/BorderBox.md @@ -11,7 +11,14 @@ BorderBox is a Box component with a border. When no `borderColor` is present, th This is a BorderBox ``` -## System props +## Props + +import {BorderBox} from "@primer/components" +import ComponentProps from "../components/ComponentProps" + + + + diff --git a/docs/content/Box.md b/docs/content/Box.md index e101a340098..24674edf8a7 100644 --- a/docs/content/Box.md +++ b/docs/content/Box.md @@ -16,12 +16,9 @@ The Box component serves as a wrapper component for most layout related needs. U ``` -## System props +## Props -Box components get the `COMMON` and `LAYOUT` categories of system props. Read our [System Props](/system-props) doc page for a full list of available props. +import {Box} from "@primer/components" +import ComponentProps from "../components/ComponentProps" -## Component props - -| Prop name | Type | Default | Description | -| :- | :- | :-: | :- | -| as | String | `div` | sets the HTML tag for the component| + diff --git a/docs/content/Link.md b/docs/content/Link.md index 25b302705f9..dd947f53490 100644 --- a/docs/content/Link.md +++ b/docs/content/Link.md @@ -16,15 +16,9 @@ In special cases where you'd like a `
    + {col} +
    + {val} +