From ab23d8c4a9dc0eed02b21ec9912d70d40dfc2e6b Mon Sep 17 00:00:00 2001 From: Nick Summers Date: Tue, 19 Mar 2024 17:51:09 -0400 Subject: [PATCH] Bugfix/form validation (#170) * Add form validation to application form * Add back essay questions to be validated * unskip tests * uncomment test in apply * Fix ordering of components in AppForm * Fix imports in text test files * Convert rest of fields to use formik * Remove react-hook-form --- package.json | 5 +- src/app/[id]/apply/page.tsx | 4 +- .../form/{ApplicationForm.tsx => AppForm.tsx} | 82 ++++++++++------- src/app/components/form/CheckboxField.tsx | 12 ++- src/app/components/form/DropdownField.tsx | 13 +-- src/app/components/form/TextArea.tsx | 30 ++---- src/app/components/form/TextField.tsx | 34 ++----- src/app/components/form/USWDSForm.tsx | 32 +++++++ src/app/components/index.tsx | 1 + src/app/components/modals/FilterModal.tsx | 36 ++++---- src/app/schemas/schemas.ts | 25 +++++ src/app/styles/globals.scss | 4 + .../components/form/TextArea.test.tsx | 91 ++++++++++--------- .../components/form/TextField.test.tsx | 81 ++++++++--------- src/app/utils/filtering.ts | 14 ++- src/app/verbiage/errors.ts | 6 ++ yarn.lock | 86 ++++++++++++++++-- 17 files changed, 338 insertions(+), 218 deletions(-) rename src/app/components/form/{ApplicationForm.tsx => AppForm.tsx} (84%) create mode 100644 src/app/components/form/USWDSForm.tsx create mode 100644 src/app/schemas/schemas.ts create mode 100644 src/app/verbiage/errors.ts diff --git a/package.json b/package.json index fc08b70..5034b3a 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,13 @@ "@aws-sdk/lib-dynamodb": "^3.529.1", "@trussworks/react-uswds": "7.0.0", "@uswds/uswds": "^3.7.1", + "formik": "^2.4.5", "mixpanel-browser": "^2.49.0", "next": "14.1.3", "react": "^18", "react-dom": "^18", - "react-hook-form": "^7.51.0", - "sharp": "^0.33.2" + "sharp": "^0.33.2", + "yup": "^1.4.0" }, "devDependencies": { "@testing-library/cypress": "^10.0.1", diff --git a/src/app/[id]/apply/page.tsx b/src/app/[id]/apply/page.tsx index 3b09182..5cd7eb6 100644 --- a/src/app/[id]/apply/page.tsx +++ b/src/app/[id]/apply/page.tsx @@ -1,7 +1,7 @@ -import { ApplicationForm } from "../../components/form/ApplicationForm"; +import { AppForm } from "../../components/form/AppForm"; export default function ApplyPage({ params }: Props) { - return ; + return ; } type Props = { diff --git a/src/app/components/form/ApplicationForm.tsx b/src/app/components/form/AppForm.tsx similarity index 84% rename from src/app/components/form/ApplicationForm.tsx rename to src/app/components/form/AppForm.tsx index 46745d2..e9d5da0 100644 --- a/src/app/components/form/ApplicationForm.tsx +++ b/src/app/components/form/AppForm.tsx @@ -1,7 +1,9 @@ "use client"; -import { useForm } from "react-hook-form"; +import * as yup from "yup"; import { useEffect, useState } from "react"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; // components import { Button, @@ -10,21 +12,24 @@ import { CardBody, GridContainer, } from "@trussworks/react-uswds"; -import { TextField, TextArea, Spinner } from "@/src/app/components"; -import NotFound from "@/src/app/not-found"; -import Link from "next/link"; -import { useRouter } from "next/navigation"; +import { Spinner, TextArea, TextField, USWDSForm } from "../index"; +// pages +import NotFound from "../../not-found"; // utils -import { getInstitutionApplication } from "@/src/app/utils/institutions"; -import { saveApplication } from "@/src/app/utils/applications"; +import { getInstitutionApplication } from "../../utils/institutions"; +import { saveApplication } from "../../utils/applications"; +// schemas +import { email, number, numberOptional, text } from "../../schemas/schemas"; -export const ApplicationForm = ({ institutionId }: Props) => { +export const AppForm = ({ institutionId }: Props) => { const router = useRouter(); const [application, setApplication] = useState< Record | undefined >(); const [loading, setLoading] = useState(true); + const appq = application?.questions; + useEffect(() => { getInstitutionApplication(Number(institutionId)).then((application) => { setApplication(application); @@ -32,10 +37,6 @@ export const ApplicationForm = ({ institutionId }: Props) => { }); }, [institutionId]); - const { handleSubmit, register } = useForm(); - - const appq = application?.questions; - const onSubmit = async (data: any) => { const submission = { questions: appq, @@ -53,15 +54,45 @@ export const ApplicationForm = ({ institutionId }: Props) => { //Handle SAT score questions const hasSATQ: boolean = appq?.includes("What is your SAT score?"); - //Handle EssayQuestions + const satSchema = hasSATQ ? number() : numberOptional(); + + const validationSchema = yup.object().shape({ + "first-name": text(), + "last-name": text(), + email: email(), + phone: text(), + "math-score": satSchema, + "reading-score": satSchema, + "writing-score": satSchema, + "question-1": text(), + "question-2": text(), + "question-3": text(), + }); + // Handle EssayQuestions const essayQ1: string | undefined = appq?.[4]; const essayQ2: string | undefined = appq?.[5]; const essayQ3: string | undefined = appq?.[6]; + const ApplicationView = !application ? ( ) : ( -
-
+
+

{application?.institutionName} @@ -80,32 +111,24 @@ export const ApplicationForm = ({ institutionId }: Props) => {
@@ -120,27 +143,21 @@ export const ApplicationForm = ({ institutionId }: Props) => {
@@ -172,7 +189,6 @@ export const ApplicationForm = ({ institutionId }: Props) => { label={essayQ1} name={"question-1"} required={false} - registerField={register} /> @@ -190,7 +206,6 @@ export const ApplicationForm = ({ institutionId }: Props) => { label={essayQ2} name={"question-2"} required={false} - registerField={register} /> @@ -209,7 +224,6 @@ export const ApplicationForm = ({ institutionId }: Props) => { label={essayQ3} name={"question-3"} required={false} - registerField={register} /> @@ -224,8 +238,8 @@ export const ApplicationForm = ({ institutionId }: Props) => {

- -
+ + ); return
{loading ? : ApplicationView}
; diff --git a/src/app/components/form/CheckboxField.tsx b/src/app/components/form/CheckboxField.tsx index 1c3840d..eb42beb 100644 --- a/src/app/components/form/CheckboxField.tsx +++ b/src/app/components/form/CheckboxField.tsx @@ -1,17 +1,20 @@ -import { FieldValues, UseFormRegister } from "react-hook-form"; +"use client"; + +import { Field } from "formik"; +//types import { CheckboxOptions } from "../../types"; -export const CheckboxField = ({ name, registerField, options }: Props) => ( +export const CheckboxField = ({ name, options }: Props) => (
{options.map((option: CheckboxOptions) => (
-
)} - + ); }; @@ -43,7 +45,6 @@ type Props = { label: string; required: boolean; name: string; - registerField: UseFormRegister; options: DropdownOptions[]; hint?: FieldHint; }; diff --git a/src/app/components/form/TextArea.tsx b/src/app/components/form/TextArea.tsx index b084a0b..323f3af 100644 --- a/src/app/components/form/TextArea.tsx +++ b/src/app/components/form/TextArea.tsx @@ -1,31 +1,21 @@ -import { FieldHint } from "../../types"; -import { FieldValues, UseFormRegister } from "react-hook-form"; +"use client"; -export const TextArea = ({ - id, - label, - name, - required, - registerField, - hint, -}: Props) => { +import { ErrorMessage, Field } from "formik"; + +export const TextArea = ({ id, label, name, required }: Props) => { const fieldLabel = required ? `${label} *` : `${label}`; return (
- {hint && ( -
- {hint.text} -
- )} -