Skip to content

Commit be3a89b

Browse files
authored
Merge pull request #104 from workfloworchestrator/1936-has-next
1936 has next
2 parents 3adfb8d + 39ea458 commit be3a89b

File tree

10 files changed

+72
-26
lines changed

10 files changed

+72
-26
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'pydantic-forms': patch
3+
---
4+
5+
Exports components from components/render folder
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'pydantic-forms': minor
3+
---
4+
5+
Adds hasNext property for multistep forms

frontend/packages/pydantic-forms/src/components/form/Footer.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ const Footer = () => {
2020
sendLabel,
2121
footerComponent,
2222
allowUntouchedSubmit,
23+
hasNext,
24+
formInputData,
2325
} = usePydanticFormContext();
2426

2527
const t = useTranslations('footer');
26-
28+
const submitButtonLabel = sendLabel ?? hasNext ? t('send') : t('submit');
2729
const PreviousButton = () => (
2830
<button
2931
type="button"
@@ -75,7 +77,7 @@ const Footer = () => {
7577
!rhf.formState.isSubmitting)
7678
}
7779
>
78-
{sendLabel ?? t('send')}
80+
{submitButtonLabel}
7981
</button>
8082
);
8183

@@ -88,8 +90,10 @@ const Footer = () => {
8890
!rhf.formState.isDirty && (
8991
<div>Het formulier is nog niet aangepast</div>
9092
)}
93+
{formInputData && formInputData.length > 0 && (
94+
<PreviousButton />
95+
)}
9196

92-
<PreviousButton />
9397
<ResetButton />
9498

9599
{!!onCancel && <CancelButton />}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './fields';
22
export * from './zodValidationsPresets';
3+
export * from './render';
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import React from 'react';
22

33
import { RenderFields } from '@/components/render';
4-
import type { FormRenderer as Renderer } from '@/types';
4+
import type { FormRenderComponent } from '@/types';
55

6-
export const FormRenderer: Renderer = ({ pydanticFormComponents }) => {
6+
export const FormRenderer: FormRenderComponent = ({
7+
pydanticFormComponents,
8+
}) => {
79
return <RenderFields pydanticFormComponents={pydanticFormComponents} />;
810
};

frontend/packages/pydantic-forms/src/core/PydanticFormContextProvider.tsx

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import React, {
99
createContext,
1010
useCallback,
1111
useEffect,
12+
useMemo,
1213
useRef,
1314
useState,
1415
} from 'react';
@@ -151,6 +152,7 @@ function PydanticFormContextProvider({
151152
// we cache the form scheme so when there is an error, we still have the form
152153
// the form is not in the error response
153154
const [rawSchema, setRawSchema] = useState<PydanticFormSchemaRawJson>();
155+
const [hasNext, setHasNext] = useState<boolean>(false);
154156

155157
// extract the JSON schema to a more usable custom schema
156158

@@ -172,10 +174,24 @@ function PydanticFormContextProvider({
172174
componentMatcher,
173175
);
174176

177+
const initialData = useMemo(
178+
() =>
179+
getFormValuesFromFieldOrLabels(
180+
pydanticFormSchema,
181+
{
182+
...formLabels?.data,
183+
...customData,
184+
},
185+
componentMatcher,
186+
),
187+
[componentMatcher, customData, formLabels?.data, pydanticFormSchema],
188+
);
189+
175190
// initialize the react-hook-form
176191
const rhf = useForm({
177192
resolver: zodResolver(resolver),
178193
mode: 'all',
194+
values: initialData,
179195
});
180196

181197
// Adds watch subscripton on form values
@@ -220,8 +236,15 @@ function PydanticFormContextProvider({
220236
}
221237

222238
setFormInputHistory(new Map<string, object>());
223-
rhf.reset();
224-
}, [apiResponse, isFullFilled, onSuccess, rhf, skipSuccessNotice]);
239+
rhf.reset(initialData);
240+
}, [
241+
apiResponse,
242+
initialData,
243+
isFullFilled,
244+
onSuccess,
245+
rhf,
246+
skipSuccessNotice,
247+
]);
225248

226249
// a useeffect for whenever the error response updates
227250
// sometimes we need to update the form,
@@ -235,6 +258,9 @@ function PydanticFormContextProvider({
235258
// when we receive a form from the JSON, we fully reset the scheme
236259
if (apiResponse?.form) {
237260
setRawSchema(apiResponse.form);
261+
if (apiResponse.meta) {
262+
setHasNext(!!apiResponse.meta.hasNext);
263+
}
238264
setErrorDetails(undefined);
239265
}
240266

@@ -251,26 +277,16 @@ function PydanticFormContextProvider({
251277
return;
252278
}
253279

254-
const initialData = getFormValuesFromFieldOrLabels(
255-
pydanticFormSchema,
256-
{
257-
...formLabels?.data,
258-
...customData,
259-
},
260-
componentMatcher,
261-
);
262-
263-
rhf.reset(initialData);
264-
}, [customData, formLabels, pydanticFormSchema, rhf, componentMatcher]);
280+
rhf.reset(undefined, { keepDefaultValues: true });
281+
}, [pydanticFormSchema, rhf]);
265282

266283
// a useeffect for filling data whenever formdefinition or labels update
267284
useEffect(() => {
268-
// this makes sure default values are set.
269-
resetFormData();
270285
getHashForArray(formInputData).then((hash) => {
271286
const currentStepFromHistory = formInputHistory.get(hash);
272287

273288
if (currentStepFromHistory) {
289+
rhf.reset();
274290
Object.entries(currentStepFromHistory).forEach(
275291
([fieldName, fieldValue]) =>
276292
rhf.setValue(fieldName, fieldValue, {
@@ -300,7 +316,6 @@ function PydanticFormContextProvider({
300316
const submitFormFn = useCallback(() => {
301317
setIsSending(true);
302318
addFormInputData(rhf?.getValues(), !!errorDetails);
303-
304319
window.scrollTo(0, 0);
305320
}, [rhf, errorDetails, addFormInputData]);
306321

@@ -362,6 +377,7 @@ function PydanticFormContextProvider({
362377
setFormInputData([]);
363378
setIsFullFilled(false);
364379
setRawSchema(undefined);
380+
setHasNext(false);
365381
}, []);
366382

367383
const PydanticFormContextState = {
@@ -393,6 +409,8 @@ function PydanticFormContextProvider({
393409
formKey,
394410
formIdKey,
395411
clearForm,
412+
formInputData,
413+
hasNext,
396414
};
397415

398416
return (

frontend/packages/pydantic-forms/src/core/helper.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,14 @@ export const isNullableField = (field: PydanticFormField) =>
256256
* And labelData (this holds the current values from API)
257257
*/
258258
export const getFormValuesFromFieldOrLabels = (
259-
pydanticFormSchema: PydanticFormSchema,
259+
pydanticFormSchema?: PydanticFormSchema,
260260
labelData?: Record<string, string>,
261261
componentMatcher?: PydanticFormsContextConfig['componentMatcher'],
262262
) => {
263+
if (!pydanticFormSchema) {
264+
return {};
265+
}
266+
263267
const fieldValues: Record<string, string> = {};
264268

265269
const includedFields: string[] = [];

frontend/packages/pydantic-forms/src/messages/en-GB.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"footer": {
33
"cancel": "Cancel",
44
"send": "Send",
5+
"submit": "Submit",
56
"reset": "Reset",
67
"previous": "Previous",
78
"next": "Next",

frontend/packages/pydantic-forms/src/messages/nl-NL.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"footer": {
33
"cancel": "Annuleren",
44
"send": "Verzenden",
5+
"submit": "Indienen",
56
"reset": "Resetten",
67
"notModifiedYet": "Het formulier is nog niet aangepast",
78
"notFilledYet": "Het formulier is nog niet ingevuld",

frontend/packages/pydantic-forms/src/types.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ export interface PydanticFormContextProps {
7878
formKey: string;
7979
formIdKey?: string;
8080
clearForm: () => void;
81+
hasNext: boolean;
82+
formInputData: object[];
8183
}
8284

8385
export enum PydanticFormState {
@@ -247,7 +249,7 @@ export type PydanticFormZodValidationFn = (
247249
rhf?: ReturnType<typeof useForm>,
248250
) => z.ZodTypeAny;
249251

250-
export type RowRenderer = React.JSXElementConstructor<{
252+
export type RowRenderComponent = React.JSXElementConstructor<{
251253
title: string;
252254
description?: string;
253255
required?: boolean;
@@ -293,9 +295,9 @@ export interface PydanticFormsContextConfig {
293295

294296
componentMatcher?: ComponentMatcher;
295297

296-
formRenderer?: FormRenderer;
298+
formRenderer?: FormRenderComponent;
297299
footerRenderer?: React.JSXElementConstructor<object>;
298-
rowRenderer?: RowRenderer;
300+
rowRenderer?: RowRenderComponent;
299301

300302
// Extend field definitions
301303
fieldDetailProvider?: PydanticFormFieldDetailProvider;
@@ -313,7 +315,7 @@ export interface PydanticFormsContextConfig {
313315
locale?: Locale;
314316
}
315317

316-
export type FormRenderer = React.JSXElementConstructor<{
318+
export type FormRenderComponent = React.JSXElementConstructor<{
317319
pydanticFormComponents: PydanticFormComponents;
318320
}>;
319321

@@ -384,6 +386,9 @@ export interface PydanticFormApiResponse {
384386
form: PydanticFormSchemaRawJson;
385387
success?: boolean;
386388
validation_errors: PydanticFormApiValidationError[];
389+
meta?: {
390+
hasNext?: boolean;
391+
};
387392
}
388393

389394
export interface PydanticFormBaseSchema {

0 commit comments

Comments
 (0)