1import ReactMarkdown from 'react-markdown';
2
3import { HeadingType } from '~/common/headingManager';
4import { mdComponentsNoValidation } from '~/components/plugins/api/APISectionUtils';
5import { Cell, HeaderCell, Row, Table, TableHead } from '~/ui/components/Table';
6import { P, CODE, createPermalinkedComponent } from '~/ui/components/Text';
7
8export type Property = {
9  description?: string[];
10  name: string;
11  type?: string | string[];
12  enum?: string[];
13  properties?: Property[];
14};
15
16type FormattedProperty = {
17  name: string;
18  description: string;
19  nestingLevel: number;
20};
21
22type Props = {
23  schema: Property[];
24};
25
26const Anchor = createPermalinkedComponent(P, {
27  baseNestingLevel: 4,
28  sidebarType: HeadingType.InlineCode,
29});
30
31const PropertyName = ({ name, nestingLevel }: Pick<FormattedProperty, 'name' | 'nestingLevel'>) => (
32  <Anchor level={nestingLevel}>
33    <CODE>{name}</CODE>
34  </Anchor>
35);
36
37export function formatSchema(rawSchema: Property[]) {
38  const formattedSchema: FormattedProperty[] = [];
39
40  rawSchema.map(property => {
41    appendProperty(formattedSchema, property, 0);
42  });
43
44  return formattedSchema;
45}
46
47//appends a property and recursively appends sub-properties
48function appendProperty(
49  formattedSchema: FormattedProperty[],
50  property: Property,
51  _nestingLevel: number
52) {
53  let nestingLevel = _nestingLevel;
54
55  formattedSchema.push({
56    name: property.name,
57    description: createDescription(property),
58    nestingLevel,
59  });
60
61  nestingLevel++;
62
63  if (property.properties) {
64    (property.properties ?? []).forEach(subproperty => {
65      appendProperty(formattedSchema, subproperty, nestingLevel);
66    });
67  }
68}
69
70export function _getType(property: Property) {
71  if (property.enum) {
72    return `enum: ${property.enum.join(', ')}`;
73  } else {
74    return property.type?.toString().replace(/,/g, ' || ');
75  }
76}
77
78export function createDescription(property: Property) {
79  let propertyDescription = `**(${_getType(property)})**`;
80  if (property.description) {
81    propertyDescription += ` - ` + property.description.join('\n');
82  }
83
84  return propertyDescription;
85}
86
87export const EasJsonPropertiesTable = ({ schema }: Props) => {
88  const formattedSchema = formatSchema(schema);
89
90  return (
91    <Table>
92      <TableHead>
93        <Row>
94          <HeaderCell>Property</HeaderCell>
95          <HeaderCell>Description</HeaderCell>
96        </Row>
97      </TableHead>
98      <tbody>
99        {formattedSchema.map((property, index) => (
100          <Row key={index}>
101            <Cell fitContent>
102              <div
103                data-testid={property.name}
104                style={{
105                  marginLeft: `${property.nestingLevel * 32}px`,
106                  display: property.nestingLevel ? 'list-item' : 'block',
107                  listStyleType: property.nestingLevel % 2 ? 'default' : 'circle',
108                  overflowX: 'visible',
109                }}>
110                <PropertyName name={property.name} nestingLevel={property.nestingLevel} />
111              </div>
112            </Cell>
113            <Cell>
114              <ReactMarkdown components={mdComponentsNoValidation}>
115                {property.description}
116              </ReactMarkdown>
117            </Cell>
118          </Row>
119        ))}
120      </tbody>
121    </Table>
122  );
123};
124