1import { css } from '@emotion/react'; 2import { theme } from '@expo/styleguide'; 3import MDX from '@mdx-js/runtime'; 4import * as React from 'react'; 5 6import * as components from '~/common/translate-markdown'; 7 8const STYLES_TABLE = css` 9 font-size: 1rem; 10 margin-top: 24px; 11`; 12 13const STYLES_HEAD = css` 14 background-color: ${theme.background.tertiary}; 15`; 16 17const STYLES_DESCRIPTION_CELL = css` 18 word-break: break-word; 19 white-space: break-spaces; 20 padding-bottom: 0.2rem; 21`; 22 23type PropertyMeta = { 24 regexHuman?: string; 25 deprecated?: boolean; 26 hidden?: boolean; 27 expoKit?: string; 28 bareWorkflow?: string; 29}; 30 31export type Property = { 32 description?: string; 33 type?: string | string[]; 34 meta?: PropertyMeta; 35 pattern?: string; 36 enum?: string[]; 37 example?: any; 38 exampleString?: string; 39 host?: object; 40 properties?: Record<string, Property>; 41 items?: { 42 properties?: Record<string, Property>; 43 [key: string]: any; 44 }; 45 uniqueItems?: boolean; 46 additionalProperties?: boolean; 47}; 48 49type FormattedProperty = { 50 name: string; 51 description: string; 52 nestingLevel: number; 53}; 54 55type AppConfigSchemaProps = { 56 schema: Record<string, Property>; 57}; 58 59export function formatSchema(rawSchema: [string, Property][]) { 60 const formattedSchema: FormattedProperty[] = []; 61 62 rawSchema.map(property => { 63 appendProperty(formattedSchema, property, 0); 64 }); 65 66 return formattedSchema; 67} 68 69//appends a property and recursivley appends sub-properties 70function appendProperty( 71 formattedSchema: FormattedProperty[], 72 property: [string, Property], 73 _nestingLevel: number 74) { 75 let nestingLevel = _nestingLevel; 76 const propertyKey = property[0]; 77 const propertyValue = property[1]; 78 79 if (propertyValue.meta && (propertyValue.meta.deprecated || propertyValue.meta.hidden)) { 80 return; 81 } 82 83 formattedSchema.push({ 84 name: nestingLevel 85 ? `<subpropertyAnchor level={${nestingLevel}}><inlineCode>${propertyKey}</inlineCode></subpropertyAnchor>` 86 : `<propertyAnchor level={0}><inlineCode>${propertyKey}</inlineCode></propertyAnchor>`, 87 description: createDescription(property), 88 nestingLevel, 89 }); 90 91 nestingLevel++; 92 93 if (propertyValue.properties) { 94 Object.entries(propertyValue.properties).forEach(subproperty => { 95 appendProperty(formattedSchema, subproperty, nestingLevel); 96 }); 97 } //Note: sub-properties are sometimes nested within "items" 98 else if (propertyValue.items && propertyValue.items.properties) { 99 Object.entries(propertyValue.items.properties).forEach(subproperty => { 100 appendProperty(formattedSchema, subproperty, nestingLevel); 101 }); 102 } 103} 104 105export function _getType(propertyValue: Property) { 106 if (propertyValue.enum) { 107 return 'enum'; 108 } else { 109 return propertyValue.type?.toString().replace(',', ' || '); 110 } 111} 112 113export function createDescription(propertyEntry: [string, Property]) { 114 const propertyValue = propertyEntry[1]; 115 116 let propertyDescription = `**(${_getType(propertyValue)})**`; 117 if (propertyValue.description) { 118 propertyDescription += ` - ` + propertyValue.description; 119 } 120 if (propertyValue.meta && propertyValue.meta.regexHuman) { 121 propertyDescription += `\n\n` + propertyValue.meta.regexHuman; 122 } 123 if (propertyValue.meta && propertyValue.meta.expoKit) { 124 propertyDescription += `<expokitDetails>${propertyValue.meta.expoKit}</expokitDetails>`; 125 } 126 if (propertyValue.meta && propertyValue.meta.bareWorkflow) { 127 propertyDescription += `<bareworkflowDetails>${propertyValue.meta.bareWorkflow}</bareworkflowDetails>`; 128 } 129 if (propertyValue.exampleString) { 130 propertyDescription += `\n\n>` + propertyValue.exampleString; 131 } 132 133 return propertyDescription; 134} 135 136const AppConfigSchemaPropertiesTable = ({ schema }: AppConfigSchemaProps) => { 137 const rawSchema = Object.entries(schema); 138 const formattedSchema = formatSchema(rawSchema); 139 140 return ( 141 <table css={STYLES_TABLE}> 142 <thead css={STYLES_HEAD}> 143 <tr> 144 <td>Property</td> 145 <td>Description</td> 146 </tr> 147 </thead> 148 <tbody> 149 {formattedSchema.map(({ name, description, nestingLevel }, index) => ( 150 <tr key={index}> 151 <td> 152 <div 153 data-testid={name} 154 style={{ 155 marginLeft: `${nestingLevel * 32}px`, 156 display: nestingLevel ? 'list-item' : 'block', 157 listStyleType: nestingLevel % 2 ? 'default' : 'circle', 158 width: 'fit-content', 159 overflowX: 'visible', 160 }}> 161 <MDX components={components}>{name}</MDX> 162 </div> 163 </td> 164 <td css={STYLES_DESCRIPTION_CELL}> 165 <MDX components={components}>{description}</MDX> 166 </td> 167 </tr> 168 ))} 169 </tbody> 170 </table> 171 ); 172}; 173 174export default AppConfigSchemaPropertiesTable; 175