Skip to content

Commit

Permalink
add validations and helper text
Browse files Browse the repository at this point in the history
  • Loading branch information
MattPereira committed Sep 22, 2023
1 parent dc25ded commit 991722d
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 118 deletions.
2 changes: 1 addition & 1 deletion frontend/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ReactDOM from "react-dom";
import { createRoot } from "react-dom/client";
import App from "./App";
import "tailwindcss/tailwind.css";
// import "./index.scss";
import "./index.scss";

if (process.env.MODE !== "production") {
const reactAxe = require("@axe-core/react");
Expand Down
72 changes: 72 additions & 0 deletions frontend/src/pages/Authentication/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from "react";
import { Link } from "react-router-dom";
import { TextField } from "tw-components";
import { useForm, SubmitHandler } from "react-hook-form";

type Inputs = {
email: string;
password: string;
};

/** Login Form Component
*
* @dev noValidate on form to disable browser vaildation so we can use react-hook-form validations instead
*
*/

export default function LoginForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<Inputs>();
const onSubmit: SubmitHandler<Inputs> = (data) => {
console.log("Sending form data to server...", data);
};

return (
<div>
<h3 className="mb-10 text-4xl font-bold">Log in</h3>
<form onSubmit={handleSubmit(onSubmit)} noValidate>
<TextField
label="Email"
id="email"
type="email"
register={register}
validations={{ required: "Please enter your email address" }}
errors={errors.email}
/>
<TextField
label="Password"
id="password"
type="password"
register={register}
validations={{
required: "Please enter your password",
pattern: {
value: /^(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/,
message:
"Must be 8 or more characters and contain at least 1 number and 1 special character",
},
}}
errors={errors.password}
/>
<div className="flex mb-4">
<input type="checkbox" className="mr-2" />
<p className="text-grey-dark">Keep me signed in</p>
</div>
<button className="font-bold w-full text-white py-[12px] rounded-3xl bg-blue-dark hover:bg-blue-dark-hover hover:shadow-lg focus:bg-blue-dark-focused">
Login
</button>
</form>
<div className="text-center mt-4">
<p>
New to Civic Tech Jobs?{" "}
<Link to="/signup" className="text-blue-dark font-bold underline">
Sign up
</Link>
</p>
</div>
</div>
);
}
81 changes: 81 additions & 0 deletions frontend/src/pages/Authentication/SignupForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React from "react";
import { Link } from "react-router-dom";
import { TextField } from "tw-components";
import { useForm, SubmitHandler } from "react-hook-form";

type Inputs = {
firstName: string;
lastName: string;
email: string;
password: string;
};

export default function SignupForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<Inputs>();
const onSubmit: SubmitHandler<Inputs> = (data) => {
console.log("Sending form data to server...", data);
};
return (
<div>
<h3 className="mb-10 text-4xl font-bold">Sign up</h3>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="grid grid-cols-1 md:grid-cols-2 md:gap-4">
<TextField
label="First name"
id="firstName"
type="text"
register={register}
errors={errors.firstName}
validations={{ required: "Please enter first name" }}
/>
<TextField
label="Last Name"
id="lastName"
type="text"
register={register}
errors={errors.lastName}
validations={{ required: "Please enter last name" }}
/>
</div>
<TextField
label="Email"
id="email"
type="email"
register={register}
errors={errors.email}
validations={{ required: "Please enter your email address" }}
/>
<TextField
label="Password"
id="password"
type="password"
register={register}
validations={{
required: "Please enter your password",
pattern: {
value: /^(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/,
message:
"Must be 8 or more characters and contain at least 1 number and 1 special character",
},
}}
errors={errors.password}
/>
<button className="font-bold w-full text-white py-[12px] rounded-3xl bg-blue-dark hover:bg-blue-dark-hover hover:shadow-lg focus:bg-blue-dark-focused">
Sign Up
</button>
</form>
<div className="text-center mt-4">
<p>
Already on Civic Tech Jobs?{" "}
<Link to="/login" className="text-blue-dark font-bold underline">
Log In
</Link>
</p>
</div>
</div>
);
}
117 changes: 7 additions & 110 deletions frontend/src/pages/Authentication/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from "react";
import { Link, useLocation } from "react-router-dom";
import { AuthNav, InputGroup } from "tw-components";
import { useForm, SubmitHandler } from "react-hook-form";
import { useLocation } from "react-router-dom";
import { AuthNav } from "tw-components";

import LoginForm from "./LoginForm";
import SignupForm from "./SignupForm";

/** AuthenticationPage
* @dev handles both "/login" and "/signup" paths
Expand All @@ -14,13 +16,13 @@ export default function AuthenticationPage() {
<>
<AuthNav />
<div className="flex flex-row" style={{ height: "calc(100vh - 64px)" }}>
<div className="hidden lg:block lg:basis-1/2 bg-tan">
<div className="max-lg:hidden lg:basis-1/2 bg-tan">
{/* figma art here */}
</div>
<div className="w-full lg:basis-1/2 bg-tan">
<div className="flex flex-col justify-center items-center h-full lg:bg-white">
<div className="w-10/12 lg:w-[439px]">
<div className="bg-white p-6 rounded-2xl lg:bg-transparent lg:p-0 ">
<div className="bg-white rounded-2xl lg:bg-transparent max-lg:p-7">
{pathname === "/login" && <LoginForm />}
{pathname === "/signup" && <SignupForm />}
</div>
Expand All @@ -31,108 +33,3 @@ export default function AuthenticationPage() {
</>
);
}

function SignupForm() {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm<Inputs>();
const onSubmit: SubmitHandler<Inputs> = (data) => {
console.log("Sending form data to server...", data);
};
return (
<div>
<h3 className="mb-4 text-4xl">Sign up</h3>
<form>
<div className="grid grid-cols-1 md:grid-cols-2 md:gap-4">
<InputGroup
label="First name"
id="firstName"
type="text"
register={register}
/>
<InputGroup
label="Last Name"
id="lastName"
type="text"
register={register}
/>
</div>
<InputGroup label="Email" id="email" type="email" register={register} />
<InputGroup
label="Password"
id="password"
type="password"
register={register}
/>
<button className="font-bold w-full text-white py-[12px] rounded-3xl bg-blue-dark hover:bg-blue-dark-hover hover:shadow-lg focus:bg-blue-dark-focused">
Sign Up
</button>
</form>
<div className="text-center mt-4">
<p>
Already on Civic Tech Jobs?{" "}
<Link to="/login" className="text-blue-dark font-bold underline">
Log In
</Link>
</p>
</div>
</div>
);
}

type Inputs = {
example: string;
exampleRequired: string;
};

function LoginForm() {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm<Inputs>();
const onSubmit: SubmitHandler<Inputs> = (data) => {
console.log("Sending form data to server...", data);
};

return (
<div>
<h3 className="mb-8 text-4xl">Log in</h3>
<form onSubmit={handleSubmit(onSubmit)}>
<InputGroup
label="Email"
id="email"
type="email"
register={register}
validations={{ required: "Please enter your email address" }}
/>
<InputGroup
label="Password"
id="password"
type="password"
register={register}
validations={{ required: "Please enter your password", minLength: 6 }}
/>
<div className="flex mb-3">
<input type="checkbox" className="mr-1" />
<p className="text-grey-dark">Keep me signed in</p>
</div>
<button className="font-bold w-full text-white py-[12px] rounded-3xl bg-blue-dark hover:bg-blue-dark-hover hover:shadow-lg focus:bg-blue-dark-focused">
Login
</button>
</form>
<div className="text-center mt-4">
<p>
New to Civic Tech Jobs?{" "}
<Link to="/signup" className="text-blue-dark font-bold underline">
Sign up
</Link>
</p>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,39 @@
import React from "react";
import { UseFormRegister } from "react-hook-form";
import { UseFormRegister, FieldError } from "react-hook-form";
import { IconEyeOpen } from "assets/images/images";

interface InputGroupProps extends React.PropsWithChildren {
interface TextFieldProps extends React.PropsWithChildren {
label: string;
id: string;
type: "text" | "email" | "password";
register: UseFormRegister<any>;
validations?: object;
errors?: FieldError | undefined;
}

/** Reusable input group component
*
* @prop label -> Label for the input
* @prop id -> ID for the input which also allows label to be linked to input
* @prop type -> The type of input (text, email, password) may add more later
* @prop register -> React Hook Form's register function
* @prop validations -> React Hook Form's validation object
* @prop errors -> React Hook Form's errors object
*
* @TODO The password input's "Forgot password" and toggle visibility functionality
*/

export default function InputGroup({
export default function TextField({
label,
id,
type,
register,
validations,
}: InputGroupProps) {
errors,
}: TextFieldProps) {
return (
<div className="w-full mb-3">
<div className="mb-1 font-bold text-base">
<div className="mb-2 font-bold text-base">
<label htmlFor={id}>{label}</label>
{type === "password" && (
<span className="text-blue-dark font-bold underline float-right cursor-pointer">
Expand All @@ -41,14 +46,21 @@ export default function InputGroup({
id={id}
type={type}
{...register(id, validations)}
className="h-11 w-full px-2 border border-grey rounded-lg"
className={`h-11 w-full px-2 border rounded-lg ${
errors
? "border-red focus:outline-red"
: "border-grey focus:outline-blue-dark"
}`}
/>
{type === "password" && (
<div className="absolute inset-y-0 right-0 pr-2 flex items-center">
<IconEyeOpen />
</div>
)}
</div>
{errors && (
<p className="mt-1 text-red font-gothic font-bold">{errors.message}</p>
)}
</div>
);
}
2 changes: 1 addition & 1 deletion frontend/src/tw-components/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { default as AuthNav } from "./AuthNav";
export { default as HeaderNav } from "./HeaderNav";
export { default as InputGroup } from "./InputGroup";
export { default as TextField } from "./TextField";
1 change: 1 addition & 0 deletions frontend/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = {
tan: "#ffe0b9",
"tan-light": "#ffefdb",
green: "#13831e",
red: "#CC0023",
// Neutral Colors
white: "#fff",
"grey-light": "#f2f2f2",
Expand Down

0 comments on commit 991722d

Please sign in to comment.