Skip to content

Commit 33d0126

Browse files
Prop table permalink (#224)
* Introduce a mechanism that allows each prop to have a permalink via HTML anchors. Under the hood the rendering components get access to a 'componentDisplayName' which can be used as a composed 'id' to create unique anchor values. For all out-of-the-box layouts this has been implemented as a simple change of background-color, for a custom 'LayoutRenderer' you can just use whatever styles you prefer. * docs(changeset): Introduces new functionality to enable permalinks to each section of a props list. It's done in a backwards-compatible manner requiring no code changes for a dependency bump, but you might want to double check styles are suitable to the surroundings of where the props are displayed. * Use styles blessed by design * More defensive approach when handling 'component' Co-authored-by: Klaus Paiva <[email protected]>
1 parent ec11a5f commit 33d0126

File tree

15 files changed

+261
-33
lines changed

15 files changed

+261
-33
lines changed

.changeset/fifty-carrots-hope.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'babel-plugin-extract-react-types': minor
3+
'pretty-proptypes': minor
4+
---
5+
6+
Introduces new functionality enabling permalinks to each section of a props list. It's done in a backwards-compatible manner requiring no code changes for a dependency bump, but you might want to double check styles are suitable to the surroundings of where the props are displayed.

packages/babel-plugin-extract-react-types/__snapshots__/index.test.js.snap

Lines changed: 84 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ SomeComponent.___types = {
4141
\\"name\\": \\"SomeComponent\\",
4242
\\"type\\": null
4343
}
44-
};"
44+
};
45+
SomeComponent.___displayName = \\"SomeComponent\\";"
4546
`;
4647

4748
exports[`flow arrow function 1`] = `
@@ -85,7 +86,8 @@ SomeComponent.___types = {
8586
\\"name\\": \\"SomeComponent\\",
8687
\\"type\\": null
8788
}
88-
};"
89+
};
90+
SomeComponent.___displayName = \\"SomeComponent\\";"
8991
`;
9092

9193
exports[`flow arrow function export default identifier 1`] = `
@@ -132,7 +134,8 @@ SomeComponent.___types = {
132134
\\"name\\": \\"SomeComponent\\",
133135
\\"type\\": null
134136
}
135-
};"
137+
};
138+
SomeComponent.___displayName = \\"SomeComponent\\";"
136139
`;
137140

138141
exports[`flow arrow function then export 1`] = `
@@ -179,7 +182,8 @@ SomeComponent.___types = {
179182
\\"name\\": \\"SomeComponent\\",
180183
\\"type\\": null
181184
}
182-
};"
185+
};
186+
SomeComponent.___displayName = \\"SomeComponent\\";"
183187
`;
184188

185189
exports[`flow class 1`] = `
@@ -226,7 +230,8 @@ SomeComponent.___types = {
226230
\\"name\\": \\"SomeComponent\\",
227231
\\"type\\": null
228232
}
229-
};"
233+
};
234+
SomeComponent.___displayName = \\"SomeComponent\\";"
230235
`;
231236
232237
exports[`flow class declaration export default identifier 1`] = `
@@ -276,7 +281,8 @@ SomeComponent.___types = {
276281
\\"name\\": \\"SomeComponent\\",
277282
\\"type\\": null
278283
}
279-
};"
284+
};
285+
SomeComponent.___displayName = \\"SomeComponent\\";"
280286
`;
281287
282288
exports[`flow class declaration then export 1`] = `
@@ -326,7 +332,8 @@ SomeComponent.___types = {
326332
\\"name\\": \\"SomeComponent\\",
327333
\\"type\\": null
328334
}
329-
};"
335+
};
336+
SomeComponent.___displayName = \\"SomeComponent\\";"
330337
`;
331338
332339
exports[`flow default class 1`] = `
@@ -373,7 +380,56 @@ SomeComponent.___types = {
373380
\\"name\\": \\"SomeComponent\\",
374381
\\"type\\": null
375382
}
376-
};"
383+
};
384+
SomeComponent.___displayName = \\"SomeComponent\\";"
385+
`;
386+
387+
exports[`flow default export with named identifier 1`] = `
388+
"// @flow
389+
type Props = {
390+
/** this does something */
391+
wow: boolean
392+
};
393+
394+
const CustomComponent = (props: Props) => {
395+
return null;
396+
};
397+
398+
export default CustomComponent;
399+
CustomComponent.___types = {
400+
\\"kind\\": \\"generic\\",
401+
\\"value\\": {
402+
\\"kind\\": \\"object\\",
403+
\\"members\\": [{
404+
\\"kind\\": \\"property\\",
405+
\\"key\\": {
406+
\\"kind\\": \\"id\\",
407+
\\"name\\": \\"wow\\"
408+
},
409+
\\"value\\": {
410+
\\"kind\\": \\"boolean\\"
411+
},
412+
\\"optional\\": false,
413+
\\"leadingComments\\": [{
414+
\\"type\\": \\"commentBlock\\",
415+
\\"value\\": \\"this does something\\",
416+
\\"raw\\": \\"* this does something \\"
417+
}]
418+
}],
419+
\\"leadingComments\\": [{
420+
\\"type\\": \\"commentLine\\",
421+
\\"value\\": \\"@flow\\",
422+
\\"raw\\": \\" @flow\\"
423+
}],
424+
\\"referenceIdName\\": \\"Props\\"
425+
},
426+
\\"name\\": {
427+
\\"kind\\": \\"id\\",
428+
\\"name\\": \\"CustomComponent\\",
429+
\\"type\\": null
430+
}
431+
};
432+
CustomComponent.___displayName = \\"CustomComponent\\";"
377433
`;
378434
379435
exports[`flow default function declaration 1`] = `
@@ -420,7 +476,8 @@ SomeComponent.___types = {
420476
\\"name\\": \\"SomeComponent\\",
421477
\\"type\\": null
422478
}
423-
};"
479+
};
480+
SomeComponent.___displayName = \\"SomeComponent\\";"
424481
`;
425482
426483
exports[`flow export default function declaration 1`] = `
@@ -465,7 +522,8 @@ SomeComponent.___types = {
465522
\\"name\\": \\"SomeComponent\\",
466523
\\"type\\": null
467524
}
468-
};"
525+
};
526+
SomeComponent.___displayName = \\"SomeComponent\\";"
469527
`;
470528
471529
exports[`flow forwardRef 1`] = `
@@ -509,7 +567,8 @@ SomeComponent.___types = {
509567
\\"name\\": \\"SomeComponent\\",
510568
\\"type\\": null
511569
}
512-
};"
570+
};
571+
SomeComponent.___displayName = \\"SomeComponent\\";"
513572
`;
514573
515574
exports[`flow forwardRef function expression 1`] = `
@@ -553,7 +612,8 @@ SomeComponent.___types = {
553612
\\"name\\": \\"SomeComponent\\",
554613
\\"type\\": null
555614
}
556-
};"
615+
};
616+
SomeComponent.___displayName = \\"SomeComponent\\";"
557617
`;
558618
559619
exports[`flow forwardRef memo arrow function 1`] = `
@@ -597,7 +657,8 @@ SomeComponent.___types = {
597657
\\"name\\": \\"SomeComponent\\",
598658
\\"type\\": null
599659
}
600-
};"
660+
};
661+
SomeComponent.___displayName = \\"SomeComponent\\";"
601662
`;
602663
603664
exports[`flow forwardRef memo function expression 1`] = `
@@ -641,7 +702,8 @@ SomeComponent.___types = {
641702
\\"name\\": \\"SomeComponent\\",
642703
\\"type\\": null
643704
}
644-
};"
705+
};
706+
SomeComponent.___displayName = \\"SomeComponent\\";"
645707
`;
646708
647709
exports[`flow function declaration 1`] = `
@@ -685,7 +747,8 @@ SomeComponent.___types = {
685747
\\"name\\": \\"SomeComponent\\",
686748
\\"type\\": null
687749
}
688-
};"
750+
};
751+
SomeComponent.___displayName = \\"SomeComponent\\";"
689752
`;
690753
691754
exports[`flow memo 1`] = `
@@ -729,7 +792,8 @@ SomeComponent.___types = {
729792
\\"name\\": \\"SomeComponent\\",
730793
\\"type\\": null
731794
}
732-
};"
795+
};
796+
SomeComponent.___displayName = \\"SomeComponent\\";"
733797
`;
734798
735799
exports[`typescript default 1`] = `
@@ -754,7 +818,8 @@ SomeComponent.___types = {
754818
\\"name\\": \\"SomeComponent\\",
755819
\\"type\\": null
756820
}
757-
};"
821+
};
822+
SomeComponent.___displayName = \\"SomeComponent\\";"
758823
`;
759824
760825
exports[`typescript named 1`] = `
@@ -779,5 +844,6 @@ SomeComponent.___types = {
779844
\\"name\\": \\"SomeComponent\\",
780845
\\"type\\": null
781846
}
782-
};"
847+
};
848+
SomeComponent.___displayName = \\"SomeComponent\\";"
783849
`;

packages/babel-plugin-extract-react-types/index.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module.exports = babel => {
1515
findExportedComponents(programPath, typeSystem, state.file.filename).forEach(
1616
({ name, component }) => {
1717
// TODO: handle when name is null
18-
// it will only happen when it's the default export
18+
// it will only happen when it's a default and anonymous export
1919
// generate something like this
2020
// export default (var someName = function() {}, someName.___types = theTypes, someName)
2121
if (name !== null) {
@@ -28,6 +28,15 @@ module.exports = babel => {
2828
)
2929
)
3030
);
31+
programPath.node.body.push(
32+
t.expressionStatement(
33+
t.assignmentExpression(
34+
'=',
35+
t.memberExpression(t.identifier(name), t.identifier('___displayName')),
36+
t.stringLiteral(name)
37+
)
38+
)
39+
);
3140
}
3241
}
3342
);

packages/babel-plugin-extract-react-types/index.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,23 @@ let flowCases = [
259259
260260
export { SomeComponent }
261261
`
262+
},
263+
{
264+
name: 'default export with named identifier',
265+
code: `
266+
// @flow
267+
268+
type Props = {
269+
/** this does something */
270+
wow: boolean
271+
};
272+
273+
const CustomComponent = (props: Props) => {
274+
return null;
275+
};
276+
277+
export default CustomComponent
278+
`
262279
}
263280
];
264281

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,11 @@ export default class HybridLayout extends Component<DynamicPropsProps> {
4848
required,
4949
name,
5050
type,
51+
componentDisplayName,
5152
components: Comp
5253
}) => (
5354
<table
55+
{...(componentDisplayName ? { id: `${componentDisplayName}-${name}` } : null)}
5456
css={css`
5557
width: 100%;
5658
border-collapse: collapse;
@@ -71,6 +73,11 @@ export default class HybridLayout extends Component<DynamicPropsProps> {
7173
tbody {
7274
border-bottom: none;
7375
}
76+
77+
&:target,
78+
&:target > caption {
79+
background: #e9f2ff;
80+
}
7481
`}
7582
>
7683
<caption

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,20 @@ const LayoutRenderer: FC<LayoutRendererProps> = ({ props, component, components,
5555
}
5656
}
5757

58+
let renderProps = rest;
59+
// ensure displayName passes through, assuming it has been captured
60+
// $FlowFixMe as mentioned above, the typing for `component` is a bit off here...
61+
if (component && component.___displayName) {
62+
renderProps = {
63+
...rest,
64+
componentDisplayName: String(component.___displayName)
65+
};
66+
}
67+
5868
return getProps(resolvedProps).map(propType =>
5969
renderPropType(
6070
propType,
61-
{ ...rest, components: { ...components, PropType: PrettyPropType } },
71+
{ ...renderProps, components: { ...components, PropType: PrettyPropType } },
6272
rest.renderType
6373
)
6474
);

packages/pretty-proptypes/src/PrettyConvert/AddBrackets.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/** @jsx jsx */
33
import { jsx } from '@emotion/core';
44
import { Component, ComponentType, Fragment, type Node } from 'react';
5-
import ExpanderDefault from '../Components/Expander';
5+
import ExpanderDefault from '../components/Expander';
66

77
type Props = {
88
openBracket: string,

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ const PropTypeWrapper = (props: { children: Node }) => (
1212
<div
1313
css={css`
1414
margin-top: ${gridSize * 4}px;
15+
16+
&:target {
17+
background: #e9f2ff;
18+
}
1519
`}
1620
{...props}
1721
/>
@@ -29,10 +33,20 @@ export default class Prop extends Component<PropProps> {
2933
render() {
3034
let { shapeComponent: ShapeComponent, ...commonProps } = this.props;
3135

32-
let { defaultValue, description, name, required, type, components } = commonProps;
36+
let {
37+
defaultValue,
38+
description,
39+
name,
40+
required,
41+
type,
42+
components,
43+
componentDisplayName
44+
} = commonProps;
3345

3446
return (
35-
<PropTypeWrapper>
47+
<PropTypeWrapper
48+
{...(componentDisplayName ? { id: `${componentDisplayName}-${name}` } : null)}
49+
>
3650
<PropTypeHeading name={name} required={required} type={type} defaultValue={defaultValue} />
3751
{description && <components.Description>{md([description])}</components.Description>}
3852
<ShapeComponent {...commonProps} />

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,19 @@ export default class Props extends Component<DynamicPropsProps> {
6868
let propTypes = getProps(props);
6969
if (!propTypes) return null;
7070

71+
let renderProps = rest;
72+
// ensure displayName passes through, assuming it has been captured
73+
// $FlowFixMe as mentioned above, the typing for `component` is a bit off here...
74+
if (component && component.___displayName) {
75+
renderProps = {
76+
...rest,
77+
componentDisplayName: String(component.___displayName)
78+
};
79+
}
80+
7181
return (
7282
<PropsWrapper heading={heading}>
73-
{propTypes.map(propType => renderPropType(propType, rest, Prop))}
83+
{propTypes.map(propType => renderPropType(propType, renderProps, Prop))}
7484
</PropsWrapper>
7585
);
7686
}

0 commit comments

Comments
 (0)