From c7aa1cebeb89770cfd8fa5a00f693d038e690944 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Wed, 18 Sep 2024 16:31:20 -0400 Subject: [PATCH] fix: ConnectedGridForm onTouched --- .../gamut/src/ConnectedForm/ConnectedForm.tsx | 8 +++ .../gamut/src/ConnectedForm/SubmitButton.tsx | 2 +- .../__tests__/ConnectedForm.test.tsx | 57 ++++++++++++++++++- packages/gamut/src/ConnectedForm/utils.tsx | 8 ++- .../ConnectedForm/ConnectedForm.examples.tsx | 1 + 5 files changed, 70 insertions(+), 6 deletions(-) diff --git a/packages/gamut/src/ConnectedForm/ConnectedForm.tsx b/packages/gamut/src/ConnectedForm/ConnectedForm.tsx index 0387ae0cbc..e394d75c17 100644 --- a/packages/gamut/src/ConnectedForm/ConnectedForm.tsx +++ b/packages/gamut/src/ConnectedForm/ConnectedForm.tsx @@ -35,6 +35,12 @@ export interface FormContextProps { * Validation rules form fields. Fields with validation rules must have a defaultValue listed in the defaultValue prop. */ validationRules?: { string?: RegisterOptions }; + /** + * Which react hook form mode we are going to use for validation. + * If you use the onTouched mode the submit button will be disabled until all + * required fields are completed. + */ + validationMode: Mode; /** * Sets if form submission was successful - if `undefined` will fall back to react-hook-forms native formState.isSubmitSuccessful. */ @@ -150,6 +156,7 @@ export const ConnectedForm = forwardRef( resetOnSubmit, validationRules, wasSubmitSuccessful, + validationMode: validation, }; }, [ disableFieldsOnSubmit, @@ -157,6 +164,7 @@ export const ConnectedForm = forwardRef( resetOnSubmit, validationRules, wasSubmitSuccessful, + validation, ]); return ( diff --git a/packages/gamut/src/ConnectedForm/SubmitButton.tsx b/packages/gamut/src/ConnectedForm/SubmitButton.tsx index e8aabd5fa7..33743b1762 100644 --- a/packages/gamut/src/ConnectedForm/SubmitButton.tsx +++ b/packages/gamut/src/ConnectedForm/SubmitButton.tsx @@ -29,7 +29,7 @@ export type SubmitButtonProps = Omit & export const SubmitButton: React.FC = ({ as: Button = FillButton, children, - disabled = false, + disabled, loading = false, ...rest }) => { diff --git a/packages/gamut/src/ConnectedForm/__tests__/ConnectedForm.test.tsx b/packages/gamut/src/ConnectedForm/__tests__/ConnectedForm.test.tsx index d07713af04..c9cf1d02a4 100644 --- a/packages/gamut/src/ConnectedForm/__tests__/ConnectedForm.test.tsx +++ b/packages/gamut/src/ConnectedForm/__tests__/ConnectedForm.test.tsx @@ -91,7 +91,7 @@ describe('ConnectedForm', () => { view ); - doBaseFormActions(selectField, textField, checkboxField, radioOption); + await doBaseFormActions(selectField, textField, checkboxField, radioOption); await act(async () => { fireEvent.submit(view.getByRole('button')); @@ -225,12 +225,65 @@ describe('ConnectedForm', () => { radioOption, } = getBaseCases(view); - doBaseFormActions(selectField, textField, checkboxField, radioOption); + await doBaseFormActions( + selectField, + textField, + checkboxField, + radioOption + ); + + expect(checkboxField.checked).toEqual(true); + expect(selectField.value).toEqual(selectValue); + expect(textField.value).toEqual(textValue); + expect(radioOption.value).toEqual(radioValue); + await waitFor(() => expect(view.getByRole('button')).not.toBeDisabled()); + }); + }); + + describe('onTouched validation', () => { + it('disables the submit button when by default when required fields are incomplete', async () => { + const api = createPromise<{}>(); + const onSubmit = async (values: {}) => api.resolve(values); + + const { view } = renderView({ + children: , + validation: 'onTouched', + validationRules, + defaultValues, + onSubmit, + }); + + expect(view.getByRole('button')).toBeDisabled(); + }); + + it('enables the submit button after the required fields are completed', async () => { + const api = createPromise<{}>(); + const onSubmit = async (values: {}) => api.resolve(values); + + const { view } = renderView({ + children: , + validation: 'onTouched', + validationRules, + defaultValues, + onSubmit, + }); + + const { + checkboxField, + selectField, + textField, + radioOption, + } = getBaseCases(view); + + await act(async () => { + doBaseFormActions(selectField, textField, checkboxField, radioOption); + }); expect(checkboxField.checked).toEqual(true); expect(selectField.value).toEqual(selectValue); expect(textField.value).toEqual(textValue); expect(radioOption.value).toEqual(radioValue); + await waitFor(() => expect(view.getByRole('button')).not.toBeDisabled()); }); }); diff --git a/packages/gamut/src/ConnectedForm/utils.tsx b/packages/gamut/src/ConnectedForm/utils.tsx index 5720ed8cc7..a2324214c5 100644 --- a/packages/gamut/src/ConnectedForm/utils.tsx +++ b/packages/gamut/src/ConnectedForm/utils.tsx @@ -115,6 +115,7 @@ export const useFormState = () => { validationRules, wasSubmitSuccessful, isSoloField, + validationMode, } = useContext(FormPropsContext); const { isSubmitting, isDirty, errors } = formState; @@ -138,6 +139,7 @@ export const useFormState = () => { setFocus, setValue, useFieldArray, + validationMode, validationRules, watch, }; @@ -198,7 +200,7 @@ export const useField = ({ name, disabled, loading }: useFieldProps) => { }; export const useSubmitState = ({ loading, disabled }: SubmitContextProps) => { - const { formState } = useFormContext(); + const { formState, validationMode } = useFormState(); const isLoading = typeof loading === 'function' ? loading(formState) : loading; @@ -206,8 +208,8 @@ export const useSubmitState = ({ loading, disabled }: SubmitContextProps) => { const isDisabled = typeof disabled === 'function' ? disabled(formState) - : disabled === undefined - ? formState.isValid + : disabled === undefined && validationMode === 'onTouched' + ? !formState.isValid : disabled; return { isLoading, isDisabled }; diff --git a/packages/styleguide/stories/Organisms/ConnectedForm/ConnectedForm.examples.tsx b/packages/styleguide/stories/Organisms/ConnectedForm/ConnectedForm.examples.tsx index 2126ef89c3..54963b61f4 100644 --- a/packages/styleguide/stories/Organisms/ConnectedForm/ConnectedForm.examples.tsx +++ b/packages/styleguide/stories/Organisms/ConnectedForm/ConnectedForm.examples.tsx @@ -147,6 +147,7 @@ export const ConnectedFormPlayground: React.FC = ({ alignItems="center" justifyContent="space-between" minHeight="50rem" + resetOnSubmit {...connectedFormProps} {...connectedForm} >