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  TypeDeclarationData,
13  TypePropertyData,
14} from '~/components/plugins/api/APIDataTypes';
15import {
16  CommentTextBlock,
17  resolveTypeName,
18  STYLES_SECONDARY,
19} from '~/components/plugins/api/APISectionUtils';
20
21export type APISectionPropsProps = {
22  data: PropsDefinitionData[];
23  defaultProps: DefaultPropsDefinitionData;
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: TypePropertyData) => defaultProp.name === name
38  )[0]?.defaultValue;
39};
40
41const renderInheritedProp = (ip: TypeDeclarationData) => {
42  const component = ip?.typeArguments ? ip.typeArguments[0]?.queryType?.name : null;
43  return component ? (
44    <LI key={`inherited-prop-${component}`}>
45      <InlineCode>{component}</InlineCode>
46    </LI>
47  ) : null;
48};
49
50const renderInheritedProps = (data: TypeDeclarationData[]): JSX.Element | undefined => {
51  const inheritedProps = data?.filter((ip: TypeDeclarationData) => ip.type === 'reference') ?? [];
52  if (inheritedProps.length) {
53    return (
54      <div>
55        <H4>Inherited Props</H4>
56        <UL>{inheritedProps.map(renderInheritedProp)}</UL>
57      </div>
58    );
59  }
60  return undefined;
61};
62
63const renderProps = (
64  { name, type }: PropsDefinitionData,
65  defaultValues: DefaultPropsDefinitionData
66): JSX.Element => {
67  const propsDeclarations = type.types?.filter((e: TypeDeclarationData) => e.declaration);
68  return (
69    <div key={`props-definition-${name}`}>
70      <UL>
71        {propsDeclarations?.map((def: TypeDeclarationData) =>
72          def.declaration?.children?.map((prop: PropData) =>
73            renderProp(prop, extractDefaultPropValue(prop, defaultValues))
74          )
75        )}
76      </UL>
77      {renderInheritedProps(type.types)}
78    </div>
79  );
80};
81
82const renderProp = ({ comment, name, type, flags }: PropData, defaultValue?: string) => (
83  <LI key={`prop-entry-${name}`}>
84    <H4>{name}</H4>
85    <P>
86      {flags?.isOptional && <span css={STYLES_SECONDARY}>Optional&emsp;&bull;&emsp;</span>}
87      <span css={STYLES_SECONDARY}>Type:</span> <InlineCode>{resolveTypeName(type)}</InlineCode>
88      {defaultValue && defaultValue !== UNKNOWN_VALUE ? (
89        <span>
90          <span css={STYLES_SECONDARY}>&emsp;&bull;&emsp;Default:</span>{' '}
91          <InlineCode>{defaultValue}</InlineCode>
92        </span>
93      ) : null}
94    </P>
95    <CommentTextBlock comment={comment} />
96  </LI>
97);
98
99const APISectionProps: React.FC<APISectionPropsProps> = ({ data, defaultProps }) =>
100  data?.length ? (
101    <>
102      <H2 key="props-header">Props</H2>
103      {data.map((propsDefinition: PropsDefinitionData) =>
104        renderProps(propsDefinition, defaultProps)
105      )}
106    </>
107  ) : null;
108
109export default APISectionProps;
110