refactor: curry GetFormHelpers (#1156)

This commit is contained in:
Presley Pizzo
2022-04-25 15:45:33 -04:00
committed by GitHub
parent 33b58a0363
commit bdc17f49e4
4 changed files with 72 additions and 43 deletions

View File

@ -49,13 +49,14 @@ export const AccountForm: React.FC<AccountFormProps> = ({
validationSchema, validationSchema,
onSubmit, onSubmit,
}) })
const getFieldHelpers = getFormHelpers<AccountFormValues>(form, formErrors)
return ( return (
<> <>
<form onSubmit={form.handleSubmit}> <form onSubmit={form.handleSubmit}>
<Stack> <Stack>
<TextField <TextField
{...getFormHelpers<AccountFormValues>(form, "name")} {...getFieldHelpers("name")}
autoFocus autoFocus
autoComplete="name" autoComplete="name"
fullWidth fullWidth
@ -63,7 +64,7 @@ export const AccountForm: React.FC<AccountFormProps> = ({
variant="outlined" variant="outlined"
/> />
<TextField <TextField
{...getFormHelpers<AccountFormValues>(form, "email", formErrors.email)} {...getFieldHelpers("email")}
onChange={onChangeTrimmed(form)} onChange={onChangeTrimmed(form)}
autoComplete="email" autoComplete="email"
fullWidth fullWidth
@ -71,7 +72,7 @@ export const AccountForm: React.FC<AccountFormProps> = ({
variant="outlined" variant="outlined"
/> />
<TextField <TextField
{...getFormHelpers<AccountFormValues>(form, "username", formErrors.username)} {...getFieldHelpers("username")}
onChange={onChangeTrimmed(form)} onChange={onChangeTrimmed(form)}
autoComplete="username" autoComplete="username"
fullWidth fullWidth

View File

@ -76,13 +76,14 @@ export const SignInForm: React.FC<SignInFormProps> = ({
validationSchema, validationSchema,
onSubmit, onSubmit,
}) })
const getFieldHelpers = getFormHelpers<BuiltInAuthFormValues>(form)
return ( return (
<> <>
<Welcome /> <Welcome />
<form onSubmit={form.handleSubmit}> <form onSubmit={form.handleSubmit}>
<TextField <TextField
{...getFormHelpers<BuiltInAuthFormValues>(form, "email")} {...getFieldHelpers("email")}
onChange={onChangeTrimmed(form)} onChange={onChangeTrimmed(form)}
autoFocus autoFocus
autoComplete="email" autoComplete="email"
@ -93,7 +94,7 @@ export const SignInForm: React.FC<SignInFormProps> = ({
variant="outlined" variant="outlined"
/> />
<TextField <TextField
{...getFormHelpers<BuiltInAuthFormValues>(form, "password")} {...getFieldHelpers("password")}
autoComplete="current-password" autoComplete="current-password"
className={styles.loginTextField} className={styles.loginTextField}
fullWidth fullWidth

View File

@ -37,10 +37,12 @@ const form = {
describe("form util functions", () => { describe("form util functions", () => {
describe("getFormHelpers", () => { describe("getFormHelpers", () => {
const untouchedGoodResult = getFormHelpers<TestType>(form, "untouchedGoodField") describe("without API errors", () => {
const untouchedBadResult = getFormHelpers<TestType>(form, "untouchedBadField") const getFieldHelpers = getFormHelpers<TestType>(form)
const touchedGoodResult = getFormHelpers<TestType>(form, "touchedGoodField") const untouchedGoodResult = getFieldHelpers("untouchedGoodField")
const touchedBadResult = getFormHelpers<TestType>(form, "touchedBadField") const untouchedBadResult = getFieldHelpers("untouchedBadField")
const touchedGoodResult = getFieldHelpers("touchedGoodField")
const touchedBadResult = getFieldHelpers("touchedBadField")
it("populates the 'field props'", () => { it("populates the 'field props'", () => {
expect(untouchedGoodResult.name).toEqual("untouchedGoodField") expect(untouchedGoodResult.name).toEqual("untouchedGoodField")
expect(untouchedGoodResult.onBlur).toBeDefined() expect(untouchedGoodResult.onBlur).toBeDefined()
@ -63,6 +65,27 @@ describe("form util functions", () => {
expect(touchedBadResult.helperText).toEqual("oops!") expect(touchedBadResult.helperText).toEqual("oops!")
}) })
}) })
describe("with API errors", () => {
it("shows an error if there is only an API error", () => {
const getFieldHelpers = getFormHelpers<TestType>(form, { touchedGoodField: "API error!" })
const result = getFieldHelpers("touchedGoodField")
expect(result.error).toBeTruthy
expect(result.helperText).toEqual("API error!")
})
it("shows an error if there is only a validation error", () => {
const getFieldHelpers = getFormHelpers<TestType>(form, {})
const result = getFieldHelpers("touchedBadField")
expect(result.error).toBeTruthy
expect(result.helperText).toEqual("oops!")
})
it("shows the API error if both are present", () => {
const getFieldHelpers = getFormHelpers<TestType>(form, { touchedBadField: "API error!" })
const result = getFieldHelpers("touchedBadField")
expect(result.error).toBeTruthy
expect(result.helperText).toEqual("API error!")
})
})
})
describe("onChangeTrimmed", () => { describe("onChangeTrimmed", () => {
it("calls handleChange with trimmed value", () => { it("calls handleChange with trimmed value", () => {

View File

@ -1,4 +1,4 @@
import { FormikContextType, getIn } from "formik" import { FormikContextType, FormikErrors, getIn } from "formik"
import { ChangeEvent, ChangeEventHandler, FocusEventHandler } from "react" import { ChangeEvent, ChangeEventHandler, FocusEventHandler } from "react"
interface FormHelpers { interface FormHelpers {
@ -11,7 +11,9 @@ interface FormHelpers {
helperText?: string helperText?: string
} }
export const getFormHelpers = <T>(form: FormikContextType<T>, name: keyof T, error?: string): FormHelpers => { export const getFormHelpers =
<T>(form: FormikContextType<T>, formErrors?: FormikErrors<T>) =>
(name: keyof T): FormHelpers => {
if (typeof name !== "string") { if (typeof name !== "string") {
throw new Error(`name must be type of string, instead received '${typeof name}'`) throw new Error(`name must be type of string, instead received '${typeof name}'`)
} }
@ -19,14 +21,16 @@ export const getFormHelpers = <T>(form: FormikContextType<T>, name: keyof T, err
// getIn is a util function from Formik that gets at any depth of nesting // getIn is a util function from Formik that gets at any depth of nesting
// and is necessary for the types to work // and is necessary for the types to work
const touched = getIn(form.touched, name) const touched = getIn(form.touched, name)
const errors = error ?? getIn(form.errors, name) const apiError = getIn(formErrors, name)
const validationError = getIn(form.errors, name)
const error = apiError ?? validationError
return { return {
...form.getFieldProps(name), ...form.getFieldProps(name),
id: name, id: name,
error: touched && Boolean(errors), error: touched && Boolean(error),
helperText: touched && errors, helperText: touched && error,
}
} }
}
export const onChangeTrimmed = export const onChangeTrimmed =
<T>(form: FormikContextType<T>) => <T>(form: FormikContextType<T>) =>