fix: merge uiSchema and formContext in Stepper component

This commit ensures that the uiSchema and formContext are merged in the
Stepper component form, with the non-props values taking precedence over
the props values.

The uiSchemas are deep-merged using `lodash.merge` to ensure that nested
values are correctly merged.

Signed-off-by: Rich Barton-Cooper <rich@syntasso.io>
This commit is contained in:
Rich Barton-Cooper
2024-07-22 15:55:23 +01:00
parent d0e95a7036
commit 8cdd846abc
2 changed files with 72 additions and 3 deletions
@@ -549,6 +549,66 @@ describe('Stepper', () => {
});
});
it('should allow overrides to the uiSchema and formContext correctly', async () => {
const manifest: TemplateParameterSchema = {
title: 'Custom Fields',
steps: [
{
title: 'Test',
schema: {
properties: {
name: {
type: 'string',
'ui:placeholder': 'Enter your name',
},
age: {
type: 'number',
'ui:placeholder': 'Enter your age',
},
},
},
},
],
};
const uiSchema = {
name: {
'ui:readonly': true,
'ui:placeholder': 'Should be overwritten',
},
};
const formContext = {
readOnlyAsDisabled: true,
};
const { getByRole } = await renderInTestApp(
<SecretsContextProvider>
<Stepper
manifest={manifest}
onCreate={jest.fn()}
extensions={[]}
formProps={{ uiSchema, formContext }}
initialState={{ name: 'Some Name', age: 40 }}
/>
</SecretsContextProvider>,
);
expect(getByRole('textbox', { name: 'name' })).toHaveValue('Some Name');
expect(getByRole('textbox', { name: 'name' })).toBeDisabled();
expect(getByRole('textbox', { name: 'name' })).toHaveAttribute(
'placeholder',
'Enter your name',
);
expect(getByRole('spinbutton', { name: 'age' })).toHaveValue(40);
expect(getByRole('spinbutton', { name: 'age' })).toBeEnabled();
expect(getByRole('spinbutton', { name: 'age' })).toHaveAttribute(
'placeholder',
'Enter your age',
);
});
describe('Scaffolder Layouts', () => {
it('should render the step in the scaffolder layout', async () => {
const ScaffolderLayout: LayoutTemplate = ({ properties }) => (
@@ -51,6 +51,7 @@ import { ErrorListTemplate } from './ErrorListTemplate';
import { makeStyles } from '@material-ui/core/styles';
import { PasswordWidget } from '../PasswordWidget/PasswordWidget';
import ajvErrors from 'ajv-errors';
import { merge } from 'lodash';
const validator = customizeValidator();
ajvErrors(validator.ajv);
@@ -193,6 +194,14 @@ export const Stepper = (stepperProps: StepperProps) => {
setFormState(current => ({ ...current, ...formData }));
};
const {
formContext: propFormContext,
uiSchema: propUiSchema,
...restFormProps
} = props.formProps ?? {};
const mergedUiSchema = merge({}, propUiSchema, currentStep?.uiSchema);
return (
<>
{isValidating && <LinearProgress variant="indeterminate" />}
@@ -229,9 +238,9 @@ export const Stepper = (stepperProps: StepperProps) => {
validator={validator}
extraErrors={errors as unknown as ErrorSchema}
formData={formState}
formContext={{ formData: formState }}
formContext={{ ...propFormContext, formData: formState }}
schema={currentStep.schema}
uiSchema={currentStep.uiSchema}
uiSchema={mergedUiSchema}
onSubmit={handleNext}
fields={fields}
showErrorList="top"
@@ -241,7 +250,7 @@ export const Stepper = (stepperProps: StepperProps) => {
experimental_defaultFormStateBehavior={{
allOf: 'populateDefaults',
}}
{...(props.formProps ?? {})}
{...restFormProps}
>
<div className={styles.footer}>
<Button