diff --git a/.changeset/eight-planets-see.md b/.changeset/eight-planets-see.md
new file mode 100644
index 0000000000..20adcac7ef
--- /dev/null
+++ b/.changeset/eight-planets-see.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder': patch
+---
+
+render details for composite property schemas
diff --git a/plugins/scaffolder/src/components/RenderSchema/RenderSchema.test.tsx b/plugins/scaffolder/src/components/RenderSchema/RenderSchema.test.tsx
index 0f1150591d..ec49f414f9 100644
--- a/plugins/scaffolder/src/components/RenderSchema/RenderSchema.test.tsx
+++ b/plugins/scaffolder/src/components/RenderSchema/RenderSchema.test.tsx
@@ -475,7 +475,7 @@ describe('JSON schema UI rendering', () => {
const tr = getByTestId(`properties-row_test.${k}`);
expect(tr).toBeInTheDocument();
- const sub = getByTestId(`properties-row_test_sub${i}.${k}`);
+ const sub = getByTestId(`properties-row_test_oneOf${i}.${k}`);
expect(sub).toBeInTheDocument();
expect(
@@ -485,7 +485,7 @@ describe('JSON schema UI rendering', () => {
).toBe(true);
expect(
- getByTestId(`properties-row_test_sub${i}.${k}Flag`),
+ getByTestId(`properties-row_test_oneOf${i}.${k}Flag`),
).toBeInTheDocument();
}
});
@@ -528,10 +528,35 @@ describe('JSON schema UI rendering', () => {
for (const i of subs.keys()) {
for (const k of Object.keys(subs[i].properties!)) {
expect(
- rendered.getByTestId(`properties-row_test_sub${i}.${k}`),
+ rendered.getByTestId(`properties-row_test_oneOf${i}.${k}`),
).toBeInTheDocument();
}
}
});
+ it('property alternatives', async () => {
+ const schema: JSONSchema7 = {
+ properties: {
+ bs: {
+ anyOf: [
+ {
+ type: 'boolean',
+ },
+ {
+ type: 'string',
+ },
+ ],
+ },
+ },
+ };
+ const rendered = await renderInTestApp(
+ ,
+ );
+ expect(
+ rendered.getByTestId(`root-row_test.bs_anyOf0`),
+ ).toBeInTheDocument();
+ expect(
+ rendered.getByTestId(`root-row_test.bs_anyOf1`),
+ ).toBeInTheDocument();
+ });
});
});
diff --git a/plugins/scaffolder/src/components/RenderSchema/RenderSchema.tsx b/plugins/scaffolder/src/components/RenderSchema/RenderSchema.tsx
index 8f967f8ee2..fc926e6c44 100644
--- a/plugins/scaffolder/src/components/RenderSchema/RenderSchema.tsx
+++ b/plugins/scaffolder/src/components/RenderSchema/RenderSchema.tsx
@@ -41,37 +41,44 @@ import {
JSONSchema7Definition,
JSONSchema7Type,
} from 'json-schema';
-import { FC, JSX, cloneElement, Fragment } from 'react';
+import { FC, JSX, cloneElement, Fragment, ReactElement } from 'react';
import { scaffolderTranslationRef } from '../../translation';
import { SchemaRenderContext, SchemaRenderStrategy } from './types';
import { TranslationMessages } from '../TemplatingExtensionsPage/types';
-const getTypes = (properties: JSONSchema7) => {
- if (!properties.type) {
+const compositeSchemaProperties = ['allOf', 'anyOf', 'not', 'oneOf'] as const;
+
+type subSchemasType = {
+ [K in (typeof compositeSchemaProperties)[number]]?: JSONSchema7Definition[];
+};
+
+const getTypes = (schema: JSONSchema7) => {
+ if (!schema.type) {
+ if (
+ Object.getOwnPropertyNames(schema).some(p =>
+ compositeSchemaProperties.includes(p as any),
+ )
+ ) {
+ return undefined;
+ }
return ['unknown'];
}
- if (properties.type !== 'array') {
- return [properties.type].flat();
+ if (schema.type !== 'array') {
+ return [schema.type].flat();
}
return [
- `${properties.type}(${
- (properties.items as JSONSchema7 | undefined)?.type ?? 'unknown'
+ `${schema.type}(${
+ (schema.items as JSONSchema7 | undefined)?.type ?? 'unknown'
})`,
];
};
-const getSubschemas = (
- schema: JSONSchema7Definition,
-): Record => {
+const getSubschemas = (schema: JSONSchema7Definition): subSchemasType => {
if (typeof schema === 'boolean') {
return {};
}
const base: Omit = {};
- const compositeSchemaProperties = ['allOf', 'anyOf', 'not', 'oneOf'] as const;
- type subSchemasType = {
- [K in (typeof compositeSchemaProperties)[number]]?: JSONSchema7Definition[];
- };
const subschemas: subSchemasType = {};
for (const [key, value] of Object.entries(schema) as [
@@ -224,7 +231,10 @@ const inspectSchema = (
return { canSubschema: false, hasEnum: false };
}
return {
- canSubschema: getTypes(schema).some(t => t.includes('object')),
+ canSubschema:
+ Object.getOwnPropertyNames(schema).some(p =>
+ compositeSchemaProperties.includes(p as any),
+ ) || getTypes(schema)!.some(t => t.includes('object')),
hasEnum: !!enumFrom(schema),
};
};
@@ -242,8 +252,8 @@ const typeColumn = {
const info = inspectSchema(element.schema);
return (
<>
- {types.map((type, index) =>
- type.includes('object') || (info.hasEnum && index === 0) ? (
+ {types?.map((type, index) =>
+ info.canSubschema || (info.hasEnum && index === 0) ? (
{
if (typeof schema === 'object') {
- const subschemas =
- strategy === 'root' || !context.parent ? getSubschemas(schema) : {};
+ const subschemas = getSubschemas(schema);
let columns: Column[] | undefined;
let elements: SchemaRenderElement[] | undefined;
if (strategy === 'root') {
@@ -393,90 +402,100 @@ export const RenderSchema = ({
{elements.map(el => {
const id = generateId(el, context);
const info = inspectSchema(el.schema);
- return (
-
-
- {columns!.map(col => (
-
+ {columns!.map(col => (
+
+ {col.render(el, context)}
+
+ ))}
+ ,
+ ];
+ if (
+ typeof el.schema !== 'boolean' &&
+ (info.canSubschema || info.hasEnum)
+ ) {
+ let details: ReactElement = (
+
+ {info.canSubschema && (
+
- {col.render(el, context)}
-
- ))}
-
- {typeof el.schema !== 'boolean' &&
- (info.canSubschema || info.hasEnum) && (
-
-
-
-
- {info.canSubschema && (
-
- )}
- {info.hasEnum && (
- <>
- {cloneElement(
- context.headings[0],
- {},
- 'Valid values:',
- )}
-
- >
- )}
-
-
-
-
+ />
)}
-
- );
+ {info.hasEnum && (
+ <>
+ {cloneElement(
+ context.headings[0],
+ {},
+ 'Valid values:',
+ )}
+
+ >
+ )}
+
+ );
+ if (getTypes(el.schema)) {
+ details = (
+
+ {details}
+
+ );
+ }
+ rows.push(
+
+
+ {details}
+
+ ,
+ );
+ }
+ return {rows};
})}
)}
- {Object.keys(subschemas).map(sk => (
+ {(Object.keys(subschemas) as Array).map(sk => (
{cloneElement(context.headings[0], {}, sk)}
- {subschemas[sk].map((sub, index) => (
+ {subschemas[sk]!.map((sub, index) => (