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