Working, now work on responsiveness

This commit is contained in:
Henry Hiles 2023-07-29 20:24:09 -04:00
parent f49b59a582
commit fb330d94a1
9 changed files with 176 additions and 60 deletions

View file

@ -1,10 +1,21 @@
import { render, screen } from "@testing-library/react" import { render, screen } from "@testing-library/react"
import { describe, expect } from "vitest" import { BookingForm } from "./components/BookingForm"
import { BookingForm } from "./components/BookingForm.jsx" import "@testing-library/jest-dom"
import { initializeTimes, updateTimes } from "./components/Main"
describe("Renders the BookingForm heading", () => { test("Renders the BookingForm heading", () => {
const availableTimes = ["17:00"] const availableTimes = ["17:00"]
render(<BookingForm availableTimes={availableTimes} />) render(<BookingForm availableTimes={availableTimes} />)
const headingElement = screen.getByText("Choose date") const headingElement = screen.getByText("Choose date")
expect(headingElement).toBeInTheDocument() expect(headingElement).toBeInTheDocument()
}) })
test("Returns a correct array of times", () => {
expect(initializeTimes()).toHaveLength(6)
expect(initializeTimes()[0]).toMatch(/\d{2}:\d{2}/)
})
test("Returns the same value provided in the state", () => {
const times = initializeTimes()
expect(updateTimes(times, "2022-02-04")).toBe(times)
})

View file

@ -1,43 +1,84 @@
import { useState } from "react" import { useState } from "react"
import "../styles/BookingForm.css" import "../styles/BookingForm.css"
export const BookingForm = ({ availableTimes, dispatch }) => { export const BookingForm = ({
availableTimes,
dispatch,
submitForm,
reservationError
}) => {
const [date, setDate] = useState(new Date().toISOString().slice(0, 10)) const [date, setDate] = useState(new Date().toISOString().slice(0, 10))
const [time, setTime] = useState(availableTimes[0]) const [time, setTime] = useState(availableTimes[0])
const [guests, setGuests] = useState(1) const [guests, setGuests] = useState(1)
const [occasion, setOccasion] = useState("None") const [occasion, setOccasion] = useState("Other")
const [dateError, setDateError] = useState("")
const [timeError, setTimeError] = useState("")
const [guestsError, setGuestsError] = useState("")
const [occasionError, setOccasionError] = useState("")
const isFormInvalid = () =>
guestsError !== "" ||
dateError !== "" ||
timeError !== "" ||
occasionError !== ""
const onDateChange = ({ target }) => {
let error = ""
if (target.value === "") error = "Field is required"
setDateError(error)
setDate(target.value)
if (!error) dispatch(target.valueAsDate)
}
const onTimeChange = ({ target }) => {
let error = ""
if (target.value === "") error = "Field is required"
setTimeError(error)
setTime(target.value)
}
const onGuestChange = ({ target }) => {
let error = ""
if (target.value === "") error = "Field is required"
else if (target.value < 1 || target.value > 10)
error = "Guests must be between 1 and 10."
setGuestsError(error)
setGuests(target.value)
}
const onOccasionChange = ({ target }) => {
let error = ""
if (target.value === "") error = "Field is required"
else if (
target.value !== "Other" &&
target.value !== "Birthday" &&
target.value !== "Anniversary"
)
error = "Invalid occasion."
setOccasionError(error)
setOccasion(target.value)
}
return ( return (
<form <form className="booking-form" onSubmit={submitForm}>
className="booking-form" <label htmlFor="date">Choose date</label>
onSubmit={(event) => {
event.preventDefault()
console.log(date, time, guests, occasion)
}}
>
<label htmlFor="res-date">Choose date</label>
<input <input
value={date} value={date}
required required
onChange={(event) => { onChange={onDateChange}
setDate(event.target.value)
dispatch(event.target.value)
}}
type="date" type="date"
id="res-date" id="date"
/> />
{dateError !== "" && <span className="error">{dateError}</span>}
<label htmlFor="time">Choose time</label> <label htmlFor="time">Choose time</label>
<select <select value={time} required onChange={onTimeChange} id="time">
value={time}
required
onChange={(event) => setTime(event.target.value)}
id="time"
>
{availableTimes.map((time) => ( {availableTimes.map((time) => (
<option key={time}>{time}</option> <option key={time}>{time}</option>
))} ))}
</select> </select>
{timeError !== "" && <span className="error">{timeError}</span>}
<label htmlFor="guests">Number of guests</label> <label htmlFor="guests">Number of guests</label>
<input <input
@ -47,25 +88,29 @@ export const BookingForm = ({ availableTimes, dispatch }) => {
max="10" max="10"
id="guests" id="guests"
value={guests} value={guests}
onChange={(event) => setGuests(event.target.value)} onChange={onGuestChange}
/> />
{guestsError !== "" && <span className="error">{guestsError}</span>}
<label htmlFor="occasion">Occasion</label> <label htmlFor="occasion">Occasion</label>
<select <select id="occasion" value={occasion} onChange={onOccasionChange}>
id="occasion" <option>Other</option>
value={occasion}
onChange={(event) => setOccasion(event.target.value)}
>
<option>None</option>
<option>Birthday</option> <option>Birthday</option>
<option>Anniversary</option> <option>Anniversary</option>
</select> </select>
{occasionError !== "" && (
<span className="error">{occasionError}</span>
)}
<input <input
type="submit" type="submit"
className="submit" className="submit"
disabled={isFormInvalid()}
value="Confirm reservation" value="Confirm reservation"
/> />
{reservationError !== "" && (
<span className="error">{reservationError}</span>
)}
</form> </form>
) )
} }

View file

@ -6,24 +6,24 @@ export const Footer = () => (
<img src="/images/footer-logo.png" alt="Little Lemon Logo" /> <img src="/images/footer-logo.png" alt="Little Lemon Logo" />
<section> <section>
<h3>Doormat Navigation</h3> <h3>Doormat Navigation</h3>
<a href="#">Home</a> <a href="/">Home</a>
<a href="#">About</a> <a href="/">About</a>
<a href="#">Menu</a> <a href="/">Menu</a>
<Link to="/book">Reserve a table</Link> <Link to="/book">Reserve a table</Link>
<a href="#">Order Online</a> <a href="/">Order online</a>
<a href="#">Login</a> <a href="/">Login</a>
</section> </section>
<section> <section>
<h3>Contact</h3> <h3>Contact</h3>
<a href="#">Address</a> <a href="/">Address</a>
<a href="#">Phone number</a> <a href="/">Phone number</a>
<a href="#">Email</a> <a href="/">Email</a>
</section> </section>
<section> <section>
<h3>Social Media Links</h3> <h3>Social Media Links</h3>
<a href="#">Twitter</a> <a href="/">Twitter</a>
<a href="#">Instagram</a> <a href="/">Instagram</a>
<a href="#">Threads</a> <a href="/">Threads</a>
</section> </section>
</footer> </footer>
) )

View file

@ -2,39 +2,65 @@ import { Route } from "react-router-dom"
import { Routes } from "react-router-dom" import { Routes } from "react-router-dom"
import { HomePage } from "../routes/HomePage" import { HomePage } from "../routes/HomePage"
import { BookingPage } from "../routes/BookingPage" import { BookingPage } from "../routes/BookingPage"
import { useReducer } from "react" import { useReducer, useState } from "react"
import { ConfirmedBookingPage } from "../routes/ConfirmedBookingPage"
import { useNavigate } from "react-router-dom"
export const initializeTimes = () => [
"17:00",
"18:00",
"19:00",
"20:00",
"21:00",
"22:00"
]
export const updateTimes = (_, date) => {
// API provided returns 404, so unable to post data. Mocking return instead.
const day = date.getDay()
const initialTimes = initializeTimes()
if (day === 0) return initialTimes.slice(0, 3)
if (day === 1) return initialTimes.slice(2, 5)
if (day === 2) return initialTimes.slice(4, 5)
if (day === 3) return initialTimes.slice(3, 5)
if (day === 4) return initialTimes.slice(1, 5)
if (day === 5) return initialTimes.slice(3, 3)
if (day === 6) return initialTimes.slice(1, 5)
}
export const Main = () => { export const Main = () => {
const initializeTimes = () => [ const navigate = useNavigate()
"17:00",
"18:00",
"19:00",
"20:00",
"21:00",
"22:00"
]
const updateTimes = (times, date) => {
console.log(times, date)
return times
}
const [availableTimes, dispatch] = useReducer( const [availableTimes, dispatch] = useReducer(
updateTimes, updateTimes,
initializeTimes() initializeTimes()
) )
const [reservationError, setReservationError] = useState("")
const submitForm = (event) => {
event.preventDefault()
// API provided returns 404, so unable to post data. Mocking return instead.
if (Math.random() > 0.8)
setReservationError("Unable to book reservation. Please try again.")
else navigate("/confirmed")
}
return ( return (
<> <>
<main> <main>
<Routes> <Routes>
<Route index element={<HomePage />} /> <Route index element={<HomePage />} />
<Route
path="/confirmed"
element={<ConfirmedBookingPage />}
/>
<Route <Route
path="/book" path="/book"
element={ element={
<BookingPage <BookingPage
availableTimes={availableTimes} availableTimes={availableTimes}
dispatch={dispatch} dispatch={dispatch}
submitForm={submitForm}
reservationError={reservationError}
/> />
} }
/> />

View file

@ -3,7 +3,7 @@ import "../styles/Special.css"
export const Special = ({ image, title, price, children }) => ( export const Special = ({ image, title, price, children }) => (
<section className="column"> <section className="column">
<img src={image} alt="Picture of Food" /> <img src={image} alt="Food" />
<article> <article>
<section className="row specialRow"> <section className="row specialRow">
<h3>{title}</h3> <h3>{title}</h3>

View file

@ -1,10 +1,20 @@
import { BookingForm } from "../components/BookingForm" import { BookingForm } from "../components/BookingForm"
import "../styles/BookingPage.css" import "../styles/BookingPage.css"
export const BookingPage = ({ availableTimes, dispatch }) => ( export const BookingPage = ({
availableTimes,
dispatch,
submitForm,
reservationError
}) => (
<section className="booking"> <section className="booking">
<h1>Book a Table</h1> <h1>Book a Table</h1>
<hr /> <hr />
<BookingForm availableTimes={availableTimes} dispatch={dispatch} /> <BookingForm
availableTimes={availableTimes}
dispatch={dispatch}
submitForm={submitForm}
reservationError={reservationError}
/>
</section> </section>
) )

View file

@ -0,0 +1,10 @@
import { Link } from "react-router-dom"
import "../styles/ConfirmedBookingPage.css"
export const ConfirmedBookingPage = () => (
<article className="confirmed">
<h1>Booking Confirmed</h1>
<p>Your booking has been confirmed.</p>
<Link to="/">Back to home</Link>
</article>
)

View file

@ -2,6 +2,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.2rem; gap: 0.2rem;
min-width: 20rem;
} }
.booking-form :is(label, .submit) { .booking-form :is(label, .submit) {
@ -19,3 +20,9 @@
.booking-form .submit { .booking-form .submit {
background-color: #fbdabb; background-color: #fbdabb;
} }
.booking-form .error {
color: rgb(255, 152, 152);
font-weight: bold;
margin: 0.5rem 0;
}

View file

@ -0,0 +1,7 @@
.confirmed {
background-color: var(--primary-1);
border-radius: 2rem;
margin: auto;
padding: 2rem 3rem;
text-align: center;
}