1import { CornerDownRightIcon } from '@expo/styleguide-icons';
2import ReactMarkdown from 'react-markdown';
3
4import { APIDataType } from '~/components/plugins/api/APIDataType';
5import {
6  AccessorDefinitionData,
7  MethodDefinitionData,
8  MethodParamData,
9  MethodSignatureData,
10  PropData,
11  TypeSignaturesData,
12} from '~/components/plugins/api/APIDataTypes';
13import { APISectionDeprecationNote } from '~/components/plugins/api/APISectionDeprecationNote';
14import { APISectionPlatformTags } from '~/components/plugins/api/APISectionPlatformTags';
15import {
16  CommentTextBlock,
17  getMethodName,
18  getTagNamesList,
19  mdComponents,
20  renderParams,
21  resolveTypeName,
22  STYLES_APIBOX,
23  STYLES_APIBOX_NESTED,
24  STYLES_NOT_EXPOSED_HEADER,
25  TypeDocKind,
26  H3Code,
27  H4Code,
28  getTagData,
29  getCommentContent,
30  BoxSectionHeader,
31} from '~/components/plugins/api/APISectionUtils';
32import { H2, LI, UL, MONOSPACE } from '~/ui/components/Text';
33
34export type APISectionMethodsProps = {
35  data: (MethodDefinitionData | PropData)[];
36  apiName?: string;
37  header?: string;
38  exposeInSidebar?: boolean;
39};
40
41export type RenderMethodOptions = {
42  apiName?: string;
43  header?: string;
44  exposeInSidebar?: boolean;
45};
46
47export const renderMethod = (
48  method: MethodDefinitionData | AccessorDefinitionData | PropData,
49  { apiName, exposeInSidebar = true }: RenderMethodOptions = {}
50) => {
51  const signatures =
52    (method as MethodDefinitionData).signatures ||
53    (method as PropData)?.type?.declaration?.signatures || [
54      (method as AccessorDefinitionData)?.getSignature,
55    ] ||
56    [];
57  const HeaderComponent = exposeInSidebar ? H3Code : H4Code;
58  return signatures.map(
59    ({ name, parameters, comment, type }: MethodSignatureData | TypeSignaturesData) => {
60      const returnComment = getTagData('returns', comment);
61      return (
62        <div
63          key={`method-signature-${method.name || name}-${parameters?.length || 0}`}
64          css={[STYLES_APIBOX, STYLES_APIBOX_NESTED]}>
65          <APISectionDeprecationNote comment={comment} />
66          <APISectionPlatformTags comment={comment} prefix="Only for:" />
67          <HeaderComponent tags={getTagNamesList(comment)}>
68            <MONOSPACE weight="medium" css={!exposeInSidebar && STYLES_NOT_EXPOSED_HEADER}>
69              {getMethodName(method as MethodDefinitionData, apiName, name, parameters)}
70            </MONOSPACE>
71          </HeaderComponent>
72          {parameters && parameters.length > 0 && (
73            <>
74              {renderParams(parameters)}
75              <br />
76            </>
77          )}
78          <CommentTextBlock comment={comment} includePlatforms={false} />
79          {resolveTypeName(type) !== 'undefined' && (
80            <>
81              <BoxSectionHeader text="Returns" />
82              <UL className="!list-none !ml-0">
83                <LI>
84                  <CornerDownRightIcon className="inline-block icon-sm text-icon-secondary align-middle mr-2" />
85                  <APIDataType typeDefinition={type} />
86                </LI>
87              </UL>
88              <>
89                <br />
90                {returnComment ? (
91                  <ReactMarkdown components={mdComponents}>
92                    {getCommentContent(returnComment.content)}
93                  </ReactMarkdown>
94                ) : undefined}
95              </>
96            </>
97          )}
98        </div>
99      );
100    }
101  );
102};
103
104const APISectionMethods = ({
105  data,
106  apiName,
107  header = 'Methods',
108  exposeInSidebar = true,
109}: APISectionMethodsProps) =>
110  data?.length ? (
111    <>
112      <H2 key={`${header}-header`}>{header}</H2>
113      {data.map((method: MethodDefinitionData | PropData) =>
114        renderMethod(method, { apiName, header, exposeInSidebar })
115      )}
116    </>
117  ) : null;
118
119export default APISectionMethods;
120
121export const APIMethod = ({
122  name,
123  comment,
124  returnTypeName,
125  isProperty = false,
126  isReturnTypeReference = false,
127  exposeInSidebar = false,
128  parameters = [],
129  platforms = [],
130}: {
131  exposeInSidebar?: boolean;
132  name: string;
133  comment: string;
134  returnTypeName: string;
135  isProperty: boolean;
136  isReturnTypeReference: boolean;
137  platforms: ('Android' | 'iOS' | 'Web')[];
138  parameters: {
139    name: string;
140    comment?: string;
141    typeName: string;
142    isReference?: boolean;
143  }[];
144}) => {
145  const parsedParameters = parameters.map(
146    param =>
147      ({
148        name: param.name,
149        type: { name: param.typeName, type: param.isReference ? 'reference' : 'literal' },
150        comment: {
151          summary: [{ kind: 'text', text: param.comment }],
152        },
153      } as MethodParamData)
154  );
155  return renderMethod(
156    {
157      name,
158      signatures: [
159        {
160          name,
161          parameters: parsedParameters,
162          comment: {
163            summary: [{ kind: 'text', text: comment }],
164            blockTags: platforms.map(text => ({
165              tag: 'platform',
166              content: [{ kind: 'text', text }],
167            })),
168          },
169          type: { name: returnTypeName, type: isReturnTypeReference ? 'reference' : 'literal' },
170        },
171      ],
172      kind: isProperty ? TypeDocKind.Property : TypeDocKind.Function,
173    },
174    { exposeInSidebar }
175  );
176};
177