diff --git a/plugins/config-schema/src/components/ConfigSchemaPage/ConfigSchemaPage.tsx b/plugins/config-schema/src/components/ConfigSchemaPage/ConfigSchemaPage.tsx index 8811a3e0bd..689ac4379f 100644 --- a/plugins/config-schema/src/components/ConfigSchemaPage/ConfigSchemaPage.tsx +++ b/plugins/config-schema/src/components/ConfigSchemaPage/ConfigSchemaPage.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ import React, { useMemo } from 'react'; -import { Grid } from '@material-ui/core'; import { Header, Page, @@ -36,19 +35,15 @@ export const ConfigSchemaPage = () => { return ( -
+
- + A description of your plugin goes here. - - - {schema ? : 'No schema available'} - - + {schema ? : 'No schema available'} ); diff --git a/plugins/config-schema/src/components/SchemaViewer/SchemaViewer.tsx b/plugins/config-schema/src/components/SchemaViewer/SchemaViewer.tsx index 5e6c1ec94c..874fea03a9 100644 --- a/plugins/config-schema/src/components/SchemaViewer/SchemaViewer.tsx +++ b/plugins/config-schema/src/components/SchemaViewer/SchemaViewer.tsx @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; +import React, { ReactNode, useMemo } from 'react'; import { Schema } from 'jsonschema'; import { Box, Chip, + createStyles, Divider, + fade, makeStyles, Paper, Table, @@ -26,8 +28,12 @@ import { TableCell, TableRow, Typography, + withStyles, } from '@material-ui/core'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; import { JsonValue } from '@backstage/config'; +import { TreeItem, TreeItemProps, TreeView } from '@material-ui/lab'; interface SchemaViewProps { path: string; @@ -414,6 +420,183 @@ export interface SchemaViewerProps { schema: Schema; } +const StyledTreeItem = withStyles(theme => + createStyles({ + label: { + userSelect: 'none', + }, + group: { + marginLeft: 7, + paddingLeft: theme.spacing(1), + borderLeft: `1px solid ${fade(theme.palette.text.primary, 0.15)}`, + }, + }), +)((props: TreeItemProps) => ); + +export function createSchemaBrowserItems( + expanded: string[], + schema: Schema, + path: string = '', + depth: number = 0, +): ReactNode { + let matchArr; + if (schema.anyOf) { + matchArr = schema.anyOf; + } else if (schema.oneOf) { + matchArr = schema.oneOf; + } else if (schema.allOf) { + matchArr = schema.allOf; + } + if (matchArr) { + return matchArr.map((childSchema, index) => { + const childPath = `${path}/${index}`; + if (depth > 0) expanded.push(childPath); + return ( + `} + > + {createSchemaBrowserItems( + expanded, + childSchema, + childPath, + depth + 1, + )} + + ); + }); + } + + switch (schema.type) { + case 'array': { + const childPath = `${path}[]`; + if (depth > 0) expanded.push(childPath); + return ( + + {schema.items && + createSchemaBrowserItems( + expanded, + schema.items as Schema, + childPath, + depth + 1, + )} + + ); + } + case 'object': + case undefined: { + const children = []; + + if (schema.properties) { + children.push( + ...Object.entries(schema.properties).map(([name, childSchema]) => { + const childPath = path ? `${path}/${name}` : name; + if (depth > 0) expanded.push(childPath); + return ( + + {createSchemaBrowserItems( + expanded, + childSchema, + childPath, + depth + 1, + )} + + ); + }), + ); + } + + if (schema.patternProperties) { + children.push( + ...Object.entries(schema.patternProperties).map( + ([name, childSchema]) => { + const childPath = `${path}/<${name}>`; + if (depth > 0) expanded.push(childPath); + return ( + `} + > + {createSchemaBrowserItems( + expanded, + childSchema, + childPath, + depth + 1, + )} + + ); + }, + ), + ); + } + + if (schema.additionalProperties && schema.additionalProperties !== true) { + const childPath = `${path}/*`; + if (depth > 0) expanded.push(childPath); + children.push( + + {createSchemaBrowserItems( + expanded, + schema.additionalProperties, + childPath, + depth + 1, + )} + , + ); + } + + return <>{children}; + } + + default: + return null; + } +} + +export function SchemaBrowser({ schema }: { schema: Schema }) { + const data = useMemo(() => { + const expanded = new Array(); + + const items = createSchemaBrowserItems(expanded, schema); + + return { items, expanded }; + }, [schema]); + + return ( + } + defaultExpandIcon={} + > + {data.items} + + ); +} + export const SchemaViewer = ({ schema }: SchemaViewerProps) => { - return ; + return ( + + + + + + + + + + + + + + ); };