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 } 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};
24
25const UNKNOWN_VALUE = '...';
26
27const extractDefaultPropValue = (
28  { comment, name }: PropData,
29  defaultProps: DefaultPropsDefinitionData
30): string | undefined => {
31  const annotationDefault = comment?.tags?.filter((tag: CommentTagData) => tag.tag === 'default');
32  if (annotationDefault?.length) {
33    return annotationDefault[0].text;
34  }
35  return defaultProps?.type?.declaration?.children?.filter(
36    (defaultProp: PropData) => defaultProp.name === name
37  )[0]?.defaultValue;
38};
39
40const renderInheritedProp = (ip: TypeDefinitionData) => {
41  return ip?.typeArguments ? (
42    <LI key={`inherited-prop-${ip.name}-${ip.type}`}>
43      <InlineCode>{resolveTypeName(ip)}</InlineCode>
44    </LI>
45  ) : null;
46};
47
48const renderInheritedProps = (data: TypeDefinitionData[] | undefined): JSX.Element | undefined => {
49  const inheritedProps = data?.filter((ip: TypeDefinitionData) => ip.type === 'reference') ?? [];
50  if (inheritedProps.length) {
51    return (
52      <div>
53        <H4>Inherited Props</H4>
54        <UL>{inheritedProps.map(renderInheritedProp)}</UL>
55      </div>
56    );
57  }
58  return undefined;
59};
60
61const renderProps = (
62  { name, type }: PropsDefinitionData,
63  defaultValues: DefaultPropsDefinitionData
64): JSX.Element => {
65  const propsDeclarations = type.types
66    ?.filter((t: TypeDefinitionData) => t.declaration)
67    .map(def => def?.declaration?.children)
68    .flat()
69    .filter((dec, i, arr) => arr.findIndex(t => t?.name === dec?.name) === i);
70
71  return (
72    <div key={`props-definition-${name}`}>
73      <UL>
74        {propsDeclarations?.map(prop =>
75          prop ? renderProp(prop, extractDefaultPropValue(prop, defaultValues)) : null
76        )}
77      </UL>
78      {renderInheritedProps(type.types)}
79    </div>
80  );
81};
82
83const renderProp = ({ comment, name, type, flags }: PropData, defaultValue?: string) => (
84  <LI key={`prop-entry-${name}`}>
85    <H4>{name}</H4>
86    <P>
87      {flags?.isOptional && <span css={STYLES_SECONDARY}>Optional&emsp;&bull;&emsp;</span>}
88      <span css={STYLES_SECONDARY}>Type:</span> <InlineCode>{resolveTypeName(type)}</InlineCode>
89      {defaultValue && defaultValue !== UNKNOWN_VALUE ? (
90        <span>
91          <span css={STYLES_SECONDARY}>&emsp;&bull;&emsp;Default:</span>{' '}
92          <InlineCode>{defaultValue}</InlineCode>
93        </span>
94      ) : null}
95    </P>
96    <CommentTextBlock comment={comment} />
97  </LI>
98);
99
100const APISectionProps: React.FC<APISectionPropsProps> = ({ data, defaultProps }) =>
101  data?.length ? (
102    <>
103      <H2 key="props-header">Props</H2>
104      {data.map((propsDefinition: PropsDefinitionData) =>
105        renderProps(propsDefinition, defaultProps)
106      )}
107    </>
108  ) : null;
109
110export default APISectionProps;
111