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