mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
refactor: curry GetFormHelpers (#1156)
This commit is contained in:
@ -49,13 +49,14 @@ export const AccountForm: React.FC<AccountFormProps> = ({
|
||||
validationSchema,
|
||||
onSubmit,
|
||||
})
|
||||
const getFieldHelpers = getFormHelpers<AccountFormValues>(form, formErrors)
|
||||
|
||||
return (
|
||||
<>
|
||||
<form onSubmit={form.handleSubmit}>
|
||||
<Stack>
|
||||
<TextField
|
||||
{...getFormHelpers<AccountFormValues>(form, "name")}
|
||||
{...getFieldHelpers("name")}
|
||||
autoFocus
|
||||
autoComplete="name"
|
||||
fullWidth
|
||||
@ -63,7 +64,7 @@ export const AccountForm: React.FC<AccountFormProps> = ({
|
||||
variant="outlined"
|
||||
/>
|
||||
<TextField
|
||||
{...getFormHelpers<AccountFormValues>(form, "email", formErrors.email)}
|
||||
{...getFieldHelpers("email")}
|
||||
onChange={onChangeTrimmed(form)}
|
||||
autoComplete="email"
|
||||
fullWidth
|
||||
@ -71,7 +72,7 @@ export const AccountForm: React.FC<AccountFormProps> = ({
|
||||
variant="outlined"
|
||||
/>
|
||||
<TextField
|
||||
{...getFormHelpers<AccountFormValues>(form, "username", formErrors.username)}
|
||||
{...getFieldHelpers("username")}
|
||||
onChange={onChangeTrimmed(form)}
|
||||
autoComplete="username"
|
||||
fullWidth
|
||||
|
@ -76,13 +76,14 @@ export const SignInForm: React.FC<SignInFormProps> = ({
|
||||
validationSchema,
|
||||
onSubmit,
|
||||
})
|
||||
const getFieldHelpers = getFormHelpers<BuiltInAuthFormValues>(form)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Welcome />
|
||||
<form onSubmit={form.handleSubmit}>
|
||||
<TextField
|
||||
{...getFormHelpers<BuiltInAuthFormValues>(form, "email")}
|
||||
{...getFieldHelpers("email")}
|
||||
onChange={onChangeTrimmed(form)}
|
||||
autoFocus
|
||||
autoComplete="email"
|
||||
@ -93,7 +94,7 @@ export const SignInForm: React.FC<SignInFormProps> = ({
|
||||
variant="outlined"
|
||||
/>
|
||||
<TextField
|
||||
{...getFormHelpers<BuiltInAuthFormValues>(form, "password")}
|
||||
{...getFieldHelpers("password")}
|
||||
autoComplete="current-password"
|
||||
className={styles.loginTextField}
|
||||
fullWidth
|
||||
|
@ -37,30 +37,53 @@ const form = {
|
||||
|
||||
describe("form util functions", () => {
|
||||
describe("getFormHelpers", () => {
|
||||
const untouchedGoodResult = getFormHelpers<TestType>(form, "untouchedGoodField")
|
||||
const untouchedBadResult = getFormHelpers<TestType>(form, "untouchedBadField")
|
||||
const touchedGoodResult = getFormHelpers<TestType>(form, "touchedGoodField")
|
||||
const touchedBadResult = getFormHelpers<TestType>(form, "touchedBadField")
|
||||
it("populates the 'field props'", () => {
|
||||
expect(untouchedGoodResult.name).toEqual("untouchedGoodField")
|
||||
expect(untouchedGoodResult.onBlur).toBeDefined()
|
||||
expect(untouchedGoodResult.onChange).toBeDefined()
|
||||
expect(untouchedGoodResult.value).toBeDefined()
|
||||
describe("without API errors", () => {
|
||||
const getFieldHelpers = getFormHelpers<TestType>(form)
|
||||
const untouchedGoodResult = getFieldHelpers("untouchedGoodField")
|
||||
const untouchedBadResult = getFieldHelpers("untouchedBadField")
|
||||
const touchedGoodResult = getFieldHelpers("touchedGoodField")
|
||||
const touchedBadResult = getFieldHelpers("touchedBadField")
|
||||
it("populates the 'field props'", () => {
|
||||
expect(untouchedGoodResult.name).toEqual("untouchedGoodField")
|
||||
expect(untouchedGoodResult.onBlur).toBeDefined()
|
||||
expect(untouchedGoodResult.onChange).toBeDefined()
|
||||
expect(untouchedGoodResult.value).toBeDefined()
|
||||
})
|
||||
it("sets the id to the name", () => {
|
||||
expect(untouchedGoodResult.id).toEqual("untouchedGoodField")
|
||||
})
|
||||
it("sets error to true if touched and invalid", () => {
|
||||
expect(untouchedGoodResult.error).toBeFalsy
|
||||
expect(untouchedBadResult.error).toBeFalsy
|
||||
expect(touchedGoodResult.error).toBeFalsy
|
||||
expect(touchedBadResult.error).toBeTruthy
|
||||
})
|
||||
it("sets helperText to the error message if touched and invalid", () => {
|
||||
expect(untouchedGoodResult.helperText).toBeUndefined
|
||||
expect(untouchedBadResult.helperText).toBeUndefined
|
||||
expect(touchedGoodResult.helperText).toBeUndefined
|
||||
expect(touchedBadResult.helperText).toEqual("oops!")
|
||||
})
|
||||
})
|
||||
it("sets the id to the name", () => {
|
||||
expect(untouchedGoodResult.id).toEqual("untouchedGoodField")
|
||||
})
|
||||
it("sets error to true if touched and invalid", () => {
|
||||
expect(untouchedGoodResult.error).toBeFalsy
|
||||
expect(untouchedBadResult.error).toBeFalsy
|
||||
expect(touchedGoodResult.error).toBeFalsy
|
||||
expect(touchedBadResult.error).toBeTruthy
|
||||
})
|
||||
it("sets helperText to the error message if touched and invalid", () => {
|
||||
expect(untouchedGoodResult.helperText).toBeUndefined
|
||||
expect(untouchedBadResult.helperText).toBeUndefined
|
||||
expect(touchedGoodResult.helperText).toBeUndefined
|
||||
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!")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FormikContextType, getIn } from "formik"
|
||||
import { FormikContextType, FormikErrors, getIn } from "formik"
|
||||
import { ChangeEvent, ChangeEventHandler, FocusEventHandler } from "react"
|
||||
|
||||
interface FormHelpers {
|
||||
@ -11,22 +11,26 @@ interface FormHelpers {
|
||||
helperText?: string
|
||||
}
|
||||
|
||||
export const getFormHelpers = <T>(form: FormikContextType<T>, name: keyof T, error?: string): FormHelpers => {
|
||||
if (typeof name !== "string") {
|
||||
throw new Error(`name must be type of string, instead received '${typeof name}'`)
|
||||
}
|
||||
export const getFormHelpers =
|
||||
<T>(form: FormikContextType<T>, formErrors?: FormikErrors<T>) =>
|
||||
(name: keyof T): FormHelpers => {
|
||||
if (typeof name !== "string") {
|
||||
throw new Error(`name must be type of string, instead received '${typeof name}'`)
|
||||
}
|
||||
|
||||
// getIn is a util function from Formik that gets at any depth of nesting
|
||||
// and is necessary for the types to work
|
||||
const touched = getIn(form.touched, name)
|
||||
const errors = error ?? getIn(form.errors, name)
|
||||
return {
|
||||
...form.getFieldProps(name),
|
||||
id: name,
|
||||
error: touched && Boolean(errors),
|
||||
helperText: touched && errors,
|
||||
// getIn is a util function from Formik that gets at any depth of nesting
|
||||
// and is necessary for the types to work
|
||||
const touched = getIn(form.touched, name)
|
||||
const apiError = getIn(formErrors, name)
|
||||
const validationError = getIn(form.errors, name)
|
||||
const error = apiError ?? validationError
|
||||
return {
|
||||
...form.getFieldProps(name),
|
||||
id: name,
|
||||
error: touched && Boolean(error),
|
||||
helperText: touched && error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const onChangeTrimmed =
|
||||
<T>(form: FormikContextType<T>) =>
|
||||
|
Reference in New Issue
Block a user