1import { css } from '@emotion/core'; 2import MDX from '@mdx-js/runtime'; 3import * as React from 'react'; 4 5import * as components from '~/common/translate-markdown'; 6import { expoColors } from '~/constants/theme'; 7 8const STYLES_TABLE = css` 9 font-size: 1rem; 10 margin-top: 24px; 11`; 12 13const STYLES_HEAD = css` 14 background-color: ${expoColors.gray[100]}; 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 55export function formatSchema(rawSchema: [string, Property][]) { 56 const formattedSchema: FormattedProperty[] = []; 57 58 rawSchema.map(property => { 59 appendProperty(formattedSchema, property, 0); 60 }); 61 62 return formattedSchema; 63} 64 65//appends a property and recursivley appends sub-properties 66function appendProperty( 67 formattedSchema: FormattedProperty[], 68 property: [string, Property], 69 _nestingLevel: number 70) { 71 let nestingLevel = _nestingLevel; 72 const propertyKey = property[0]; 73 const propertyValue = property[1]; 74 75 if (propertyValue.meta && (propertyValue.meta.deprecated || propertyValue.meta.hidden)) { 76 return; 77 } 78 79 formattedSchema.push({ 80 name: nestingLevel 81 ? `<subpropertyAnchor level={${nestingLevel}}><inlineCode>${propertyKey}</inlineCode></subpropertyAnchor>` 82 : `<propertyAnchor level={0}><inlineCode>${propertyKey}</inlineCode></propertyAnchor>`, 83 description: createDescription(property), 84 nestingLevel, 85 }); 86 87 nestingLevel++; 88 89 if (propertyValue.properties) { 90 Object.entries(propertyValue.properties).forEach(subproperty => { 91 appendProperty(formattedSchema, subproperty, nestingLevel); 92 }); 93 } //Note: sub-properties are sometimes nested within "items" 94 else if (propertyValue.items && propertyValue.items.properties) { 95 Object.entries(propertyValue.items.properties).forEach(subproperty => { 96 appendProperty(formattedSchema, subproperty, nestingLevel); 97 }); 98 } 99} 100 101export function _getType(propertyValue: Property) { 102 if (propertyValue.enum) { 103 return 'enum'; 104 } else { 105 return propertyValue.type?.toString().replace(',', ' || '); 106 } 107} 108 109export function createDescription(propertyEntry: [string, Property]) { 110 const propertyValue = propertyEntry[1]; 111 112 let propertyDescription = `**(${_getType(propertyValue)})**`; 113 if (propertyValue.description) { 114 propertyDescription += ` - ` + propertyValue.description; 115 } 116 if (propertyValue.meta && propertyValue.meta.regexHuman) { 117 propertyDescription += `\n\n` + propertyValue.meta.regexHuman; 118 } 119 if (propertyValue.meta && propertyValue.meta.expoKit) { 120 propertyDescription += `<expokitDetails>${propertyValue.meta.expoKit}</expokitDetails>`; 121 } 122 if (propertyValue.meta && propertyValue.meta.bareWorkflow) { 123 propertyDescription += `<bareworkflowDetails>${propertyValue.meta.bareWorkflow}</bareworkflowDetails>`; 124 } 125 if (propertyValue.exampleString) { 126 propertyDescription += `\n\n>` + propertyValue.exampleString; 127 } 128 129 return propertyDescription; 130} 131 132export default class AppConfigSchemaPropertiesTable extends React.Component<{ 133 schema: Record<string, Property>; 134}> { 135 render() { 136 const rawSchema = Object.entries(this.props.schema); 137 const formattedSchema = formatSchema(rawSchema); 138 139 return ( 140 <table css={STYLES_TABLE}> 141 <thead css={STYLES_HEAD}> 142 <tr> 143 <td>Property</td> 144 <td>Description</td> 145 </tr> 146 </thead> 147 <tbody> 148 {formattedSchema.map((property, index) => { 149 return ( 150 <tr key={index}> 151 <td> 152 <div 153 data-testid={property.name} 154 style={{ 155 marginLeft: `${property.nestingLevel * 32}px`, 156 display: property.nestingLevel ? 'list-item' : 'block', 157 listStyleType: property.nestingLevel % 2 ? 'default' : 'circle', 158 width: 'fit-content', 159 overflowX: 'visible', 160 }}> 161 <MDX components={components}>{property.name}</MDX> 162 </div> 163 </td> 164 <td css={STYLES_DESCRIPTION_CELL}> 165 <MDX components={components}>{property.description}</MDX> 166 </td> 167 </tr> 168 ); 169 })} 170 </tbody> 171 </table> 172 ); 173 } 174} 175