add ScaffolderLayouts to NextScaffolderPage

Signed-off-by: Paul Cowan <paul.cowan@cutting.scot>
This commit is contained in:
Paul Cowan
2022-12-23 16:36:52 +00:00
parent cd63a3d39f
commit d2ddde2108
8 changed files with 138 additions and 12 deletions
@@ -392,4 +392,47 @@ describe('Stepper', () => {
await fireEvent.click(getByRole('button', { name: 'Make' }));
});
});
describe('Scaffolder Layouts', () => {
it('should render the step in the scaffolder layout', async () => {
const ScaffolderLayout: LayoutTemplate = ({ properties }) => (
<>
<h1>A Scaffolder Layout</h1>
{properties.map((prop, i) => (
<div key={i}>{prop.content}</div>
))}
</>
);
const manifest: TemplateParameterSchema = {
steps: [
{
title: 'Step 1',
schema: {
type: 'object',
'ui:ObjectFieldTemplate': 'Layout',
properties: {
field1: {
type: 'string',
},
},
},
},
],
title: 'scaffolder layouts',
};
const { getByText, getByRole } = await renderInTestApp(
<Stepper
manifest={manifest}
extensions={[]}
onComplete={jest.fn()}
layouts={[{ name: 'Layout', component: ScaffolderLayout }]}
/>,
);
expect(getByText('A Scaffolder Layout')).toBeInTheDocument();
expect(getByRole('textbox', { name: 'field1' })).toBeInTheDocument();
});
});
});
@@ -28,11 +28,14 @@ import React, { useCallback, useMemo, useState, type ReactNode } from 'react';
import { NextFieldExtensionOptions } from '../../extensions';
import { TemplateParameterSchema } from '../../../types';
import { createAsyncValidators } from './createAsyncValidators';
import type { FormProps } from '../../types';
import type { FormProps, LayoutOptions } from '../../types';
import { ReviewState, type ReviewStateProps } from '../ReviewState';
import { useTemplateSchema } from '../../hooks/useTemplateSchema';
import { useFormDataFromQuery } from '../../hooks/useFormDataFromQuery';
import validator from '@rjsf/validator-ajv6';
import { useFormDataFromQuery } from '../../hooks';
import type { FormProps } from '../../types';
import { selectedTemplateRouteRef } from '../../../routes';
const useStyles = makeStyles(theme => ({
backButton: {
@@ -65,6 +68,7 @@ export type StepperProps = {
createButtonText?: ReactNode;
reviewButtonText?: ReactNode;
};
layouts?: LayoutOptions[];
};
// TODO(blam): We require here, as the types in this package depend on @rjsf/core explicitly
@@ -76,17 +80,15 @@ const Form = withTheme(require('@rjsf/material-ui-v5').Theme);
* The `Stepper` component is the Wizard that is rendered when a user selects a template
* @alpha
*/
export const Stepper = (stepperProps: StepperProps) => {
const { components = {}, ...props } = stepperProps;
const { layouts = [], components = {}, ...props } = stepperProps;
const {
ReviewStateComponent = ReviewState,
createButtonText = 'Create',
reviewButtonText = 'Review',
} = components;
const analytics = useAnalytics();
const { steps } = useTemplateSchema(props.manifest);
const { steps } = useTemplateSchema(props.manifest, layouts);
const apiHolder = useApiHolder();
const [activeStep, setActiveStep] = useState(0);
const [formState, setFormState] = useFormDataFromQuery(props.initialState);
@@ -177,7 +179,7 @@ export const Stepper = (stepperProps: StepperProps) => {
fields={extensions}
showErrorList={false}
onChange={handleChange}
{...(props.FormProps ?? {})}
{...(formProps ?? {})}
>
<div className={styles.footer}>
<Button
@@ -233,4 +233,42 @@ describe('useTemplateSchema', () => {
});
});
});
it('should replace ui:ObjectFieldTemplate with actual component', () => {
const layouts = [{ name: 'TwoColumn', component: jest.fn() }];
const manifest: TemplateParameterSchema = {
title: 'Test Template',
description: 'Test Template Description',
steps: [
{
title: 'Step 1',
description: 'Step 1 Description',
schema: {
type: 'object',
'ui:ObjectFieldTemplate': 'TwoColumn',
properties: {
field1: {
type: 'string',
},
},
},
},
],
};
const { result } = renderHook(() => useTemplateSchema(manifest, layouts), {
wrapper: ({ children }) => (
<TestApiProvider
apis={[[featureFlagsApiRef, { isActive: () => true }]]}
>
{children}
</TestApiProvider>
),
});
const [{ uiSchema }] = result.current.steps;
expect(uiSchema['ui:ObjectFieldTemplate']).toEqual(layouts[0].component);
});
});
@@ -24,3 +24,22 @@ export type FormProps = Pick<
SchemaFormProps,
'transformErrors' | 'noHtml5Validate'
>;
/**
* The field template from `@rjsf/core` which is a react component that gets passed `@rjsf/core` field related props.
*
* @public
*/
export type LayoutTemplate<T = any> = NonNullable<
SchemaFormProps<T>['uiSchema']
>['ui:ObjectFieldTemplate'];
/**
* The type of layouts that is passed to the TemplateForms
*
* @public
*/
export interface LayoutOptions<P = any> {
name: string;
component: LayoutTemplate<P>;
}