1import { css } from '@emotion/react';
2import { shadows, theme, typography } from '@expo/styleguide';
3import { borderRadius, breakpoints, spacing } from '@expo/styleguide-base';
4import type { ComponentProps, ComponentType } from 'react';
5import { Fragment } from 'react';
6import ReactMarkdown from 'react-markdown';
7import remarkGfm from 'remark-gfm';
8
9import { APIDataType } from './APIDataType';
10
11import { HeadingType } from '~/common/headingManager';
12import { Code as PrismCodeBlock } from '~/components/base/code';
13import {
14  CommentContentData,
15  CommentData,
16  MethodDefinitionData,
17  MethodParamData,
18  MethodSignatureData,
19  PropData,
20  TypeDefinitionData,
21  TypePropertyDataFlags,
22  TypeSignaturesData,
23} from '~/components/plugins/api/APIDataTypes';
24import { APISectionPlatformTags } from '~/components/plugins/api/APISectionPlatformTags';
25import { Callout } from '~/ui/components/Callout';
26import { Cell, HeaderCell, Row, Table, TableHead } from '~/ui/components/Table';
27import { tableWrapperStyle } from '~/ui/components/Table/Table';
28import { Tag } from '~/ui/components/Tag';
29import {
30  A,
31  BOLD,
32  CODE,
33  H4,
34  LI,
35  OL,
36  P,
37  RawH3,
38  RawH4,
39  UL,
40  createPermalinkedComponent,
41  DEMI,
42  CALLOUT,
43  createTextComponent,
44} from '~/ui/components/Text';
45import { TextElement } from '~/ui/components/Text/types';
46
47const isDev = process.env.NODE_ENV === 'development';
48
49export enum TypeDocKind {
50  Namespace = 4,
51  Enum = 8,
52  Variable = 32,
53  Function = 64,
54  Class = 128,
55  Interface = 256,
56  Property = 1024,
57  Method = 2048,
58  Parameter = 32768,
59  Accessor = 262144,
60  TypeAlias = 4194304,
61}
62
63export type MDComponents = ComponentProps<typeof ReactMarkdown>['components'];
64
65const getInvalidLinkMessage = (href: string) =>
66  `Using "../" when linking other packages in doc comments produce a broken link! Please use "./" instead. Problematic link:\n\t${href}`;
67
68export const mdComponents: MDComponents = {
69  blockquote: ({ children }) => <Callout>{children}</Callout>,
70  code: ({ children, className }) =>
71    className ? (
72      <PrismCodeBlock className={className}>{children}</PrismCodeBlock>
73    ) : (
74      <CODE css={css({ display: 'inline' })}>{children}</CODE>
75    ),
76  h1: ({ children }) => <H4>{children}</H4>,
77  ul: ({ children }) => <UL className={ELEMENT_SPACING}>{children}</UL>,
78  ol: ({ children }) => <OL className={ELEMENT_SPACING}>{children}</OL>,
79  li: ({ children }) => <LI>{children}</LI>,
80  a: ({ href, children }) => {
81    if (
82      href?.startsWith('../') &&
83      !href?.startsWith('../..') &&
84      !href?.startsWith('../react-native')
85    ) {
86      if (isDev) {
87        throw new Error(getInvalidLinkMessage(href));
88      } else {
89        console.warn(getInvalidLinkMessage(href));
90      }
91    }
92    return <A href={href}>{children}</A>;
93  },
94  p: ({ children }) => (children ? <P className={ELEMENT_SPACING}>{children}</P> : null),
95  strong: ({ children }) => <BOLD>{children}</BOLD>,
96  span: ({ children }) => (children ? <span>{children}</span> : null),
97  table: ({ children }) => <Table>{children}</Table>,
98  thead: ({ children }) => <TableHead>{children}</TableHead>,
99  tr: ({ children }) => <Row>{children}</Row>,
100  th: ({ children }) => <HeaderCell>{children}</HeaderCell>,
101  td: ({ children }) => <Cell>{children}</Cell>,
102};
103
104export const mdComponentsNoValidation: MDComponents = {
105  ...mdComponents,
106  a: ({ href, children }) => <A href={href}>{children}</A>,
107};
108
109const nonLinkableTypes = [
110  'ColorValue',
111  'Component',
112  'ComponentClass',
113  'PureComponent',
114  'E',
115  'EventSubscription',
116  'Listener',
117  'NativeSyntheticEvent',
118  'ParsedQs',
119  'ServiceActionResult',
120  'T',
121  'TaskOptions',
122  'Uint8Array',
123  // React & React Native
124  'React.FC',
125  'ForwardRefExoticComponent',
126  'StyleProp',
127  'HTMLInputElement',
128  // Cross-package permissions management
129  'RequestPermissionMethod',
130  'GetPermissionMethod',
131  'Options',
132  'PermissionHookBehavior',
133];
134
135/**
136 * List of type names that should not be visible in the docs.
137 */
138const omittableTypes = [
139  // Internal React type that adds `ref` prop to the component
140  'RefAttributes',
141];
142
143/**
144 * Map of internal names/type names that should be replaced with something more developer-friendly.
145 */
146const replaceableTypes: Partial<Record<string, string>> = {
147  ForwardRefExoticComponent: 'Component',
148};
149
150const hardcodedTypeLinks: Record<string, string> = {
151  AVPlaybackSource: '/versions/latest/sdk/av/#avplaybacksource',
152  AVPlaybackStatus: '/versions/latest/sdk/av/#avplaybackstatus',
153  AVPlaybackStatusToSet: '/versions/latest/sdk/av/#avplaybackstatustoset',
154  Blob: 'https://developer.mozilla.org/en-US/docs/Web/API/Blob',
155  Date: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date',
156  DeviceSensor: '/versions/latest/sdk/sensors',
157  Element: 'https://www.typescriptlang.org/docs/handbook/jsx.html#function-component',
158  Error: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error',
159  ExpoConfig:
160    'https://github.com/expo/expo/blob/main/packages/%40expo/config-types/src/ExpoConfig.ts',
161  File: 'https://developer.mozilla.org/en-US/docs/Web/API/File',
162  FileList: 'https://developer.mozilla.org/en-US/docs/Web/API/FileList',
163  Manifest: '/versions/latest/sdk/constants/#manifest',
164  MediaTrackSettings: 'https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings',
165  MessageEvent: 'https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent',
166  Omit: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys',
167  Pick: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys',
168  Partial: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype',
169  Promise:
170    'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise',
171  SyntheticEvent:
172    'https://beta.reactjs.org/reference/react-dom/components/common#react-event-object',
173  View: 'https://reactnative.dev/docs/view',
174  ViewProps: 'https://reactnative.dev/docs/view#props',
175  ViewStyle: 'https://reactnative.dev/docs/view-style-props',
176  WebGL2RenderingContext: 'https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext',
177  WebGLFramebuffer: 'https://developer.mozilla.org/en-US/docs/Web/API/WebGLFramebuffer',
178};
179
180const renderWithLink = (name: string, type?: string) => {
181  const replacedName = replaceableTypes[name] ?? name;
182
183  if (name.includes('.')) return name;
184
185  return nonLinkableTypes.includes(replacedName) ? (
186    replacedName + (type === 'array' ? '[]' : '')
187  ) : (
188    <A
189      href={hardcodedTypeLinks[replacedName] || `#${replacedName.toLowerCase()}`}
190      key={`type-link-${replacedName}`}>
191      {replacedName}
192      {type === 'array' && '[]'}
193    </A>
194  );
195};
196
197const renderUnion = (types: TypeDefinitionData[]) =>
198  types
199    .map(type => resolveTypeName(type))
200    .map((valueToRender, index) => (
201      <span key={`union-type-${index}`}>
202        {valueToRender}
203        {index + 1 !== types.length && ' | '}
204      </span>
205    ));
206
207export const resolveTypeName = (
208  typeDefinition: TypeDefinitionData
209): string | JSX.Element | (string | JSX.Element)[] => {
210  if (!typeDefinition) {
211    return 'undefined';
212  }
213
214  const {
215    elements,
216    elementType,
217    name,
218    type,
219    types,
220    typeArguments,
221    declaration,
222    value,
223    queryType,
224    operator,
225    objectType,
226    indexType,
227  } = typeDefinition;
228
229  try {
230    if (name) {
231      if (type === 'reference') {
232        if (typeArguments) {
233          if (name === 'Record' || name === 'React.ComponentProps') {
234            return (
235              <>
236                {name}&lt;
237                {typeArguments.map((type, index) => (
238                  <span key={`record-type-${index}`}>
239                    {resolveTypeName(type)}
240                    {index !== typeArguments.length - 1 ? ', ' : null}
241                  </span>
242                ))}
243                &gt;
244              </>
245            );
246          } else {
247            return (
248              <>
249                {renderWithLink(name)}
250                &lt;
251                {typeArguments.map((type, index) => (
252                  <span key={`${name}-nested-type-${index}`}>
253                    {resolveTypeName(type)}
254                    {index !== typeArguments.length - 1 ? ', ' : null}
255                  </span>
256                ))}
257                &gt;
258              </>
259            );
260          }
261        } else {
262          return renderWithLink(name);
263        }
264      } else {
265        return name;
266      }
267    } else if (elementType?.name) {
268      if (elementType.type === 'reference') {
269        return renderWithLink(elementType.name, type);
270      } else if (type === 'array') {
271        return elementType.name + '[]';
272      }
273      return elementType.name + type;
274    } else if (elementType?.declaration) {
275      if (type === 'array') {
276        const { parameters, type: paramType } = elementType.declaration.indexSignature || {};
277        if (parameters && paramType) {
278          return `{ [${listParams(parameters)}]: ${resolveTypeName(paramType)} }`;
279        }
280      }
281      return elementType.name + type;
282    } else if (type === 'union' && types?.length) {
283      return renderUnion(types);
284    } else if (elementType && elementType.type === 'union' && elementType?.types?.length) {
285      const unionTypes = elementType?.types || [];
286      return (
287        <>
288          ({renderUnion(unionTypes)}){type === 'array' && '[]'}
289        </>
290      );
291    } else if (declaration?.signatures) {
292      const baseSignature = declaration.signatures[0];
293      if (baseSignature?.parameters?.length) {
294        return (
295          <>
296            (
297            {baseSignature.parameters?.map((param, index) => (
298              <span key={`param-${index}-${param.name}`}>
299                {param.name}: {resolveTypeName(param.type)}
300                {index + 1 !== baseSignature.parameters?.length && ', '}
301              </span>
302            ))}
303            ) {'=>'} {resolveTypeName(baseSignature.type)}
304          </>
305        );
306      } else {
307        return (
308          <>
309            {'() =>'} {resolveTypeName(baseSignature.type)}
310          </>
311        );
312      }
313    } else if (type === 'reflection' && declaration?.children) {
314      return (
315        <>
316          {'{\n'}
317          {declaration?.children.map((child: PropData, i) => (
318            <span key={`reflection-${name}-${i}`}>
319              {'  '}
320              {child.name + ': '}
321              {resolveTypeName(child.type)}
322              {i + 1 !== declaration?.children?.length ? ', ' : null}
323              {'\n'}
324            </span>
325          ))}
326          {'}'}
327        </>
328      );
329    } else if (type === 'tuple' && elements) {
330      return (
331        <>
332          [
333          {elements.map((elem, i) => (
334            <span key={`tuple-${name}-${i}`}>
335              {resolveTypeName(elem)}
336              {i + 1 !== elements.length ? ', ' : null}
337            </span>
338          ))}
339          ]
340        </>
341      );
342    } else if (type === 'query' && queryType) {
343      return queryType.name;
344    } else if (type === 'literal' && typeof value === 'boolean') {
345      return `${value}`;
346    } else if (type === 'literal' && (value || (typeof value === 'number' && value === 0))) {
347      return `'${value}'`;
348    } else if (type === 'intersection' && types) {
349      return types
350        .filter(({ name }) => !omittableTypes.includes(name ?? ''))
351        .map((value, index, array) => (
352          <span key={`intersection-${name}-${index}`}>
353            {resolveTypeName(value)}
354            {index + 1 !== array.length && ' & '}
355          </span>
356        ));
357    } else if (type === 'indexedAccess') {
358      return `${objectType?.name}['${indexType?.value}']`;
359    } else if (type === 'typeOperator') {
360      return operator || 'undefined';
361    } else if (type === 'intrinsic') {
362      return name || 'undefined';
363    } else if (value === null) {
364      return 'null';
365    }
366    return 'undefined';
367  } catch (e) {
368    console.warn('Type resolve has failed!', e);
369    return 'undefined';
370  }
371};
372
373export const parseParamName = (name: string) => (name.startsWith('__') ? name.substr(2) : name);
374
375export const renderParamRow = ({
376  comment,
377  name,
378  type,
379  flags,
380  defaultValue,
381}: MethodParamData): JSX.Element => {
382  const defaultData = getTagData('default', comment);
383  const initValue = parseCommentContent(
384    defaultValue || (defaultData ? getCommentContent(defaultData.content) : '')
385  );
386  return (
387    <Row key={`param-${name}`}>
388      <Cell>
389        <BOLD>{parseParamName(name)}</BOLD>
390        {renderFlags(flags, initValue)}
391      </Cell>
392      <Cell>
393        <APIDataType typeDefinition={type} />
394      </Cell>
395      <Cell>
396        <CommentTextBlock
397          comment={comment}
398          afterContent={renderDefaultValue(initValue)}
399          emptyCommentFallback="-"
400        />
401      </Cell>
402    </Row>
403  );
404};
405
406export const ParamsTableHeadRow = () => (
407  <TableHead>
408    <Row>
409      <HeaderCell>Name</HeaderCell>
410      <HeaderCell>Type</HeaderCell>
411      <HeaderCell>Description</HeaderCell>
412    </Row>
413  </TableHead>
414);
415
416const InheritPermalink = createPermalinkedComponent(
417  createTextComponent(
418    TextElement.SPAN,
419    css({ fontSize: 'inherit', fontWeight: 'inherit', color: 'inherit' })
420  ),
421  { baseNestingLevel: 2 }
422);
423
424export const BoxSectionHeader = ({
425  text,
426  exposeInSidebar,
427}: {
428  text: string;
429  exposeInSidebar?: boolean;
430}) => {
431  const TextWrapper = exposeInSidebar ? InheritPermalink : Fragment;
432  return (
433    <CALLOUT theme="secondary" weight="medium" css={STYLES_NESTED_SECTION_HEADER}>
434      <TextWrapper>{text}</TextWrapper>
435    </CALLOUT>
436  );
437};
438
439export const renderParams = (parameters: MethodParamData[]) => (
440  <Table>
441    <ParamsTableHeadRow />
442    <tbody>{parameters?.map(renderParamRow)}</tbody>
443  </Table>
444);
445
446export const listParams = (parameters: MethodParamData[]) =>
447  parameters ? parameters?.map(param => parseParamName(param.name)).join(', ') : '';
448
449export const renderDefaultValue = (defaultValue?: string) =>
450  defaultValue && defaultValue !== '...' ? (
451    <div css={defaultValueContainerStyle}>
452      <DEMI>Default:</DEMI> <CODE>{defaultValue}</CODE>
453    </div>
454  ) : undefined;
455
456export const renderTypeOrSignatureType = (
457  type?: TypeDefinitionData,
458  signatures?: MethodSignatureData[] | TypeSignaturesData[],
459  allowBlock: boolean = false
460) => {
461  if (signatures && signatures.length) {
462    return (
463      <CODE key={`signature-type-${signatures[0].name}`}>
464        (
465        {signatures?.map(({ parameters }) =>
466          parameters?.map(param => (
467            <span key={`signature-param-${param.name}`}>
468              {param.name}
469              {param.flags?.isOptional && '?'}: {resolveTypeName(param.type)}
470            </span>
471          ))
472        )}
473        ) =&gt; {signatures[0].type ? resolveTypeName(signatures[0].type) : 'void'}
474      </CODE>
475    );
476  } else if (type) {
477    if (allowBlock) {
478      return <APIDataType typeDefinition={type} />;
479    }
480    return <CODE key={`signature-type-${type.name}`}>{resolveTypeName(type)}</CODE>;
481  }
482  return undefined;
483};
484
485export const renderFlags = (flags?: TypePropertyDataFlags, defaultValue?: string) =>
486  (flags?.isOptional || defaultValue) && (
487    <>
488      <br />
489      <span css={STYLES_OPTIONAL}>(optional)</span>
490    </>
491  );
492
493export const renderIndexSignature = (kind: TypeDocKind) =>
494  kind === TypeDocKind.Parameter && (
495    <>
496      <br />
497      <A
498        css={STYLES_OPTIONAL}
499        href="https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures"
500        openInNewTab
501        isStyled>
502        (index signature)
503      </A>
504    </>
505  );
506
507export type CommentTextBlockProps = {
508  comment?: CommentData;
509  components?: MDComponents;
510  beforeContent?: JSX.Element;
511  afterContent?: JSX.Element;
512  includePlatforms?: boolean;
513  inlineHeaders?: boolean;
514  emptyCommentFallback?: string;
515};
516
517export const parseCommentContent = (content?: string): string =>
518  content && content.length ? content.replace(/&ast;/g, '*').replace(/\t/g, '') : '';
519
520export const getCommentOrSignatureComment = (
521  comment?: CommentData,
522  signatures?: MethodSignatureData[] | TypeSignaturesData[]
523) => comment || (signatures && signatures[0]?.comment);
524
525export const getTagData = (tagName: string, comment?: CommentData) =>
526  getAllTagData(tagName, comment)?.[0];
527
528export const getAllTagData = (tagName: string, comment?: CommentData) =>
529  comment?.blockTags?.filter(tag => tag.tag.substring(1) === tagName);
530
531export const getTagNamesList = (comment?: CommentData) =>
532  comment && [
533    ...(getAllTagData('platform', comment)?.map(platformData =>
534      getCommentContent(platformData.content)
535    ) || []),
536    ...(getTagData('deprecated', comment) ? ['deprecated'] : []),
537    ...(getTagData('experimental', comment) ? ['experimental'] : []),
538  ];
539
540export const getMethodName = (
541  method: MethodDefinitionData,
542  apiName?: string,
543  name?: string,
544  parameters?: MethodParamData[]
545) => {
546  const isProperty = method.kind === TypeDocKind.Property && !parameters?.length;
547  const methodName = ((apiName && `${apiName}.`) ?? '') + (method.name || name);
548  if (!isProperty) {
549    return `${methodName}(${parameters ? listParams(parameters) : ''})`;
550  }
551
552  return methodName;
553};
554
555export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
556
557const PARAM_TAGS_REGEX = /@tag-\S*/g;
558
559const getParamTags = (shortText?: string) => {
560  if (!shortText || !shortText.includes('@tag-')) {
561    return undefined;
562  }
563  return Array.from(shortText.matchAll(PARAM_TAGS_REGEX), match => match[0]);
564};
565
566export const getCommentContent = (content: CommentContentData[]) => {
567  return content
568    .map(entry => entry.text)
569    .join('')
570    .trim();
571};
572
573export const CommentTextBlock = ({
574  comment,
575  beforeContent,
576  afterContent,
577  includePlatforms = true,
578  inlineHeaders = false,
579  emptyCommentFallback,
580}: CommentTextBlockProps) => {
581  const content = comment && comment.summary ? getCommentContent(comment.summary) : undefined;
582
583  if (emptyCommentFallback && (!comment || !content || !content.length)) {
584    return <>{emptyCommentFallback}</>;
585  }
586
587  const paramTags = content ? getParamTags(content) : undefined;
588  const parsedContent = (
589    <ReactMarkdown components={mdComponents} remarkPlugins={[remarkGfm]}>
590      {parseCommentContent(paramTags ? content?.replaceAll(PARAM_TAGS_REGEX, '') : content)}
591    </ReactMarkdown>
592  );
593
594  const examples = getAllTagData('example', comment);
595  const exampleText = examples?.map((example, index) => (
596    <Fragment key={'example-' + index}>
597      {inlineHeaders ? (
598        <div css={STYLES_EXAMPLE_IN_TABLE}>
599          <BOLD>Example</BOLD>
600        </div>
601      ) : (
602        <BoxSectionHeader text="Example" />
603      )}
604      <ReactMarkdown components={mdComponents}>{getCommentContent(example.content)}</ReactMarkdown>
605    </Fragment>
606  ));
607
608  const see = getTagData('see', comment);
609  const seeText = see && (
610    <Callout>
611      <ReactMarkdown components={mdComponents}>
612        {`**See:** ` + getCommentContent(see.content)}
613      </ReactMarkdown>
614    </Callout>
615  );
616
617  const hasPlatforms = (getAllTagData('platform', comment)?.length || 0) > 0;
618
619  return (
620    <>
621      {includePlatforms && hasPlatforms && (
622        <APISectionPlatformTags comment={comment} prefix="Only for:" />
623      )}
624      {paramTags && (
625        <>
626          <BOLD>Only for:&ensp;</BOLD>
627          {paramTags.map(tag => (
628            <Tag key={tag} name={tag.split('-')[1]} />
629          ))}
630        </>
631      )}
632      {beforeContent}
633      {parsedContent}
634      {afterContent}
635      {seeText}
636      {exampleText}
637    </>
638  );
639};
640
641const getMonospaceHeader = (element: ComponentType<any>) => {
642  const level = parseInt(element?.displayName?.replace(/\D/g, '') ?? '0', 10);
643  return createPermalinkedComponent(element, {
644    baseNestingLevel: level !== 0 ? level : undefined,
645    sidebarType: HeadingType.InlineCode,
646  });
647};
648
649export const H3Code = getMonospaceHeader(RawH3);
650export const H4Code = getMonospaceHeader(RawH4);
651
652export const getComponentName = (name?: string, children: PropData[] = []) => {
653  if (name && name !== 'default') return name;
654  const ctor = children.filter((child: PropData) => child.name === 'constructor')[0];
655  return ctor?.signatures?.[0]?.type?.name ?? 'default';
656};
657
658export const STYLES_APIBOX = css({
659  borderRadius: borderRadius.md,
660  borderWidth: 1,
661  borderStyle: 'solid',
662  borderColor: theme.border.default,
663  padding: spacing[5],
664  boxShadow: shadows.xs,
665  marginBottom: spacing[6],
666  overflowX: 'hidden',
667
668  h3: {
669    marginBottom: spacing[2.5],
670  },
671
672  'h2, h3, h4': {
673    marginTop: 0,
674  },
675
676  th: {
677    color: theme.text.secondary,
678    padding: `${spacing[3]}px ${spacing[4]}px`,
679  },
680
681  li: {
682    marginBottom: 0,
683  },
684
685  [`.css-${tableWrapperStyle.name}`]: {
686    boxShadow: 'none',
687    marginBottom: 0,
688  },
689
690  [`@media screen and (max-width: ${breakpoints.medium + 124}px)`]: {
691    paddingInline: spacing[4],
692  },
693});
694
695export const STYLES_APIBOX_NESTED = css({
696  boxShadow: 'none',
697  marginBottom: spacing[4],
698  padding: `${spacing[4]}px ${spacing[5]}px 0`,
699
700  h4: {
701    marginTop: 0,
702  },
703});
704
705export const STYLES_APIBOX_WRAPPER = css({
706  marginBottom: spacing[4],
707  padding: `${spacing[4]}px ${spacing[5]}px 0`,
708
709  [`.css-${tableWrapperStyle.name}:last-child`]: {
710    marginBottom: spacing[4],
711  },
712});
713
714export const STYLE_APIBOX_NO_SPACING = css({ marginBottom: -spacing[5] });
715
716export const STYLES_NESTED_SECTION_HEADER = css({
717  display: 'flex',
718  borderTop: `1px solid ${theme.border.default}`,
719  borderBottom: `1px solid ${theme.border.default}`,
720  margin: `${spacing[4]}px -${spacing[5]}px ${spacing[4]}px`,
721  padding: `${spacing[2.5]}px ${spacing[5]}px`,
722  backgroundColor: theme.background.subtle,
723
724  h4: {
725    ...typography.fontSizes[16],
726    fontWeight: 600,
727    marginBottom: 0,
728    marginTop: 0,
729    color: theme.text.secondary,
730  },
731});
732
733export const STYLES_NOT_EXPOSED_HEADER = css({
734  marginBottom: spacing[1],
735  display: 'inline-block',
736
737  code: {
738    marginBottom: 0,
739  },
740});
741
742export const STYLES_OPTIONAL = css({
743  color: theme.text.secondary,
744  fontSize: '90%',
745  paddingTop: 22,
746});
747
748export const STYLES_SECONDARY = css({
749  color: theme.text.secondary,
750  fontSize: '90%',
751  fontWeight: 600,
752});
753
754const defaultValueContainerStyle = css({
755  marginTop: spacing[2],
756  marginBottom: spacing[2],
757
758  '&:last-child': {
759    marginBottom: 0,
760  },
761});
762
763const STYLES_EXAMPLE_IN_TABLE = css({
764  margin: `${spacing[2]}px 0`,
765});
766
767export const ELEMENT_SPACING = 'mb-4';
768