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, H3, H3Code, H4, H4Code } from '~/components/plugins/Headings'; 7import { 8 DefaultPropsDefinitionData, 9 PropData, 10 PropsDefinitionData, 11 TypeDefinitionData, 12} from '~/components/plugins/api/APIDataTypes'; 13import { 14 CommentTextBlock, 15 getCommentOrSignatureComment, 16 getTagData, 17 renderTypeOrSignatureType, 18 resolveTypeName, 19 STYLES_APIBOX, 20 STYLES_APIBOX_NESTED, 21 STYLES_NESTED_SECTION_HEADER, 22 STYLES_NOT_EXPOSED_HEADER, 23 STYLES_SECONDARY, 24} from '~/components/plugins/api/APISectionUtils'; 25 26export type APISectionPropsProps = { 27 data: PropsDefinitionData[]; 28 defaultProps?: DefaultPropsDefinitionData; 29 header?: string; 30}; 31 32const UNKNOWN_VALUE = '...'; 33 34const extractDefaultPropValue = ( 35 { comment, name }: PropData, 36 defaultProps?: DefaultPropsDefinitionData 37): string | undefined => { 38 const annotationDefault = getTagData('default', comment); 39 if (annotationDefault) { 40 return annotationDefault.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 {propsDeclarations?.map(prop => 87 prop 88 ? renderProp(prop, extractDefaultPropValue(prop, defaultValues), exposeInSidebar) 89 : null 90 )} 91 {renderInheritedProps(type.types, exposeInSidebar)} 92 </div> 93 ); 94}; 95 96export const renderProp = ( 97 { comment, name, type, flags, signatures }: PropData, 98 defaultValue?: string, 99 exposeInSidebar?: boolean 100) => { 101 const HeaderComponent = exposeInSidebar ? H3Code : H4Code; 102 return ( 103 <div key={`prop-entry-${name}`} css={[STYLES_APIBOX, !exposeInSidebar && STYLES_APIBOX_NESTED]}> 104 <HeaderComponent> 105 <InlineCode customCss={!exposeInSidebar ? STYLES_NOT_EXPOSED_HEADER : undefined}> 106 {name} 107 </InlineCode> 108 </HeaderComponent> 109 <P> 110 {flags?.isOptional && <span css={STYLES_SECONDARY}>Optional • </span>} 111 <span css={STYLES_SECONDARY}>Type:</span> {renderTypeOrSignatureType(type, signatures)} 112 {defaultValue && defaultValue !== UNKNOWN_VALUE ? ( 113 <span> 114 <span css={STYLES_SECONDARY}> • Default:</span>{' '} 115 <InlineCode>{defaultValue}</InlineCode> 116 </span> 117 ) : null} 118 </P> 119 <CommentTextBlock comment={getCommentOrSignatureComment(comment, signatures)} /> 120 </div> 121 ); 122}; 123 124const APISectionProps = ({ data, defaultProps, header = 'Props' }: APISectionPropsProps) => { 125 const baseProp = data.find(prop => prop.name === header); 126 return data?.length ? ( 127 <> 128 {header === 'Props' ? ( 129 <H2 key="props-header">{header}</H2> 130 ) : ( 131 <div> 132 <div css={STYLES_NESTED_SECTION_HEADER}> 133 <H4 key={`${header}-props-header`}>{header}</H4> 134 </div> 135 {baseProp && baseProp.comment ? <CommentTextBlock comment={baseProp.comment} /> : null} 136 </div> 137 )} 138 {data.map((propsDefinition: PropsDefinitionData) => 139 renderProps(propsDefinition, defaultProps, header === 'Props') 140 )} 141 </> 142 ) : null; 143}; 144 145export default APISectionProps; 146