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