From 3eea135c9b643a181c54da37a0fb70f55571f96d Mon Sep 17 00:00:00 2001 From: Jack Meyer Date: Wed, 15 Jan 2020 19:40:22 -0600 Subject: [PATCH] feat(patients): add tabs in patient view --- src/HospitalRun.tsx | 1 - .../patients/view/GeneralInformation.test.tsx | 137 ++++++++++++++++ .../patients/view/ViewPatient.test.tsx | 149 +++++------------- src/locales/en-US/translation.json | 2 +- src/patients/view/GeneralInformation.tsx | 145 +++++++++++++++++ src/patients/view/ViewPatient.tsx | 144 ++--------------- 6 files changed, 339 insertions(+), 239 deletions(-) create mode 100644 src/__tests__/patients/view/GeneralInformation.test.tsx create mode 100644 src/patients/view/GeneralInformation.tsx diff --git a/src/HospitalRun.tsx b/src/HospitalRun.tsx index 4a4631a779..b1e1b212c0 100644 --- a/src/HospitalRun.tsx +++ b/src/HospitalRun.tsx @@ -43,7 +43,6 @@ const HospitalRun = () => { /> diff --git a/src/__tests__/patients/view/GeneralInformation.test.tsx b/src/__tests__/patients/view/GeneralInformation.test.tsx new file mode 100644 index 0000000000..2804fff888 --- /dev/null +++ b/src/__tests__/patients/view/GeneralInformation.test.tsx @@ -0,0 +1,137 @@ +import '../../../__mocks__/matchMediaMock' +import React from 'react' +import { mount } from 'enzyme' +import { act } from 'react-dom/test-utils' +import GeneralInformation from 'patients/view/GeneralInformation' +import Patient from '../../../model/Patient' + +describe('General Information', () => { + const patient = { + id: '123', + prefix: 'prefix', + givenName: 'givenName', + familyName: 'familyName', + suffix: 'suffix', + sex: 'male', + type: 'charity', + occupation: 'occupation', + preferredLanguage: 'preferredLanguage', + phoneNumber: 'phoneNumber', + email: 'email@email.com', + address: 'address', + friendlyId: 'P00001', + dateOfBirth: new Date().toISOString(), + } as Patient + + const setup = () => { + const wrapper = mount() + + wrapper.update() + return wrapper + } + + beforeEach(() => { + jest.restoreAllMocks() + }) + + it('should render the sex select', () => { + const wrapper = setup() + + const sexSelect = wrapper.findWhere((w: any) => w.prop('name') === 'sex') + expect(sexSelect.prop('value')).toEqual(patient.sex) + expect(sexSelect.prop('label')).toEqual('patient.sex') + expect(sexSelect.prop('isEditable')).toBeFalsy() + }) + + it('should render the patient type select', () => { + const wrapper = setup() + + const typeSelect = wrapper.findWhere((w: any) => w.prop('name') === 'type') + expect(typeSelect.prop('value')).toEqual(patient.type) + expect(typeSelect.prop('label')).toEqual('patient.type') + expect(typeSelect.prop('isEditable')).toBeFalsy() + }) + + it('should render the age of the patient', () => { + const wrapper = setup() + + const ageInput = wrapper.findWhere((w) => w.prop('name') === 'age') + expect(ageInput.prop('value')).toEqual('0') + expect(ageInput.prop('label')).toEqual('patient.age') + expect(ageInput.prop('isEditable')).toBeFalsy() + }) + + it('should render the date of the birth of the patient', () => { + const wrapper = setup() + + const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'dateOfBirth') + expect(dateOfBirthInput.prop('value')).toEqual(new Date(patient.dateOfBirth)) + expect(dateOfBirthInput.prop('label')).toEqual('patient.dateOfBirth') + expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() + }) + + it('should render the occupation of the patient', () => { + const wrapper = setup() + + const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'occupation') + expect(dateOfBirthInput.prop('value')).toEqual(patient.occupation) + expect(dateOfBirthInput.prop('label')).toEqual('patient.occupation') + expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() + }) + + it('should render the preferred language of the patient', () => { + const wrapper = setup() + + const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'preferredLanguage') + expect(dateOfBirthInput.prop('value')).toEqual(patient.preferredLanguage) + expect(dateOfBirthInput.prop('label')).toEqual('patient.preferredLanguage') + expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() + }) + + it('should render the phone number of the patient', () => { + const wrapper = setup() + + const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'phoneNumber') + expect(dateOfBirthInput.prop('value')).toEqual(patient.phoneNumber) + expect(dateOfBirthInput.prop('label')).toEqual('patient.phoneNumber') + expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() + }) + + it('should render the email of the patient', () => { + const wrapper = setup() + + const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'email') + expect(dateOfBirthInput.prop('value')).toEqual(patient.email) + expect(dateOfBirthInput.prop('label')).toEqual('patient.email') + expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() + }) + + it('should render the address of the patient', () => { + const wrapper = setup() + + const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'address') + expect(dateOfBirthInput.prop('value')).toEqual(patient.address) + expect(dateOfBirthInput.prop('label')).toEqual('patient.address') + expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() + }) + + it('should render the age and date of birth as approximate if patient.isApproximateDateOfBirth is true', async () => { + let wrapper: any + patient.isApproximateDateOfBirth = true + await act(async () => { + wrapper = await mount() + }) + + wrapper.update() + + const ageInput = wrapper.findWhere((w: any) => w.prop('name') === 'age') + expect(ageInput.prop('value')).toEqual('0') + expect(ageInput.prop('label')).toEqual('patient.approximateAge') + expect(ageInput.prop('isEditable')).toBeFalsy() + + const dateOfBirthInput = wrapper.findWhere((w: any) => w.prop('name') === 'dateOfBirth') + expect(dateOfBirthInput.prop('value')).toEqual(new Date(patient.dateOfBirth)) + expect(dateOfBirthInput.prop('label')).toEqual('patient.approximateDateOfBirth') + expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() + }) +}) diff --git a/src/__tests__/patients/view/ViewPatient.test.tsx b/src/__tests__/patients/view/ViewPatient.test.tsx index 08d829b75b..dfa163961c 100644 --- a/src/__tests__/patients/view/ViewPatient.test.tsx +++ b/src/__tests__/patients/view/ViewPatient.test.tsx @@ -4,7 +4,10 @@ import { Provider } from 'react-redux' import { mount } from 'enzyme' import { mocked } from 'ts-jest/utils' import { act } from 'react-dom/test-utils' -import { MemoryRouter, Route } from 'react-router-dom' +import { MemoryRouter, Route, BrowserRouter, Router } from 'react-router-dom' +import { TabsHeader, Tab } from '@hospitalrun/components' +import GeneralInformation from 'patients/view/GeneralInformation' +import { createMemoryHistory } from 'history' import Patient from '../../../model/Patient' import PatientRepository from '../../../clients/db/PatientRepository' import * as titleUtil from '../../../page-header/useTitle' @@ -29,6 +32,7 @@ describe('ViewPatient', () => { dateOfBirth: new Date().toISOString(), } as Patient + let history = createMemoryHistory() const setup = () => { jest.spyOn(PatientRepository, 'find') const mockedPatientRepository = mocked(PatientRepository, true) @@ -39,13 +43,15 @@ describe('ViewPatient', () => { }), })) + history = createMemoryHistory() + history.push('/patients/123') const wrapper = mount( - + - + , ) @@ -67,125 +73,48 @@ describe('ViewPatient', () => { ) }) - it('should render the sex select', () => { - const wrapper = setup() - - const sexSelect = wrapper.findWhere((w) => w.prop('name') === 'sex') - expect(sexSelect.prop('value')).toEqual(patient.sex) - expect(sexSelect.prop('label')).toEqual('patient.sex') - expect(sexSelect.prop('isEditable')).toBeFalsy() - }) - - it('should render the patient type select', () => { - const wrapper = setup() - - const typeSelect = wrapper.findWhere((w) => w.prop('name') === 'type') - expect(typeSelect.prop('value')).toEqual(patient.type) - expect(typeSelect.prop('label')).toEqual('patient.type') - expect(typeSelect.prop('isEditable')).toBeFalsy() - }) - - it('should render the age of the patient', () => { - const wrapper = setup() - - const ageInput = wrapper.findWhere((w) => w.prop('name') === 'age') - expect(ageInput.prop('value')).toEqual('0') - expect(ageInput.prop('label')).toEqual('patient.age') - expect(ageInput.prop('isEditable')).toBeFalsy() - }) - - it('should render the date of the birth of the patient', () => { - const wrapper = setup() - - const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'dateOfBirth') - expect(dateOfBirthInput.prop('value')).toEqual(new Date(patient.dateOfBirth)) - expect(dateOfBirthInput.prop('label')).toEqual('patient.dateOfBirth') - expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() - }) - - it('should render the occupation of the patient', () => { - const wrapper = setup() - - const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'occupation') - expect(dateOfBirthInput.prop('value')).toEqual(patient.occupation) - expect(dateOfBirthInput.prop('label')).toEqual('patient.occupation') - expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() - }) - - it('should render the preferred language of the patient', () => { - const wrapper = setup() - - const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'preferredLanguage') - expect(dateOfBirthInput.prop('value')).toEqual(patient.preferredLanguage) - expect(dateOfBirthInput.prop('label')).toEqual('patient.preferredLanguage') - expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() - }) - - it('should render the phone number of the patient', () => { - const wrapper = setup() - - const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'phoneNumber') - expect(dateOfBirthInput.prop('value')).toEqual(patient.phoneNumber) - expect(dateOfBirthInput.prop('label')).toEqual('patient.phoneNumber') - expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() - }) + it('should render a tabs header with the correct tabs', async () => { + let wrapper: any + await act(async () => { + wrapper = await setup() + }) - it('should render the email of the patient', () => { - const wrapper = setup() + const tabsHeader = wrapper.find(TabsHeader) + const tabs = tabsHeader.find(Tab) + expect(tabsHeader).toHaveLength(1) - const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'email') - expect(dateOfBirthInput.prop('value')).toEqual(patient.email) - expect(dateOfBirthInput.prop('label')).toEqual('patient.email') - expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() + expect(tabs).toHaveLength(1) + expect(tabs.at(0).prop('label')).toEqual('patient.generalInformation') }) - it('should render the address of the patient', () => { - const wrapper = setup() + it('should mark the general information tab as active and render the general information component when route is /patients/:id', async () => { + let wrapper: any + await act(async () => { + wrapper = await setup() + }) - const dateOfBirthInput = wrapper.findWhere((w) => w.prop('name') === 'address') - expect(dateOfBirthInput.prop('value')).toEqual(patient.address) - expect(dateOfBirthInput.prop('label')).toEqual('patient.address') - expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() + const tabsHeader = wrapper.find(TabsHeader) + const tabs = tabsHeader.find(Tab) + const generalInformation = wrapper.find(GeneralInformation) + expect(tabs.at(0).prop('active')).toBeTruthy() + expect(generalInformation).toHaveLength(1) + expect(generalInformation.prop('patient')).toEqual(patient) }) - it('should render the age and date of birth as approximate if patient.isApproximateDateOfBirth is true', async () => { - jest.restoreAllMocks() - const patientWithApproximateDob = { - ...patient, - isApproximateDateOfBirth: true, - } as Patient - jest.spyOn(PatientRepository, 'find') - const mockedPatientRepository = mocked(PatientRepository, true) - mockedPatientRepository.find.mockResolvedValue(patientWithApproximateDob) - jest.mock('react-router-dom', () => ({ - useParams: () => ({ - id: '123', - }), - })) - + it('should navigate /patients/:id when the general information tab is clicked', async () => { let wrapper: any await act(async () => { - wrapper = await mount( - - - - - - - , - ) + wrapper = await setup() }) - wrapper.update() + const tabsHeader = wrapper.find(TabsHeader) + const tabs = tabsHeader.find(Tab) + await act(async () => { + await (tabs.at(0).prop('onClick') as any)() + }) - const ageInput = wrapper.findWhere((w: any) => w.prop('name') === 'age') - expect(ageInput.prop('value')).toEqual('0') - expect(ageInput.prop('label')).toEqual('patient.approximateAge') - expect(ageInput.prop('isEditable')).toBeFalsy() + wrapper.update() - const dateOfBirthInput = wrapper.findWhere((w: any) => w.prop('name') === 'dateOfBirth') - expect(dateOfBirthInput.prop('value')).toEqual(new Date(patient.dateOfBirth)) - expect(dateOfBirthInput.prop('label')).toEqual('patient.approximateDateOfBirth') - expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() + expect(history.location.pathname).toEqual('/patients/123') }) }) diff --git a/src/locales/en-US/translation.json b/src/locales/en-US/translation.json index 065c29eda8..2ab2ae532b 100644 --- a/src/locales/en-US/translation.json +++ b/src/locales/en-US/translation.json @@ -27,8 +27,8 @@ "type": "Patient Type", "preferredLanguage": "Preferred Language", "basicInformation": "Basic Information", + "generalInformation": "General Information", "contactInformation": "Contact Information", - "approximateAge": "Approximate Age", "unknownDateOfBirth": "Unknown", "types": { "charity": "Charity", diff --git a/src/patients/view/GeneralInformation.tsx b/src/patients/view/GeneralInformation.tsx new file mode 100644 index 0000000000..82240f3e99 --- /dev/null +++ b/src/patients/view/GeneralInformation.tsx @@ -0,0 +1,145 @@ +import React from 'react' +import { Panel } from '@hospitalrun/components' +import { differenceInYears } from 'date-fns' +import Patient from 'model/Patient' +import { useTranslation } from 'react-i18next' +import TextFieldWithLabelFormGroup from '../../components/input/TextFieldWithLabelFormGroup' +import TextInputWithLabelFormGroup from '../../components/input/TextInputWithLabelFormGroup' +import SelectWithLabelFormGroup from '../../components/input/SelectWithLableFormGroup' +import DatePickerWithLabelFormGroup from '../../components/input/DatePickerWithLabelFormGroup' + +interface Props { + patient: Patient +} + +const getPatientAge = (dateOfBirth: string | undefined): string => { + if (!dateOfBirth) { + return '' + } + + const dob = new Date(dateOfBirth) + return differenceInYears(new Date(), dob).toString() +} + +const getPatientDateOfBirth = (dateOfBirth: string | undefined): Date | undefined => { + if (!dateOfBirth) { + return undefined + } + + return new Date(dateOfBirth) +} + +const GeneralInformation = (props: Props) => { + const { t } = useTranslation() + const { patient } = props + return ( +
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ ) +} + +export default GeneralInformation diff --git a/src/patients/view/ViewPatient.tsx b/src/patients/view/ViewPatient.tsx index 00218e9973..0a90e9e8f7 100644 --- a/src/patients/view/ViewPatient.tsx +++ b/src/patients/view/ViewPatient.tsx @@ -1,35 +1,15 @@ import React, { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' -import { useParams, withRouter } from 'react-router-dom' -import { Panel, Spinner } from '@hospitalrun/components' +import { useParams, withRouter, Route, useHistory, useLocation } from 'react-router-dom' +import { Panel, Spinner, TabsHeader, Tab } from '@hospitalrun/components' import { useTranslation } from 'react-i18next' -import { differenceInYears } from 'date-fns' import useTitle from '../../page-header/useTitle' import { fetchPatient } from '../patient-slice' import { RootState } from '../../store' -import TextFieldWithLabelFormGroup from '../../components/input/TextFieldWithLabelFormGroup' -import TextInputWithLabelFormGroup from '../../components/input/TextInputWithLabelFormGroup' -import SelectWithLabelFormGroup from '../../components/input/SelectWithLableFormGroup' -import DatePickerWithLabelFormGroup from '../../components/input/DatePickerWithLabelFormGroup' + import { getPatientFullName } from '../util/patient-name-util' import Patient from '../../model/Patient' - -const getPatientAge = (dateOfBirth: string | undefined): string => { - if (!dateOfBirth) { - return '' - } - - const dob = new Date(dateOfBirth) - return differenceInYears(new Date(), dob).toString() -} - -const getPatientDateOfBirth = (dateOfBirth: string | undefined): Date | undefined => { - if (!dateOfBirth) { - return undefined - } - - return new Date(dateOfBirth) -} +import GeneralInformation from './GeneralInformation' const getFriendlyId = (p: Patient): string => { if (p) { @@ -40,6 +20,8 @@ const getFriendlyId = (p: Patient): string => { } const ViewPatient = () => { + const history = useHistory() + const location = useLocation() const { t } = useTranslation() const dispatch = useDispatch() const { patient } = useSelector((state: RootState) => state.patient) @@ -58,109 +40,17 @@ const ViewPatient = () => { return (
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
-
-
- -
-
+ + history.push(`/patients/${patient.id}`)} + /> + + + + +
)