Skip to content

Commit

Permalink
fix: display error message when myinfo child fields are not filled in (
Browse files Browse the repository at this point in the history
…#6735)

* feat: add error message for child subfields

* fix: correct typo

* feat: validate upon input change

* feat: try ref

* feat: compare ref by id

* fix: check for ref on error and remove console.log statements

* fix: remove comment

---------

Co-authored-by: Ken <ken@open.gov.sg>
  • Loading branch information
wanlingt and KenLSM authored Sep 21, 2023
1 parent baf4aa8 commit 860aad7
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useCallback, useEffect, useMemo } from 'react'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import {
FieldArrayWithId,
FieldError,
useFieldArray,
UseFieldArrayRemove,
useFormContext,
Expand All @@ -20,6 +21,7 @@ import {
VisuallyHidden,
VStack,
} from '@chakra-ui/react'
import { get } from 'lodash'
import simplur from 'simplur'

import { DATE_DISPLAY_FORMAT } from '~shared/constants/dates'
Expand All @@ -37,6 +39,7 @@ import { createChildrenValidationRules } from '~utils/fieldValidation'
import { Button } from '~components/Button/Button'
import { DatePicker } from '~components/DatePicker'
import { SingleSelect } from '~components/Dropdown/SingleSelect'
import FormErrorMessage from '~components/FormControl/FormErrorMessage'
import { FormLabel } from '~components/FormControl/FormLabel/FormLabel'
import { IconButton } from '~components/IconButton/IconButton'

Expand Down Expand Up @@ -71,9 +74,11 @@ export const ChildrenCompoundField = ({
)

const formContext = useFormContext<ChildrenCompoundFieldInputs>()
const { isSubmitting } = useFormState<ChildrenCompoundFieldInputs>({
const { isSubmitting, errors } = useFormState<ChildrenCompoundFieldInputs>({
name: schema._id,
})
const error: FieldError[][] | undefined = get(errors, schema._id)?.child
const childError: FieldError[] | undefined = error ? error[0] : undefined

const { fields, append, remove } = useFieldArray<ChildrenCompoundFieldInputs>(
{
Expand Down Expand Up @@ -142,6 +147,7 @@ export const ChildrenCompoundField = ({
myInfoChildrenBirthRecords,
isSubmitting,
formContext,
error: childError,
}}
/>
))}
Expand Down Expand Up @@ -188,6 +194,7 @@ interface ChildrenBodyProps {

// eslint-disable-next-line @typescript-eslint/no-explicit-any
formContext: UseFormReturn<ChildrenCompoundFieldInputs, any>
error: FieldError[] | undefined
}

const ChildrenBody = ({
Expand All @@ -200,6 +207,7 @@ const ChildrenBody = ({
myInfoChildrenBirthRecords,
isSubmitting,
formContext,
error,
}: ChildrenBodyProps): JSX.Element => {
const { register, getValues, setValue, watch } = formContext

Expand All @@ -213,10 +221,19 @@ const ChildrenBody = ({
[schema],
)

const { onChange: selectOnChange, ...selectRest } = register(
childNamePath,
validationRules,
)
const {
ref: childNameRegisterRef,
onChange: selectOnChange,
...selectRest
} = register(childNamePath, validationRules)

const childNameRef = useRef<HTMLInputElement | null>(null)

const childNameError = error
? error.find(
(e) => (e?.ref as HTMLInputElement)?.id === childNameRef.current?.id,
)
: undefined

const childName = watch(childNamePath) as unknown as string

Expand Down Expand Up @@ -305,20 +322,29 @@ const ChildrenBody = ({
<FormLabel gridArea="formlabel">Child</FormLabel>
<Flex align="stretch" alignItems="stretch" justify="space-between">
<Box flexGrow={10}>
<SingleSelect
isRequired
{...selectRest}
placeholder={"Select your child's name"}
colorScheme={`theme-${colorTheme}`}
items={childNameValues}
value={childName}
isDisabled={isSubmitting}
onChange={(name) => {
// This is bad practice but we have no choice because our
// custom Select doesn't forward the event.
setValue(childNamePath, name)
}}
/>
<FormControl key={field.id} isRequired isInvalid={!!childNameError}>
<SingleSelect
isRequired
{...selectRest}
placeholder={"Select your child's name"}
colorScheme={`theme-${colorTheme}`}
items={childNameValues}
value={childName}
isDisabled={isSubmitting}
onChange={(name) => {
// This is bad practice but we have no choice because our
// custom Select doesn't forward the event.
setValue(childNamePath, name, { shouldValidate: true })
}}
ref={(e) => {
childNameRegisterRef(e)
if (e) {
childNameRef.current = e
}
}}
/>
<FormErrorMessage>{childNameError?.message}</FormErrorMessage>
</FormControl>
</Box>
<IconButton
variant="clear"
Expand All @@ -343,6 +369,7 @@ const ChildrenBody = ({
const key = `${field.id}+${index}`
const fieldPath = `${schema._id}.child.${currChildBodyIdx}.${index}`
const myInfoValue = getChildAttr(subField)
const childrenSubFieldError = error ? error[index] : undefined

// We want to format the date by converting the value from a myinfo format to
// a format used by our date fields
Expand All @@ -356,20 +383,28 @@ const ChildrenBody = ({
// We need to do this as the underlying data is not updated
// by the field's value, but rather by onChange, which we did
// not trigger via prefill.
setValue(fieldPath, myInfoFormattedValue)
setValue(fieldPath, myInfoFormattedValue, { shouldValidate: true })
}
const isDisabled = isSubmitting || !!myInfoValue
switch (subField) {
case MyInfoChildAttributes.ChildBirthCertNo: {
return (
<FormControl key={key} isDisabled={isDisabled} isRequired>
<FormControl
key={key}
isDisabled={isDisabled}
isRequired
isInvalid={!!childrenSubFieldError}
>
<FormLabel useMarkdownForDescription gridArea="formlabel">
{MYINFO_ATTRIBUTE_MAP[subField].description}
</FormLabel>
<ChakraInput
{...register(fieldPath, validationRules)}
value={value}
/>
<FormErrorMessage>
{childrenSubFieldError?.message}
</FormErrorMessage>
</FormControl>
)
}
Expand All @@ -378,7 +413,12 @@ const ChildrenBody = ({
case MyInfoChildAttributes.ChildRace:
case MyInfoChildAttributes.ChildSecondaryRace: {
return (
<FormControl key={key} isDisabled={isDisabled} isRequired>
<FormControl
key={key}
isDisabled={isDisabled}
isRequired
isInvalid={!!childrenSubFieldError}
>
<FormLabel useMarkdownForDescription gridArea="formlabel">
{MYINFO_ATTRIBUTE_MAP[subField].description}
</FormLabel>
Expand All @@ -391,16 +431,24 @@ const ChildrenBody = ({
onChange={(option) =>
// This is bad practice but we have no choice because our
// custom Select doesn't forward the event.
setValue(fieldPath, option)
setValue(fieldPath, option, { shouldValidate: true })
}
/>
<FormErrorMessage>
{childrenSubFieldError?.message}
</FormErrorMessage>
</FormControl>
)
}
case MyInfoChildAttributes.ChildDateOfBirth: {
const { onChange, ...rest } = register(fieldPath, validationRules)
return (
<FormControl key={key} isDisabled={isDisabled} isRequired>
<FormControl
key={key}
isDisabled={isDisabled}
isRequired
isInvalid={!!childrenSubFieldError}
>
<FormLabel useMarkdownForDescription gridArea="formlabel">
{MYINFO_ATTRIBUTE_MAP[subField].description}
</FormLabel>
Expand All @@ -409,10 +457,13 @@ const ChildrenBody = ({
displayFormat={DATE_DISPLAY_FORMAT}
inputValue={value}
onInputValueChange={(date) => {
setValue(fieldPath, date)
setValue(fieldPath, date, { shouldValidate: true })
}}
colorScheme={`theme-${colorTheme}`}
/>
<FormErrorMessage>
{childrenSubFieldError?.message}
</FormErrorMessage>
</FormControl>
)
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/templates/Field/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export type ChildrenCompoundFieldValues = {
// Each subarray represents one child value
// e.g.
// child : [['NAME', 'T12345678Z', 'VACCINATED'], ... ]
// cildFields: [name, bc number, vaxx status]
// childFields: [name, bc number, vaxx status]
child: string[][]
// Array of attribute names
childFields: MyInfoChildAttributes[]
Expand Down

0 comments on commit 860aad7

Please sign in to comment.