1import React from 'react';
2
3import { InlineCode } from '~/components/base/code';
4import { LI, UL } from '~/components/base/list';
5import { P } from '~/components/base/paragraph';
6import { H2, H4, H4Code } from '~/components/plugins/Headings';
7import {
8  CommentTagData,
9  DefaultPropsDefinitionData,
10  PropData,
11  PropsDefinitionData,
12  TypeDefinitionData,
13} from '~/components/plugins/api/APIDataTypes';
14import {
15  CommentTextBlock,
16  resolveTypeName,
17  STYLES_SECONDARY,
18} from '~/components/plugins/api/APISectionUtils';
19
20export type APISectionPropsProps = {
21  data: PropsDefinitionData[];
22  defaultProps?: DefaultPropsDefinitionData;
23  header?: string;
24};
25
26const UNKNOWN_VALUE = '...';
27
28const extractDefaultPropValue = (
29  { comment, name }: PropData,
30  defaultProps?: DefaultPropsDefinitionData
31): string | undefined => {
32  const annotationDefault = comment?.tags?.filter((tag: CommentTagData) => tag.tag === 'default');
33  if (annotationDefault?.length) {
34    return annotationDefault[0].text;
35  }
36  return defaultProps?.type?.declaration?.children?.filter(
37    (defaultProp: PropData) => defaultProp.name === name
38  )[0]?.defaultValue;
39};
40
41const renderInheritedProp = (ip: TypeDefinitionData) => {
42  return (
43    <LI key={`inherited-prop-${ip.name}-${ip.type}`}>
44      {ip?.typeArguments ? (
45        <InlineCode>{resolveTypeName(ip)}</InlineCode>
46      ) : (
47        <InlineCode>{ip.name}</InlineCode>
48      )}
49    </LI>
50  );
51};
52
53const renderInheritedProps = (data: TypeDefinitionData[] | undefined): JSX.Element | undefined => {
54  const inheritedProps = data?.filter((ip: TypeDefinitionData) => ip.type === 'reference') ?? [];
55  if (inheritedProps.length) {
56    return (
57      <div>
58        <H4>Inherited Props</H4>
59        <UL>{inheritedProps.map(renderInheritedProp)}</UL>
60      </div>
61    );
62  }
63  return undefined;
64};
65
66const renderProps = (
67  { name, type }: PropsDefinitionData,
68  defaultValues?: DefaultPropsDefinitionData
69): JSX.Element => {
70  const baseTypes = type.types
71    ? type.types?.filter((t: TypeDefinitionData) => t.declaration)
72    : [type];
73  const propsDeclarations = baseTypes
74    .map(def => def?.declaration?.children)
75    .flat()
76    .filter((dec, i, arr) => arr.findIndex(t => t?.name === dec?.name) === i);
77
78  return (
79    <div key={`props-definition-${name}`}>
80      <UL>
81        {propsDeclarations?.map(prop =>
82          prop ? renderProp(prop, extractDefaultPropValue(prop, defaultValues)) : null
83        )}
84      </UL>
85      {renderInheritedProps(type.types)}
86    </div>
87  );
88};
89
90const renderProp = ({ comment, name, type, flags }: PropData, defaultValue?: string) => (
91  <LI key={`prop-entry-${name}`}>
92    <H4>{name}</H4>
93    <P>
94      {flags?.isOptional && <span css={STYLES_SECONDARY}>Optional&emsp;&bull;&emsp;</span>}
95      <span css={STYLES_SECONDARY}>Type:</span> <InlineCode>{resolveTypeName(type)}</InlineCode>
96      {defaultValue && defaultValue !== UNKNOWN_VALUE ? (
97        <span>
98          <span css={STYLES_SECONDARY}>&emsp;&bull;&emsp;Default:</span>{' '}
99          <InlineCode>{defaultValue}</InlineCode>
100        </span>
101      ) : null}
102    </P>
103    <CommentTextBlock comment={comment} />
104  </LI>
105);
106
107const APISectionProps: React.FC<APISectionPropsProps> = ({
108  data,
109  defaultProps,
110  header = 'Props',
111}) =>
112  data?.length ? (
113    <>
114      {header === 'Props' ? (
115        <H2 key="props-header">{header}</H2>
116      ) : (
117        <>
118          <H4Code key={`${header}-props-header`}>
119            <InlineCode>{header}</InlineCode>
120          </H4Code>
121          <br />
122        </>
123      )}
124      {data.map((propsDefinition: PropsDefinitionData) =>
125        renderProps(propsDefinition, defaultProps)
126      )}
127    </>
128  ) : null;
129
130export default APISectionProps;
131