Skip to content
This repository has been archived by the owner on Dec 27, 2022. It is now read-only.

feat: introduce onRawSubmit and parseSubmitException #150

Merged
merged 3 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@
"final-form": "4.20.6",
"final-form-arrays": "3.0.2",
"final-form-focus": "1.1.2",
"react-final-form": "6.5.8"
"react-final-form": "6.5.8",
"react-final-form-arrays": "3.1.3"
},
"dependenciesMeta": {
"@react-spring/core": {
Expand Down
17 changes: 17 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions src/components/Form/__tests__/__snapshots__/index.spec.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ exports[`Form renders correctly with node children 1`] = `
</DocumentFragment>
`;

exports[`Form renders correctly with onRawSubmit which should take precedence 1`] = `
<DocumentFragment>
<form
novalidate=""
>
<button
type="submit"
>
Submit
</button>
</form>
</DocumentFragment>
`;

exports[`Form renders correctly with onSubmit that return {} 1`] = `
<DocumentFragment>
<form
Expand Down Expand Up @@ -62,6 +76,20 @@ exports[`Form renders correctly with onSubmit that throw 1`] = `
</DocumentFragment>
`;

exports[`Form renders correctly with parseSubmitException 1`] = `
<DocumentFragment>
<form
novalidate=""
>
<button
type="submit"
>
Submit
</button>
</form>
</DocumentFragment>
`;

exports[`Form renders correctly with validate 1`] = `
<DocumentFragment>
<form
Expand Down
59 changes: 59 additions & 0 deletions src/components/Form/__tests__/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,63 @@ describe('Form', () => {
},
)
})

test('renders correctly with parseSubmitException', () => {
const onSubmit = jest.fn(() => Promise.reject(new Error('error')))
const onSubmitSuccess = jest.fn(() => {})
const onSubmitError = jest.fn(() => {})
const parseSubmitException = jest.fn(() => 'parsed error')

return shouldMatchEmotionSnapshot(
<Form
errors={mockErrors}
onSubmitSuccess={onSubmitSuccess}
onSubmit={onSubmit}
onSubmitError={onSubmitError}
parseSubmitException={parseSubmitException}
>
<button type="submit">Submit</button>
</Form>,
{
transform: async ({ getByText }) => {
userEvent.click(getByText('Submit'))
expect(onSubmit).toBeCalledTimes(1)
await waitFor(() => expect(onSubmitError).toBeCalledTimes(1))
await waitFor(() => expect(parseSubmitException).toBeCalledTimes(1))
expect(parseSubmitException).toBeCalledWith(new Error('error'))
expect(onSubmitSuccess).toBeCalledTimes(0)
},
},
)
})

test('renders correctly with onRawSubmit which should take precedence', () => {
const onSubmit = jest.fn(() => Promise.reject(new Error('error')))
const onSubmitSuccess = jest.fn(() => {})
const onRawSubmit = jest.fn(() => {})
const onSubmitError = jest.fn(() => {})
const parseSubmitException = jest.fn(() => 'parsed error')

return shouldMatchEmotionSnapshot(
<Form
errors={mockErrors}
onSubmitSuccess={onSubmitSuccess}
onSubmit={onSubmit}
onSubmitError={onSubmitError}
parseSubmitException={parseSubmitException}
onRawSubmit={onRawSubmit}
>
<button type="submit">Submit</button>
</Form>,
{
transform: async ({ getByText }) => {
userEvent.click(getByText('Submit'))
await waitFor(() => expect(onRawSubmit).toBeCalledTimes(1))
expect(onSubmit).toBeCalledTimes(0)
expect(onSubmitError).toBeCalledTimes(0)
expect(parseSubmitException).toBeCalledTimes(0)
},
},
)
})
})
31 changes: 29 additions & 2 deletions src/components/Form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,24 @@ export type FormProps<FormValues = unknown> = {
| ((props: FormRenderProps<FormValues, Partial<FormValues>>) => ReactNode)
| ReactNode
errors: FormErrors
/**
* onRawSubmit is the base onSubmit from final-form
*/
onRawSubmit?: ReactFinalFormProps<FormValues, Partial<FormValues>>['onSubmit']
/**
* onSubmit acts as onRawSubmit but will call onSubmitSuccess/Error lifecycles
* and will parse eexception with parseSubmitException if provided
* @deprecated its behavior is inconsistent, favor onRawSubmit
*/
onSubmit?: ReactFinalFormProps<FormValues, Partial<FormValues>>['onSubmit']
onSubmitSuccess?: OnSubmitSucccessFn<FormValues>
onSubmitError?: OnSubmitErrorFn
/**
* parseSubmitException will be invoked on onSubmit throw
* It will take the error and must return a readable string or undefined
* @deprecated its behavior is inconsistent, favor onRawSubmit
*/
parseSubmitException?: (error: unknown) => string | undefined
initialValues?: Partial<FormValues>
validateOnBlur?: ReactFinalFormProps<
FormValues,
Expand All @@ -36,6 +51,7 @@ export type FormProps<FormValues = unknown> = {
}
const Form = <FormValues,>({
children,
onRawSubmit,
onSubmit,
onSubmitError,
onSubmitSuccess,
Expand All @@ -47,18 +63,25 @@ const Form = <FormValues,>({
render,
mutators,
keepDirtyOnReinitialize,
parseSubmitException,
}: FormProps<FormValues>): JSX.Element => (
<ErrorProvider errors={errors}>
<ReactFinalForm
initialValues={initialValues}
validateOnBlur={validateOnBlur}
validate={validate}
decorators={[focusOnErrors as unknown as Decorator<FormValues, Partial<FormValues>>]}
decorators={[
focusOnErrors as unknown as Decorator<FormValues, Partial<FormValues>>,
]}
mutators={{
...arrayMutators,
...mutators,
}}
onSubmit={async (values, form, callback) => {
if (onRawSubmit) {
return onRawSubmit(values, form, callback)
}

try {
const res = await onSubmit?.(values, form, callback)
if (res !== undefined) {
Expand All @@ -71,7 +94,11 @@ const Form = <FormValues,>({
} catch (submitError) {
await onSubmitError?.(submitError)

return { [FORM_ERROR]: submitError }
return {
[FORM_ERROR]: parseSubmitException
? parseSubmitException(submitError)
: submitError,
}
}
}}
render={
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export { default as SwitchField } from './components/SwitchField'
export { default as TagsField } from './components/TagsField'
export { default as TextBoxField } from './components/TextBoxField'
export type { FormErrors } from './types'
export { FormSpy, useFormState, useForm, useField } from 'react-final-form'
export { FieldArray, useFieldArray } from 'react-final-form-arrays'