diff --git a/cypress/e2e/desktop/add.cy.ts b/cypress/e2e/desktop/add.cy.ts index 53e94b3af..2ff07fbf9 100644 --- a/cypress/e2e/desktop/add.cy.ts +++ b/cypress/e2e/desktop/add.cy.ts @@ -147,43 +147,6 @@ describe('Adding new toilets to the platform', () => { cy.contains('08:00 - 19:00').should('not.exist'); }); - it('should edit all hours when edit all check box is clicked and the toilet is open', () => { - cy.visit('/loos/add'); - - const toiletName = faker.word.adjective() + ' ' + faker.word.noun(); - cy.findByPlaceholderText('e.g. Sainsburys or street name').type( - toiletName - ); - //If user clicks edit all hours, enables editing of all hours - cy.get('[name=has-opening-times]').click(); - cy.get('[name=edit-all-day-hours]').click(); - cy.get('[name=monday-is-open]').click(); - cy.get('[name=tuesday-is-open]').click(); - - cy.get('[name=monday-opens]').type('08:00'); - cy.get('[name=monday-closes]').type('16:00'); - - cy.get('[name=monday-opens]').contains('08:00'); - cy.get('[name=monday-closes]').contains('16:00'); - - cy.get('[name=tuesday-opens]').contains('08:00'); - cy.get('[name=tuesday-closes]').contains('16:00'); - - cy.get('[name=wednesday-opens]').contains(''); - cy.get('[name=wednesday-closes]').contains(''); - - //If user disables edit all hours, disables editing - cy.get('[name=edit-all-day-hours]').click(); - cy.get('[name=monday-opens]').type('09:00'); - cy.get('[name=monday-closes]').type('17:00'); - - cy.get('[name=monday-opens]').contains('09:00'); - cy.get('[name=monday-closes]').contains('17:00'); - - cy.get('[name=tuesday-opens]').contains('08:00'); - cy.get('[name=tuesday-closes]').contains('16:00'); - }); - it('should add a toilet after submitting the form and retain all submitted data', () => { cy.visit('/loos/add?lat=51.92008861374827&lng=0.10883331298828125'); const toiletName = faker.word.adjective() + ' ' + faker.word.noun(); diff --git a/src/components/EntryForm.tsx b/src/components/EntryForm.tsx index b5c06875f..6717a0914 100644 --- a/src/components/EntryForm.tsx +++ b/src/components/EntryForm.tsx @@ -1,11 +1,15 @@ import React, { InputHTMLAttributes, useEffect, useState } from 'react'; import styled from '@emotion/styled'; -import { useForm, Controller, UseFormRegister } from 'react-hook-form'; +import { + useForm, + Controller, + UseFormRegister, + FieldValues, +} from 'react-hook-form'; import Image from 'next/legacy/image'; import isFunction from 'lodash/isFunction'; import omit from 'lodash/omit'; import pick from 'lodash/pick'; -import zipObject from 'lodash/zipObject'; import { ErrorMessage } from '@hookform/error-message'; import Container from '../components/Container'; import Notification from '../components/Notification'; @@ -14,7 +18,7 @@ import Text from '../components/Text'; import Spacer from '../components/Spacer'; import VisuallyHidden from '../components/VisuallyHidden'; import Switch from '../components/Switch'; -import { WEEKDAYS, isClosed, Weekdays } from '../lib/openingTimes'; +import { WEEKDAYS, isClosed } from '../lib/openingTimes'; import crosshair from '../../public/crosshair-small.svg'; import { useMapState } from './MapState'; @@ -22,17 +26,11 @@ import Icon from './Icon'; import { faAsterisk } from '@fortawesome/free-solid-svg-icons'; import { css } from '@emotion/react'; -type OpeningDayStates = - | `${Lowercase}-is-open` - | `${Lowercase}-opens` - | `${Lowercase}-closes`; - -const openingTimesFields = WEEKDAYS.flatMap((day): OpeningDayStates[] => { - const lowercaseDay = day.toLowerCase() as Lowercase; +const openingTimesFields = WEEKDAYS.flatMap((day: string) => { return [ - `${lowercaseDay}-is-open`, - `${lowercaseDay}-opens`, - `${lowercaseDay}-closes`, + `${day.toLowerCase()}-is-open`, + `${day.toLowerCase()}-opens`, + `${day.toLowerCase()}-closes`, ]; }); @@ -93,47 +91,6 @@ const RadioInput = styled.input` } `; -const defaultFormState = { - accessible: null, - name: '', - allGender: null, - women: null, - men: null, - radar: null, - automatic: null, - babyChange: null, - children: null, - paymentDetails: null, - isFree: null, - urinalOnly: null, - 'has-opening-times': false, - 'monday-is-open': false, - 'monday-opens': '', - 'monday-closes': '', - 'tuesday-is-open': false, - 'tuesday-opens': '', - 'tuesday-closes': '', - 'wednesday-is-open': false, - 'wednesday-opens': '', - 'wednesday-closes': '', - 'thursday-is-open': false, - 'thursday-opens': '', - 'thursday-closes': '', - 'friday-is-open': false, - 'friday-opens': '', - 'friday-closes': '', - 'saturday-is-open': false, - 'saturday-opens': '', - 'saturday-closes': '', - 'sunday-is-open': false, - 'sunday-opens': '', - 'sunday-closes': '', - geometry: { - coordinates: [0, 0], - }, - notes: '', -}; -type DefaultFormState = typeof defaultFormState; const Radio = React.forwardRef< HTMLInputElement, InputHTMLAttributes @@ -147,143 +104,121 @@ const Radio = React.forwardRef< }); interface Question { - field: keyof DefaultFormState; - subQuestion: string | React.ReactElement; + field: string; + label: string | React.ReactElement; value: boolean | null | ''; onChange?: React.FormEventHandler; } -interface Section { - register: UseFormRegister; +const Section: React.FC<{ + register: UseFormRegister; id: string; title: string; questions: Question[]; - children?: React.ReactNode; -} - -const Section: React.FC
= ({ - register, - id, - title, - questions, - children, -}) => { - return ( -
- - - - - - - - - - - - - - - - - - - - - {questions.map(({ field, subQuestion, value, onChange }) => { - return ( - - - {subQuestion} - - - - - - - - - - - - +}> = ({ register, id, title, questions, children }) => ( +
+
-

{title}

-
+ + + + + + + + + + + + + + + + + + + + {questions.map(({ field, label, value, onChange }) => { + return ( + + + {label} - ); - })} - -
+

{title}

+
+ + + + + + + + + + + + + ); + })} + + - {children} -
- ); -}; + {children} + +); const EntryForm = ({ title, loo, children, ...props }) => { const [noPayment, setNoPayment] = useState(loo.noPayment); const [mapState] = useMapState(); const [mapMoved, setMapMoved] = useState(false); - const [editAllHours, setEditAllHours] = useState(false); useEffect( function registerMapMovedHandler() { @@ -311,40 +246,17 @@ const EntryForm = ({ title, loo, children, ...props }) => { ? loo.openingTimes.map((x: string | unknown[]) => !isClosed(x)) : WEEKDAYS.map(() => false); - const changeAllHourValues = (event) => { - if (editAllHours) { - const formValues = getValues(openingTimesFields); - const openingHours = zipObject(openingTimesFields, formValues); - // Determine the target days - const targetDays = Object.keys(openingHours) - .filter((key) => openingHours[key] === true) - .map((day: OpeningDayStates) => day.replace('-is-open', '')); - // Determine the target fields based on the current target - const target = event.target.name.endsWith('-opens') - ? '-opens' - : '-closes'; - const targetFields = targetDays.flatMap((day) => - openingTimesFields.filter( - (field) => field.includes(day) && field.includes(target) - ) - ); - //This probably can be done in a more performant way - targetFields.forEach((targetField) => { - setValue(targetField, event.target.value); - }); - } - }; - const { register, control, handleSubmit, formState, setValue, getValues } = - useForm({ criteriaMode: 'all', defaultValues: defaultFormState }); + useForm({ criteriaMode: 'all' }); // read the formState before render to subscribe the form state through Proxy const { isDirty, dirtyFields } = formState; - const onSubmit = ( - data: { - [x: string]: unknown; - } & DefaultFormState - ) => { + + const onSubmit = (data: { + [x: string]: unknown; + isFree: string; + geometry: { coordinates: string[] }; + }) => { const dirtyFieldNames = Object.keys(dirtyFields); // only include fields which have been modified @@ -374,8 +286,8 @@ const EntryForm = ({ title, loo, children, ...props }) => { // map geometry data to expected structure // eslint-disable-next-line functional/immutable-data transformed.location = { - lat: data.geometry.coordinates[0], - lng: data.geometry.coordinates[1], + lat: parseFloat(data.geometry.coordinates[0]), + lng: parseFloat(data.geometry.coordinates[1]), }; // remove payment details if the isFree field value has changed and is now @@ -393,14 +305,13 @@ const EntryForm = ({ title, loo, children, ...props }) => { ) { if (data['has-opening-times']) { const openingTimes = WEEKDAYS.map((day) => { - const lowercaseDay = day.toLowerCase(); - if (!data[`${lowercaseDay}-is-open`]) { + if (!data[`${day.toLowerCase()}-is-open`]) { return []; } return [ - data[`${lowercaseDay}-opens`], - data[`${lowercaseDay}-closes`], + data[`${day.toLowerCase()}-opens`], + data[`${day.toLowerCase()}-closes`], ]; }); @@ -539,22 +450,22 @@ const EntryForm = ({ title, loo, children, ...props }) => { questions={[ { field: 'women', - subQuestion: 'Women?', + label: 'Women?', value: loo['women'], }, { field: 'men', - subQuestion: 'Men?', + label: 'Men?', value: loo['men'], }, { field: 'accessible', - subQuestion: 'Is there a disabled toilet?', + label: 'Is there a disabled toilet?', value: loo['accessible'], }, { field: 'radar', - subQuestion: 'Does this toilet have a RADAR lock?', + label: 'Does this toilet have a RADAR lock?', value: loo['radar'], }, ]} @@ -569,27 +480,27 @@ const EntryForm = ({ title, loo, children, ...props }) => { questions={[ { field: 'allGender', - subQuestion: 'A gender neutral toilet?', + label: 'A gender neutral toilet?', value: loo['allGender'], }, { field: 'children', - subQuestion: 'A children’s toilet?', + label: 'A children’s toilet?', value: loo['children'], }, { field: 'babyChange', - subQuestion: 'Baby Changing?', + label: 'Baby Changing?', value: loo['babyChange'], }, { field: 'urinalOnly', - subQuestion: 'Only a urinal?', + label: 'Only a urinal?', value: loo['urinalOnly'], }, { field: 'automatic', - subQuestion: 'An automatic / self-cleaning toilet?', + label: 'An automatic / self-cleaning toilet?', value: loo['automatic'], }, ]} @@ -604,9 +515,7 @@ const EntryForm = ({ title, loo, children, ...props }) => { questions={[ { field: 'isFree', - subQuestion: ( - Is this toilet free? - ), + label: Is this toilet free?, value: noPayment === null ? '' : noPayment, onChange: (event) => { const input = event.target as HTMLInputElement; @@ -665,7 +574,7 @@ const EntryForm = ({ title, loo, children, ...props }) => { checked={field.value} onChange={field.onChange} onClick={field.onChange} - value={`${field.value}`} + value={field.value} /> )} /> @@ -682,27 +591,8 @@ const EntryForm = ({ title, loo, children, ...props }) => { {getValues('has-opening-times') && (
    - - setEditAllHours(!editAllHours)} - /> - - - {WEEKDAYS.map((day, index) => { - const lowercaseDay = day.toLowerCase() as Lowercase; - const id = `heading-${lowercaseDay}`; + const id = `heading-${day.toLowerCase()}`; return ( { alignItems="center" justifyContent="space-between" mt={index === 0 ? undefined : 2} - pl={[2, 4]} - pr={[2, 4]} >

    {day}

    @@ -723,12 +611,11 @@ const EntryForm = ({ title, loo, children, ...props }) => { display="flex" justifyContent="space-between" width={['auto', '50%']} - mt={2} > ( @@ -737,7 +624,7 @@ const EntryForm = ({ title, loo, children, ...props }) => { checked={field.value} onChange={field.onChange} onClick={field.onChange} - value={`${field.value}`} + value={field.value} /> )} /> @@ -745,7 +632,7 @@ const EntryForm = ({ title, loo, children, ...props }) => { - {getValues(`${lowercaseDay}-is-open`) ? ( + {getValues(`${day.toLowerCase()}-is-open`) ? ( { ? loo.openingTimes[index][0] : undefined } - name={`${lowercaseDay}-opens`} - {...register(`${lowercaseDay}-opens`, { - onChange: changeAllHourValues, + name={`${day.toLowerCase()}-opens`} + {...register(`${day.toLowerCase()}-opens`, { required: `Please specify an opening time on ${day}`, })} /> ( <> { ? loo.openingTimes[index][1] : undefined } - name={`${lowercaseDay}-closes`} - {...register(`${lowercaseDay}-closes`, { - onChange: changeAllHourValues, + name={`${day.toLowerCase()}-closes`} + {...register(`${day.toLowerCase()}-closes`, { required: `Please specify a closing time on ${day}`, })} /> ( <>