Merge pull request #15402 from dagda1/onchange-handler-for-stepper

add onChange handler to next/Stepper
This commit is contained in:
Ben Lambert
2022-12-23 14:54:22 +01:00
committed by GitHub
3 changed files with 109 additions and 16 deletions
@@ -20,6 +20,7 @@ import { renderInTestApp } from '@backstage/test-utils';
import { act, fireEvent } from '@testing-library/react';
import type { RJSFValidationError } from '@rjsf/utils';
import { JsonValue } from '@backstage/types';
import { NextFieldExtensionComponentProps } from '../../../extensions/types';
describe('Stepper', () => {
it('should render the step titles for each step of the manifest', async () => {
@@ -110,6 +111,98 @@ describe('Stepper', () => {
);
});
it('should merge nested formData correctly in multiple steps', async () => {
const Repo = ({
onChange,
}: NextFieldExtensionComponentProps<{ repository: string }, any>) => (
<input
aria-label="repo"
type="text"
onChange={e => onChange({ repository: e.target.value })}
defaultValue=""
/>
);
const Owner = ({
onChange,
}: NextFieldExtensionComponentProps<{ owner: string }, any>) => (
<input
aria-label="owner"
type="text"
onChange={e => onChange({ owner: e.target.value })}
defaultValue=""
/>
);
const manifest: TemplateParameterSchema = {
steps: [
{
title: 'Step 1',
schema: {
properties: {
first: {
type: 'object',
'ui:field': 'Repo',
},
},
},
},
{
title: 'Step 2',
schema: {
properties: {
second: {
type: 'object',
'ui:field': 'Owner',
},
},
},
},
],
title: 'React JSON Schema Form Test',
};
const onComplete = jest.fn(async (values: Record<string, JsonValue>) => {
expect(values).toEqual({
first: { repository: 'Repo' },
second: { owner: 'Owner' },
});
});
const { getByRole } = await renderInTestApp(
<Stepper
manifest={manifest}
onComplete={onComplete}
extensions={[
{ name: 'Repo', component: Repo },
{ name: 'Owner', component: Owner },
]}
/>,
);
await fireEvent.change(getByRole('textbox', { name: 'repo' }), {
target: { value: 'Repo' },
});
await act(async () => {
await fireEvent.click(getByRole('button', { name: 'Next' }));
});
await fireEvent.change(getByRole('textbox', { name: 'owner' }), {
target: { value: 'Owner' },
});
await act(async () => {
await fireEvent.click(getByRole('button', { name: 'Review' }));
});
await act(async () => {
await fireEvent.click(getByRole('button', { name: 'Create' }));
});
expect(onComplete).toHaveBeenCalled();
});
it('should render custom field extensions properly', async () => {
const MockComponent = () => {
return <h1>im a custom field extension</h1>;
@@ -26,9 +26,9 @@ import {
Button,
makeStyles,
} from '@material-ui/core';
import { withTheme } from '@rjsf/core-v5';
import { type IChangeEvent, withTheme } from '@rjsf/core-v5';
import { ErrorSchema, FieldValidation } from '@rjsf/utils';
import React, { useMemo, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { NextFieldExtensionOptions } from '../../../extensions';
import { TemplateParameterSchema } from '../../../types';
import { createAsyncValidators } from './createAsyncValidators';
@@ -36,7 +36,6 @@ import { useTemplateSchema } from './useTemplateSchema';
import { ReviewState } from './ReviewState';
import validator from '@rjsf/validator-ajv6';
import { selectedTemplateRouteRef } from '../../../routes';
import { getDefaultFormState } from '@rjsf/utils';
import { useFormData } from './useFormData';
import { FormProps } from '../../types';
@@ -102,6 +101,12 @@ export const Stepper = (props: StepperProps) => {
setActiveStep(prevActiveStep => prevActiveStep - 1);
};
const handleChange = useCallback(
(e: IChangeEvent) =>
setFormState(current => ({ ...current, ...e.formData })),
[setFormState],
);
const handleNext = async ({
formData,
}: {
@@ -111,18 +116,7 @@ export const Stepper = (props: StepperProps) => {
// to display it's own loading? Or should we grey out the entire form.
setErrors(undefined);
const schema = steps[activeStep]?.schema;
const rootSchema = steps[activeStep]?.mergedSchema;
const newFormData = getDefaultFormState(
validator,
schema,
formData,
rootSchema,
true,
);
const returnedValidation = await validation(newFormData);
const returnedValidation = await validation(formData);
const hasErrors = Object.values(returnedValidation).some(
i => i.__errors?.length,
@@ -138,7 +132,7 @@ export const Stepper = (props: StepperProps) => {
return stepNum;
});
}
setFormState(current => ({ ...current, ...newFormData }));
setFormState(current => ({ ...current, ...formData }));
};
return (
@@ -165,6 +159,7 @@ export const Stepper = (props: StepperProps) => {
onSubmit={handleNext}
fields={extensions}
showErrorList={false}
onChange={handleChange}
{...(props.FormProps ?? {})}
>
<div className={styles.footer}>