-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue 442/basic information form #533
Changes from 52 commits
5514856
1c08e7a
73754fa
9b8cfcc
d617a48
69a2443
bf92d76
375c3e7
9da1545
1e5be1f
446f19f
5eee3e8
587df65
bb31619
b37161b
5c3df18
c9bce76
02fcff3
ac5a9a9
0c8f978
ec4d5cf
d886160
7854e89
7462f68
fc194e1
7776ac5
f82472f
a414027
6879a85
68e7872
fb53ae1
e981037
6da2248
f9380b8
4816f96
2f28c17
1e13721
2f8b8b2
e983684
ae466e2
6903809
011e334
c342b40
df4278d
5a4a72b
35f633e
e3bbdc1
b8aacc8
362d961
52e5050
73b0474
ef025be
d72e6ea
719ca65
343beee
fbb810b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,182 @@ | ||
// React Imports | ||
import React from 'react'; | ||
// MUI Imports | ||
import Typography from '@mui/material/Typography'; | ||
import React, { useState, useEffect } from 'react'; | ||
// Material UI Imports | ||
import Button from '@mui/material/Button'; | ||
import CheckIcon from '@mui/icons-material/Check'; | ||
import ClearIcon from '@mui/icons-material/Clear'; | ||
import FormControl from '@mui/material/FormControl'; | ||
import FormHelperText from '@mui/material/FormHelperText'; | ||
import Grid from '@mui/material/Grid'; | ||
import InputLabel from '@mui/material/InputLabel'; | ||
import MenuItem from '@mui/material/MenuItem'; | ||
import Select from '@mui/material/Select'; | ||
import TextField from '@mui/material/TextField'; | ||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; | ||
import { DatePicker } from '@mui/x-date-pickers/DatePicker'; | ||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; | ||
import dayjs from 'dayjs'; | ||
// Hook Imports | ||
import { useCivicProfile } from '@hooks'; | ||
// Component Imports | ||
import { FormSection } from '../Form'; | ||
|
||
const BasicInfo = () => <Typography>Basic Info</Typography>; | ||
/** | ||
* BasicInfo Component - A form to fill out basic user info | ||
* | ||
* @memberof CivicProfileForms | ||
* @name BasicInfo | ||
* @returns {React.JSX.Element} The BasicInfo Component | ||
*/ | ||
const BasicInfo = () => { | ||
const { data, add, isSuccess, storedDataset, refetch } = useCivicProfile(); | ||
const [formData, setFormData] = useState({ | ||
firstName: '', | ||
lastName: '', | ||
dateOfBirth: null, | ||
gender: '' | ||
}); | ||
|
||
useEffect(() => { | ||
if (isSuccess) { | ||
setFormData((prevFormData) => ({ ...prevFormData, ...data })); | ||
} | ||
}, [isSuccess, data]); | ||
useEffect(() => { | ||
if (!storedDataset) { | ||
refetch(); | ||
} | ||
}, [storedDataset]); | ||
|
||
const handleChange = (e) => { | ||
if (e.$isDayjsObject) { | ||
setFormData((prevFormData) => ({ | ||
...prevFormData, | ||
dateOfBirth: e.toDate() | ||
})); | ||
} else if (e.target) { | ||
const { name, value } = e.target; | ||
setFormData((prevFormData) => ({ ...prevFormData, [name]: value })); | ||
} | ||
}; | ||
|
||
const handleSubmit = (e) => { | ||
e.preventDefault(); | ||
if (!isSuccess || !storedDataset) { | ||
return; | ||
} | ||
add(formData); | ||
}; | ||
|
||
const handleClear = () => { | ||
setFormData((prevFormData) => ({ | ||
...prevFormData, | ||
firstName: '', | ||
lastName: '', | ||
dateOfBirth: null, | ||
gender: '' | ||
})); | ||
}; | ||
|
||
return ( | ||
<FormSection title="Basic Information"> | ||
<form aria-labelledby="add-contact-form" onSubmit={handleSubmit} autoComplete="off"> | ||
<Grid container spacing={2}> | ||
<Grid item xs={12} md={6}> | ||
<TextField | ||
id="hmis-basic-info-first-name" | ||
name="firstName" | ||
label="Legal first name" | ||
onChange={handleChange} | ||
value={formData.firstName} | ||
fullWidth | ||
autoFocus | ||
/> | ||
</Grid> | ||
<Grid item xs={12} md={6}> | ||
<TextField | ||
id="hmis-basic-info-last-name" | ||
name="lastName" | ||
label="Legal last name" | ||
onChange={handleChange} | ||
value={formData.lastName} | ||
fullWidth | ||
/> | ||
</Grid> | ||
<Grid item xs={12} md={6}> | ||
<FormControl fullWidth> | ||
<LocalizationProvider dateAdapter={AdapterDayjs}> | ||
<DatePicker | ||
id="hmis-basic-info-date-of-birth" | ||
name="dateOfBirth" | ||
label="Date of birth" | ||
onChange={handleChange} | ||
value={formData.dateOfBirth ? dayjs(formData.dateOfBirth) : null} | ||
format="YYYY-MM-DD" | ||
type="date" | ||
disableFuture | ||
openTo="year" | ||
views={['year', 'month', 'day']} | ||
/> | ||
</LocalizationProvider> | ||
<FormHelperText>YYYY-MM-DD</FormHelperText> | ||
</FormControl> | ||
</Grid> | ||
<Grid item xs={12} md={6}> | ||
<FormControl fullWidth> | ||
<InputLabel id="hmis-basic-info-gender">Gender</InputLabel> | ||
<Select | ||
id="hmis-basic-info-gender-select" | ||
name="gender" | ||
label="Gender" | ||
onChange={handleChange} | ||
value={formData.gender} | ||
labelId="hmis-basic-info-gender" | ||
defaultValue="" | ||
> | ||
<MenuItem value={0}>Female</MenuItem> | ||
<MenuItem value={1}>Male</MenuItem> | ||
<MenuItem value={2}>Transgender male to female</MenuItem> | ||
<MenuItem value={3}>Transgender female to male</MenuItem> | ||
<MenuItem value={4}>Don't identify as male, female or transgender</MenuItem> | ||
<MenuItem value={8}>Don't know</MenuItem> | ||
<MenuItem value={9}>Decline to answer</MenuItem> | ||
</Select> | ||
</FormControl> | ||
</Grid> | ||
<Grid item xs={12} md={6}> | ||
<Button | ||
variant="outlined" | ||
type="button" | ||
label="Clear button" | ||
color="error" | ||
startIcon={<ClearIcon />} | ||
fullWidth | ||
sx={{ borderRadius: '20px' }} | ||
onClick={handleClear} | ||
aria-label="Clear button" | ||
> | ||
Clear | ||
</Button> | ||
</Grid> | ||
<Grid item xs={12} md={6}> | ||
<Button | ||
variant="contained" | ||
type="submit" | ||
label="Submit button" | ||
color="primary" | ||
startIcon={<CheckIcon />} | ||
fullWidth | ||
sx={{ borderRadius: '20px' }} | ||
disabled={!isSuccess} | ||
aria-label="Submit button" | ||
> | ||
Submit | ||
</Button> | ||
</Grid> | ||
</Grid> | ||
</form> | ||
</FormSection> | ||
); | ||
}; | ||
|
||
export default BasicInfo; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,84 @@ | ||
import React from 'react'; | ||
import { render } from '@testing-library/react'; | ||
import { expect, it, describe } from 'vitest'; | ||
import { vi, expect, it, describe } from 'vitest'; | ||
import { BasicInfo } from '@components/CivicProfileForms'; | ||
import { useCivicProfile } from '@hooks'; | ||
import userEvent from '@testing-library/user-event'; | ||
|
||
describe('Basic Info Form', () => { | ||
vi.mock('@hooks', async () => { | ||
const actual = await vi.importActual('@hooks'); | ||
|
||
return { | ||
...actual, | ||
useCivicProfile: vi.fn() | ||
}; | ||
}); | ||
|
||
describe('Basic info form', () => { | ||
it('renders', () => { | ||
const { getByText } = render(<BasicInfo />); | ||
expect(getByText('Basic Info')).not.toBeNull(); | ||
useCivicProfile.mockReturnValue({ data: {}, isSuccess: true, refetch: vi.fn() }); | ||
const { getByRole } = render(<BasicInfo />); | ||
const legalFirstName = getByRole('textbox', { name: 'Legal first name' }); | ||
expect(legalFirstName).not.toBeNull(); | ||
}); | ||
|
||
it('clears all input fields when you click the clear button', async () => { | ||
// const user = userEvent.setup(); | ||
const mockClear = vi.fn(); | ||
const basicInfoProfile = { | ||
legalFirstName: 'Jane', | ||
legalLastName: 'Doe', | ||
legalDOB: '1980-12-15', | ||
legalGender: 1 | ||
}; | ||
useCivicProfile.mockReturnValue({ | ||
add: mockClear, | ||
isSuccess: true, | ||
storedDataset: {}, | ||
refetch: vi.fn() | ||
}); | ||
const { getByRole } = render(<BasicInfo />); | ||
const legalFirstName = getByRole('textbox', { name: 'Legal first name' }); | ||
const legalLastName = getByRole('textbox', { name: 'Legal last name' }); | ||
const legalDOB = getByRole('textbox', { name: 'Choose date' }); | ||
const legalGender = getByRole('combobox', { name: 'Gender' }); | ||
const clearButton = getByRole('button', { name: 'Clear button' }); | ||
|
||
await userEvent.type(legalFirstName, basicInfoProfile.legalFirstName); | ||
await userEvent.type(legalLastName, basicInfoProfile.legalLastName); | ||
await userEvent.type(legalDOB, basicInfoProfile.legalDOB); | ||
await userEvent.type(legalGender, `${basicInfoProfile.legalGender}{enter}`); | ||
|
||
await userEvent.click(clearButton); | ||
|
||
expect(legalFirstName.value).toBe(''); | ||
expect(legalLastName.value).toBe(''); | ||
expect(legalDOB.value).toBe(null); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this test is failing because the input field for the date picker is not your typical text field. You can't use the same approach to test the data picker. The same would be said for If you'd like, we can open up another issue to write unit tests for these inputs. |
||
expect(legalGender.value).toBe(''); | ||
}); | ||
|
||
// TODO: Resolve test not passing | ||
// it('submits a basic info profile update when you click the submit button', async () => { | ||
// const user = userEvent.setup(); | ||
// const mockAdd = vi.fn(); | ||
// const basicInfoProfile = { | ||
// legalFirstName: 'Jane', | ||
// legalLastName: 'Doe', | ||
// legalDOB: '1980-12-15', | ||
// legalGender: 1 | ||
// }; | ||
// useCivicProfile.mockReturnValue({ add: mockAdd, isSuccess: true }); | ||
// const { getByRole } = render(<BasicInfo />); | ||
// const legalFirstName = getByRole('textbox', { name: 'Legal first name' }); | ||
// const legalLastName = getByRole('textbox', { name: 'Legal last name' }); | ||
// const legalDOB = getByRole('textbox', { name: 'Choose date' }); | ||
// const legalGender = getByRole('combobox', { name: 'Gender' }); | ||
// const submitButton = getByRole('button', { name: 'Submit button' }); | ||
// await user.type(legalFirstName, basicInfoProfile.legalFirstName); | ||
// await user.type(legalLastName, basicInfoProfile.legalLastName); | ||
// await user.type(legalDOB, basicInfoProfile.legalDOB); | ||
// await user.type(legalGender, `${basicInfoProfile.legalGender}{enter}`); | ||
// await user.click(submitButton); | ||
// expect(mockAdd).toBeCalledWith(basicInfoProfile); | ||
// }); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not too big of an issue since the feature appears to be working as intended, however, MUI is complaining about the input fields if it's not using one of the values from the option elements.
Think we could simply set the defaultValue, state, and clear value for gender as the number
9
to work around the problem. So Line 36, Line 75, and Line 133.