Skip to content

Commit 84e9241

Browse files
authored
New hybrid layout (#174)
* Add stories for existing layouts * Change renderPropType to be more extensible * Add new hybrid layout * Add layout stories * Fix up spacing + borders * Add a required prop * Defualt styling for hybrid layout * Stories for hybrid layout * Required prop first * Revise styling to match finalised design * Update stories * docs(changeset): Added a new hybrid layout for displaying props. Co-authored-by: Declan Warn <dwarn@atlassian.com>
1 parent 48e0ff1 commit 84e9241

File tree

13 files changed

+319
-9
lines changed

13 files changed

+319
-9
lines changed

.changeset/afraid-socks-give.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'pretty-proptypes': minor
3+
---
4+
5+
Added a new hybrid layout for displaying props.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// @flow
2+
/** @jsx jsx */
3+
import { jsx, css } from '@emotion/core';
4+
import { Fragment, Component, type ComponentType, type Node } from 'react';
5+
import md from 'react-markings';
6+
import PrettyPropType from '../PrettyConvert';
7+
import { HeadingType, HeadingDefault, Heading, HeadingRequired } from '../Prop/Heading';
8+
import type { CommonProps } from '../types';
9+
import { colors } from '../components/constants';
10+
11+
type PropProps = CommonProps & {
12+
shapeComponent: ComponentType<CommonProps>
13+
};
14+
15+
export default class PropEntry extends Component<PropProps> {
16+
static defaultProps = {
17+
shapeComponent: (props: CommonProps) => <PrettyPropType {...props} />
18+
};
19+
20+
render() {
21+
let { shapeComponent: ShapeComponent, ...commonProps } = this.props;
22+
23+
let { defaultValue, description, name, required, type, components } = commonProps;
24+
25+
return (
26+
<Fragment>
27+
<table
28+
css={css`
29+
width: 100%;
30+
border-collapse: collapse;
31+
margin-top: 40px;
32+
33+
th {
34+
text-align: left;
35+
padding: 4px 16px 4px 8px;
36+
white-space: nowrap;
37+
vertical-align: top;
38+
}
39+
40+
td {
41+
padding: 4px 0 4px 8px;
42+
width: 100%;
43+
}
44+
`}
45+
>
46+
<caption
47+
css={css`
48+
text-align: left;
49+
margin: 0;
50+
font-size: 1em;
51+
`}
52+
>
53+
<Heading
54+
css={css`
55+
font-size: 1em;
56+
padding-bottom: 8px;
57+
border-bottom: 1px solid ${colors.N30};
58+
margin-bottom: 4px;
59+
`}
60+
>
61+
<code
62+
css={css`
63+
background-color: ${colors.N20};
64+
color: ${colors.N800};
65+
border-radius: 3px;
66+
padding: 4px 8px;
67+
line-height: 20px;
68+
display: inline-block;
69+
`}
70+
>
71+
{name}
72+
</code>
73+
{required && defaultValue === undefined && (
74+
<HeadingRequired
75+
css={css`
76+
margin-left: 1em;
77+
color: ${colors.R400};
78+
`}
79+
>
80+
required
81+
</HeadingRequired>
82+
)}
83+
</Heading>
84+
</caption>
85+
<tbody
86+
css={css`
87+
border-bottom: none;
88+
`}
89+
>
90+
<tr>
91+
<th scope="row">Description</th>
92+
<td>
93+
{description && (
94+
<components.Description>{md([description])}</components.Description>
95+
)}
96+
</td>
97+
</tr>
98+
{defaultValue !== undefined && (
99+
<tr>
100+
<th scope="row">Default</th>
101+
<td>
102+
<HeadingDefault>{defaultValue}</HeadingDefault>
103+
</td>
104+
</tr>
105+
)}
106+
<tr>
107+
<th scope="row">Type</th>
108+
<td
109+
css={css`
110+
display: flex;
111+
flex-direction: column;
112+
`}
113+
>
114+
<span>
115+
<HeadingType>{type}</HeadingType>
116+
</span>
117+
<span>
118+
<ShapeComponent {...commonProps} />
119+
</span>
120+
</td>
121+
</tr>
122+
</tbody>
123+
</table>
124+
</Fragment>
125+
);
126+
}
127+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// @flow
2+
3+
/* eslint-disable no-underscore-dangle */
4+
5+
/** @jsx jsx */
6+
import { jsx, css } from '@emotion/core';
7+
import React, { Component, type ComponentType } from 'react';
8+
9+
import type { Components } from '../components';
10+
import type { CommonProps } from '../types';
11+
import PropsWrapper from '../Props/Wrapper';
12+
import getPropTypes from '../getPropTypes';
13+
import renderPropType from '../PropType';
14+
import PropEntry from './PropEntry';
15+
16+
type Obj = {
17+
kind: 'object',
18+
members: Array<any>
19+
};
20+
21+
type Gen = {
22+
kind: 'generic',
23+
value: any
24+
};
25+
26+
type Inter = {
27+
kind: 'intersection',
28+
types: Array<Obj | Gen>
29+
};
30+
31+
type DynamicPropsProps = {
32+
components?: Components,
33+
heading?: string,
34+
shouldCollapseProps?: boolean,
35+
overrides?: {
36+
[string]: ComponentType<CommonProps>
37+
},
38+
props?: {
39+
component?: Obj | Inter
40+
},
41+
component?: ComponentType<any>
42+
};
43+
44+
const getProps = props => {
45+
if (props && props.component) {
46+
return getPropTypes(props.component);
47+
}
48+
return null;
49+
};
50+
51+
export default class HybridLayout extends Component<DynamicPropsProps> {
52+
render() {
53+
let { props, heading, component, components, ...rest } = this.props;
54+
if (component) {
55+
/* $FlowFixMe the component prop is typed as a component because
56+
that's what people pass to Props and the ___types property shouldn't
57+
exist in the components types so we're just going to ignore this error */
58+
if (component.___types) {
59+
props = { type: 'program', component: component.___types };
60+
} else {
61+
/* eslint-disable-next-line no-console */
62+
console.error(
63+
'A component was passed to <Props> but it does not have types attached.\n' +
64+
'babel-plugin-extract-react-types may not be correctly installed.\n' +
65+
'<Props> will fallback to the props prop to display types.'
66+
);
67+
}
68+
}
69+
70+
if (!components || !components.Description) {
71+
components = components || {};
72+
components.Description = ({ children }) => (
73+
<div
74+
css={css`
75+
p:first-of-type {
76+
margin-top: 0px;
77+
}
78+
p:last-of-type {
79+
margin-bottom: 0px;
80+
}
81+
`}
82+
>
83+
{children}
84+
</div>
85+
);
86+
}
87+
88+
let propTypes = getProps(props);
89+
if (!propTypes) return null;
90+
91+
return (
92+
<PropsWrapper heading={heading}>
93+
{propTypes.map(propType => renderPropType(propType, { ...rest, components }, PropEntry))}
94+
</PropsWrapper>
95+
);
96+
}
97+
}

packages/pretty-proptypes/src/PropType/index.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
/* eslint-disable no-param-reassign */
33
import React from 'react';
44
import convert, { getKind, reduceToObj } from 'kind2string';
5-
import PropRow from '../PropsTable/PropRow';
6-
import Prop from '../Prop';
75
import allComponents from '../components';
86

97
const renderPropType = (
108
propType: any,
11-
{ table = false, overrides = {}, shouldCollapseProps, components }: any,
9+
{ overrides = {}, shouldCollapseProps, components }: any,
10+
PropComponent
1211
) => {
1312
if (!components) {
1413
components = allComponents;
@@ -56,9 +55,11 @@ const renderPropType = (
5655
typeValue: propType.value
5756
};
5857

59-
return overrides[name]
60-
? <OverrideComponent {...commonProps} />
61-
: (table ? <PropRow {...commonProps} />: <Prop {...commonProps} />);
58+
return overrides[name] ? (
59+
<OverrideComponent {...commonProps} />
60+
) : (
61+
<PropComponent {...commonProps} />
62+
);
6263
};
6364

6465
export default renderPropType;

packages/pretty-proptypes/src/Props/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import PropsWrapper from './Wrapper';
1010
import getPropTypes from '../getPropTypes';
1111
import renderPropType from '../PropType';
1212

13+
import Prop from '../Prop';
14+
1315
type Obj = {
1416
kind: 'object',
1517
members: Array<any>
@@ -68,7 +70,7 @@ export default class Props extends Component<DynamicPropsProps> {
6870

6971
return (
7072
<PropsWrapper heading={heading}>
71-
{propTypes.map(propType => renderPropType(propType, rest))}
73+
{propTypes.map(propType => renderPropType(propType, rest, Prop))}
7274
</PropsWrapper>
7375
);
7476
}

packages/pretty-proptypes/src/PropsTable/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { CommonProps } from '../types';
1111
import PropsWrapper from '../Props/Wrapper';
1212
import getPropTypes from '../getPropTypes';
1313
import renderPropType from '../PropType';
14+
import PropRow from './PropRow';
1415

1516
type Obj = {
1617
kind: 'object',
@@ -83,7 +84,7 @@ export default class PropsTable extends Component<DynamicPropsProps> {
8384
<td>Description</td>
8485
</tr>
8586
</thead>
86-
{propTypes.map(propType => renderPropType(propType, { table: true, ...rest }))}
87+
{propTypes.map(propType => renderPropType(propType, rest, PropRow))}
8788
</table>
8889
</PropsWrapper>
8990
);

packages/pretty-proptypes/src/components/constants.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,8 @@ export const colors = {
1515
R75: '#FFBDAD',
1616
R50: '#FFEBE6',
1717
P300: '#6554C0',
18-
T300: '#00B8D9'
18+
T300: '#00B8D9',
19+
R400: '#DE350B',
20+
N30: '#EBECF0',
21+
N800: '#172B4D'
1922
};

packages/pretty-proptypes/src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export { default as Prop } from './Prop';
22
export { default } from './Props';
33
export { default as PropsTable } from './PropsTable';
44
export { default as components } from './components';
5+
export { default as HybridLayout } from './HybridLayout';

stories/FlowComponent.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import React from 'react';
77

88
type FlowComponentProps = {
9+
// This prop is required as it is not optional and has no default
10+
requiredProp: any,
911
// This prop is a string
1012
stringProp: string,
1113
// This prop is a number

stories/TypeScriptComponent.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ interface DummyInterface {
1010
}
1111

1212
type TypeScriptComponentProps = {
13+
// This prop is required as it is not optional and has no default
14+
requiredProp: any;
1315
// This prop is a string
1416
stringProp: string;
1517
// This prop is a number

0 commit comments

Comments
 (0)