1import { css } from '@emotion/react'; 2import React from 'react'; 3 4import { InlineCode } from '~/components/base/code'; 5import { H4 } from '~/components/base/headings'; 6import { LI, UL } from '~/components/base/list'; 7import { P } from '~/components/base/paragraph'; 8import { H2, H3, H3Code } from '~/components/plugins/Headings'; 9import { 10 CommentTagData, 11 DefaultPropsDefinitionData, 12 PropData, 13 PropsDefinitionData, 14 TypeDefinitionData, 15} from '~/components/plugins/api/APIDataTypes'; 16import { 17 CommentTextBlock, 18 resolveTypeName, 19 STYLES_SECONDARY, 20} from '~/components/plugins/api/APISectionUtils'; 21 22export type APISectionPropsProps = { 23 data: PropsDefinitionData[]; 24 defaultProps?: DefaultPropsDefinitionData; 25 header?: string; 26}; 27 28const UNKNOWN_VALUE = '...'; 29 30const PROP_LIST_ELEMENT_STYLE = css` 31 padding: 0; 32`; 33 34const extractDefaultPropValue = ( 35 { comment, name }: PropData, 36 defaultProps?: DefaultPropsDefinitionData 37): string | undefined => { 38 const annotationDefault = comment?.tags?.filter((tag: CommentTagData) => tag.tag === 'default'); 39 if (annotationDefault?.length) { 40 return annotationDefault[0].text; 41 } 42 return defaultProps?.type?.declaration?.children?.filter( 43 (defaultProp: PropData) => defaultProp.name === name 44 )[0]?.defaultValue; 45}; 46 47const renderInheritedProp = (ip: TypeDefinitionData) => { 48 return ( 49 <LI key={`inherited-prop-${ip.name}-${ip.type}`}> 50 <InlineCode>{resolveTypeName(ip)}</InlineCode> 51 </LI> 52 ); 53}; 54 55const renderInheritedProps = ( 56 data: TypeDefinitionData[] | undefined, 57 exposeInSidebar?: boolean 58): JSX.Element | undefined => { 59 const inheritedProps = data?.filter((ip: TypeDefinitionData) => ip.type === 'reference') ?? []; 60 if (inheritedProps.length) { 61 return ( 62 <> 63 {exposeInSidebar ? <H3>Inherited Props</H3> : <H4>Inherited Props</H4>} 64 <UL>{inheritedProps.map(renderInheritedProp)}</UL> 65 </> 66 ); 67 } 68 return undefined; 69}; 70 71const renderProps = ( 72 { name, type }: PropsDefinitionData, 73 defaultValues?: DefaultPropsDefinitionData, 74 exposeInSidebar?: boolean 75): JSX.Element => { 76 const baseTypes = type.types 77 ? type.types?.filter((t: TypeDefinitionData) => t.declaration) 78 : [type]; 79 const propsDeclarations = baseTypes 80 .map(def => def?.declaration?.children) 81 .flat() 82 .filter((dec, i, arr) => arr.findIndex(t => t?.name === dec?.name) === i); 83 84 return ( 85 <div key={`props-definition-${name}`}> 86 <UL> 87 {propsDeclarations?.map(prop => 88 prop 89 ? renderProp(prop, extractDefaultPropValue(prop, defaultValues), exposeInSidebar) 90 : null 91 )} 92 </UL> 93 {renderInheritedProps(type.types, exposeInSidebar)} 94 </div> 95 ); 96}; 97 98const renderProp = ( 99 { comment, name, type, flags }: PropData, 100 defaultValue?: string, 101 exposeInSidebar?: boolean 102) => ( 103 <LI key={`prop-entry-${name}`} customCss={exposeInSidebar ? PROP_LIST_ELEMENT_STYLE : undefined}> 104 {exposeInSidebar ? <H3>{name}</H3> : <H4>{name}</H4>} 105 <P> 106 {flags?.isOptional && <span css={STYLES_SECONDARY}>Optional • </span>} 107 <span css={STYLES_SECONDARY}>Type:</span> <InlineCode>{resolveTypeName(type)}</InlineCode> 108 {defaultValue && defaultValue !== UNKNOWN_VALUE ? ( 109 <span> 110 <span css={STYLES_SECONDARY}> • Default:</span>{' '} 111 <InlineCode>{defaultValue}</InlineCode> 112 </span> 113 ) : null} 114 </P> 115 <CommentTextBlock comment={comment} /> 116 </LI> 117); 118 119const APISectionProps: React.FC<APISectionPropsProps> = ({ 120 data, 121 defaultProps, 122 header = 'Props', 123}) => 124 data?.length ? ( 125 <> 126 {header === 'Props' ? ( 127 <H2 key="props-header">{header}</H2> 128 ) : ( 129 <> 130 <H3Code key={`${header}-props-header`}> 131 <InlineCode>{header}</InlineCode> 132 </H3Code> 133 <br /> 134 </> 135 )} 136 {data.map((propsDefinition: PropsDefinitionData) => 137 renderProps(propsDefinition, defaultProps, header === 'Props') 138 )} 139 </> 140 ) : null; 141 142export default APISectionProps; 143