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