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