Skip to content
This repository has been archived by the owner on Jan 9, 2023. It is now read-only.

Commit

Permalink
feat(diagnoses): adds ability to add a diagnosis
Browse files Browse the repository at this point in the history
  • Loading branch information
jackcmeyer committed Mar 2, 2020
1 parent bdf7aa1 commit e1ce6c9
Show file tree
Hide file tree
Showing 10 changed files with 424 additions and 5 deletions.
3 changes: 1 addition & 2 deletions src/__tests__/patients/allergies/Allergies.test.tsx
Expand Up @@ -9,7 +9,6 @@ import thunk from 'redux-thunk'
import { Router } from 'react-router'
import { Provider } from 'react-redux'
import Patient from 'model/Patient'
import User from 'model/User'
import { Button, Modal, List, ListItem, Alert } from '@hospitalrun/components'
import { act } from '@testing-library/react'
import { mocked } from 'ts-jest/utils'
Expand All @@ -33,7 +32,7 @@ let user: any
let store: any

const setup = (patient = expectedPatient, permissions = [Permissions.AddAllergy]) => {
user = { permissions } as User
user = { permissions }
store = mockStore({ patient, user })
const wrapper = mount(
<Router history={history}>
Expand Down
95 changes: 95 additions & 0 deletions src/__tests__/patients/diagnoses/AddDiagnosisModal.test.tsx
@@ -0,0 +1,95 @@
import '../../../__mocks__/matchMediaMock'
import React from 'react'
import { shallow, mount } from 'enzyme'
import { Modal, Alert } from '@hospitalrun/components'
import { act } from '@testing-library/react'
import AddDiagnosisModal from 'patients/diagnoses/AddDiagnosisModal'
import Diagnosis from 'model/Diagnosis'

describe('Add Diagnosis Modal', () => {
it('should render a modal with the correct labels', () => {
const wrapper = shallow(
<AddDiagnosisModal show onCloseButtonClick={jest.fn()} onSave={jest.fn()} />,
)

const modal = wrapper.find(Modal)
expect(modal).toHaveLength(1)
expect(modal.prop('title')).toEqual('patient.diagnoses.new')
expect(modal.prop('closeButton')?.children).toEqual('actions.cancel')
expect(modal.prop('closeButton')?.color).toEqual('danger')
expect(modal.prop('successButton')?.children).toEqual('patient.diagnoses.new')
expect(modal.prop('successButton')?.color).toEqual('success')
expect(modal.prop('successButton')?.icon).toEqual('add')
})

describe('cancel', () => {
it('should call the onCloseButtonClick function when the close button is clicked', () => {
const onCloseButtonClickSpy = jest.fn()
const wrapper = shallow(
<AddDiagnosisModal show onCloseButtonClick={onCloseButtonClickSpy} onSave={jest.fn()} />,
)

act(() => {
const modal = wrapper.find(Modal)
const { onClick } = modal.prop('closeButton') as any
onClick()
})

expect(onCloseButtonClickSpy).toHaveBeenCalledTimes(1)
})
})

describe('save', () => {
it('should call the onSave function with the correct data when the save button is clicked', () => {
const expectedName = 'expected name'
const expectedDate = new Date()
const onSaveSpy = jest.fn()
const wrapper = mount(
<AddDiagnosisModal show onCloseButtonClick={jest.fn()} onSave={onSaveSpy} />,
)

act(() => {
const input = wrapper.findWhere((c: any) => c.prop('name') === 'name')
const onChange = input.prop('onChange')
onChange({ target: { value: expectedName } })
})
wrapper.update()

act(() => {
const input = wrapper.findWhere((c: any) => c.prop('name') === 'diagnosisDate')
const onChange = input.prop('onChange')
onChange(expectedDate)
})
wrapper.update()

act(() => {
const modal = wrapper.find(Modal)
const onSave = (modal.prop('successButton') as any).onClick
onSave({} as React.MouseEvent<HTMLButtonElement>)
})

expect(onSaveSpy).toHaveBeenCalledTimes(1)
expect(onSaveSpy).toHaveBeenCalledWith({
name: expectedName,
diagnosisDate: expectedDate.toISOString(),
} as Diagnosis)
})

it('should display an error message if the name field is not filled out', () => {
const wrapper = mount(
<AddDiagnosisModal show onCloseButtonClick={jest.fn()} onSave={jest.fn()} />,
)

act(() => {
const modal = wrapper.find(Modal)
const onSave = (modal.prop('successButton') as any).onClick
onSave({} as React.MouseEvent<HTMLButtonElement>)
})
wrapper.update()

expect(wrapper.find(Alert)).toHaveLength(1)
expect(wrapper.find(Alert).prop('title')).toEqual('states.error')
expect(wrapper.find(Alert).prop('message')).toContain('patient.diagnoses.error.nameRequired')
})
})
})
129 changes: 129 additions & 0 deletions src/__tests__/patients/diagnoses/Diagnoses.test.tsx
@@ -0,0 +1,129 @@
import '../../../__mocks__/matchMediaMock'
import React from 'react'
import { mount } from 'enzyme'
import { createMemoryHistory } from 'history'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import Patient from 'model/Patient'
import Diagnosis from 'model/Diagnosis'
import Permissions from 'model/Permissions'
import { Router } from 'react-router'
import { Provider } from 'react-redux'
import Diagnoses from 'patients/diagnoses/Diagnoses'
import { Button, Modal, List, ListItem, Alert } from '@hospitalrun/components'
import { act } from 'react-dom/test-utils'
import { mocked } from 'ts-jest/utils'
import PatientRepository from 'clients/db/PatientRepository'
import AddDiagnosisModal from 'patients/diagnoses/AddDiagnosisModal'
import * as patientSlice from '../../../patients/patient-slice'

const expectedPatient = {
id: '123',
diagnoses: [
{ id: '123', name: 'diagnosis1', diagnosisDate: new Date().toISOString() } as Diagnosis,
],
} as Patient

const mockStore = configureMockStore([thunk])
const history = createMemoryHistory()

let user: any
let store: any

const setup = (patient = expectedPatient, permissions = [Permissions.AddDiagnosis]) => {
user = { permissions }
store = mockStore({ patient, user })
const wrapper = mount(
<Router history={history}>
<Provider store={store}>
<Diagnoses patient={patient} />
</Provider>
</Router>,
)

return wrapper
}
describe('Diagnoses', () => {
describe('add diagnoses button', () => {
beforeEach(() => {
jest.resetAllMocks()
jest.spyOn(PatientRepository, 'saveOrUpdate')
})

it('should render a add diagnoses button', () => {
const wrapper = setup()

const addDiagnosisButton = wrapper.find(Button)
expect(addDiagnosisButton).toHaveLength(1)
expect(addDiagnosisButton.text().trim()).toEqual('patient.diagnoses.new')
})

it('should not render a diagnoses button if the user does not have permissions', () => {
const wrapper = setup(expectedPatient, [])

const addDiagnosisButton = wrapper.find(Button)
expect(addDiagnosisButton).toHaveLength(0)
})

it('should open the Add Diagnosis Modal', () => {
const wrapper = setup()

act(() => {
wrapper.find(Button).prop('onClick')()
})
wrapper.update()

expect(wrapper.find(Modal).prop('show')).toBeTruthy()
})

it('should update the patient with the new diagnosis when the save button is clicked', async () => {
const expectedDiagnosis = {
name: 'name',
diagnosisDate: new Date().toISOString(),
} as Diagnosis
const expectedUpdatedPatient = {
...expectedPatient,
diagnoses: [...(expectedPatient.diagnoses as any), expectedDiagnosis],
} as Patient

const mockedPatientRepository = mocked(PatientRepository, true)
mockedPatientRepository.saveOrUpdate.mockResolvedValue(expectedUpdatedPatient)

const wrapper = setup()

await act(async () => {
const modal = wrapper.find(AddDiagnosisModal)
await modal.prop('onSave')(expectedDiagnosis)
})

expect(mockedPatientRepository.saveOrUpdate).toHaveBeenCalledWith(expectedUpdatedPatient)
expect(store.getActions()).toContainEqual(patientSlice.updatePatientStart())
expect(store.getActions()).toContainEqual(
patientSlice.updatePatientSuccess(expectedUpdatedPatient),
)
})
})

describe('diagnoses list', () => {
it('should list the patients diagnoses', () => {
const diagnoses = expectedPatient.diagnoses as Diagnosis[]
const wrapper = setup()

const list = wrapper.find(List)
const listItems = wrapper.find(ListItem)

expect(list).toHaveLength(1)
expect(listItems).toHaveLength(diagnoses.length)
})

it('should render a warning message if the patient does not have any diagnoses', () => {
const wrapper = setup({ ...expectedPatient, diagnoses: [] })

const alert = wrapper.find(Alert)

expect(alert).toHaveLength(1)
expect(alert.prop('title')).toEqual('patient.diagnoses.warning.noDiagnoses')
expect(alert.prop('message')).toEqual('patient.diagnoses.addDiagnosisAbove')
})
})
})
14 changes: 13 additions & 1 deletion src/locales/en-US/translation.json
Expand Up @@ -65,7 +65,19 @@
"addAllergyAbove": "Add an allergy using the button above."
},
"diagnoses": {
"label": "Diagnoses"
"label": "Diagnoses",
"new": "Add Diagnoses",
"diagnosisName": "Diagnosis Name",
"diagnosisDate": "Diagnosis Date",
"warning": {
"noDiagnoses": "No Diagnoses"
},
"error": {
"nameRequired": "Diagnosis Name is required.",
"dateRequired": "Diagnosis Date is required."
},
"addDiagnosisAbove": "Add a diagnosis using the button above."

}
},
"sex": {
Expand Down
5 changes: 5 additions & 0 deletions src/model/Diagnosis.ts
@@ -0,0 +1,5 @@
export default interface Diagnosis {
id: string
name: string
diagnosisDate: string
}
2 changes: 2 additions & 0 deletions src/model/Patient.ts
Expand Up @@ -3,6 +3,7 @@ import Name from './Name'
import ContactInformation from './ContactInformation'
import RelatedPerson from './RelatedPerson'
import Allergy from './Allergy'
import Diagnosis from './Diagnosis'

export default interface Patient extends AbstractDBModel, Name, ContactInformation {
sex: string
Expand All @@ -14,4 +15,5 @@ export default interface Patient extends AbstractDBModel, Name, ContactInformati
friendlyId: string
relatedPersons?: RelatedPerson[]
allergies?: Allergy[]
diagnoses?: Diagnosis[]
}
1 change: 1 addition & 0 deletions src/model/Permissions.ts
Expand Up @@ -5,6 +5,7 @@ enum Permissions {
WriteAppointments = 'write:appointments',
DeleteAppointment = 'delete:appointment',
AddAllergy = 'write:allergy',
AddDiagnosis = 'write:diagnosis',
}

export default Permissions
106 changes: 106 additions & 0 deletions src/patients/diagnoses/AddDiagnosisModal.tsx
@@ -0,0 +1,106 @@
import React, { useState, useEffect } from 'react'
import { Modal, Alert } from '@hospitalrun/components'
import { useTranslation } from 'react-i18next'
import Diagnosis from 'model/Diagnosis'
import TextInputWithLabelFormGroup from 'components/input/TextInputWithLabelFormGroup'
import DatePickerWithLabelFormGroup from 'components/input/DatePickerWithLabelFormGroup'

interface Props {
show: boolean
onCloseButtonClick: () => void
onSave: (diagnosis: Diagnosis) => void
}

const AddDiagnosisModal = (props: Props) => {
const { show, onCloseButtonClick, onSave } = props
const [diagnosis, setDiagnosis] = useState({ name: '', diagnosisDate: new Date().toISOString() })
const [errorMessage, setErrorMessage] = useState('')

const { t } = useTranslation()

useEffect(() => {
setErrorMessage('')
setDiagnosis({ name: '', diagnosisDate: new Date().toISOString() })
}, [show])

const onSaveButtonClick = () => {
let newErrorMessage = ''
if (!diagnosis.name) {
newErrorMessage += t('patient.diagnoses.error.nameRequired')
}
setErrorMessage(newErrorMessage)

if (!newErrorMessage) {
onSave(diagnosis as Diagnosis)
}
}

const onNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const name = event.target.value
setDiagnosis((prevDiagnosis) => ({ ...prevDiagnosis, name }))
}

const onDiagnosisDateChange = (diagnosisDate: Date) => {
if (diagnosisDate) {
setDiagnosis((prevDiagnosis) => ({
...prevDiagnosis,
diagnosisDate: diagnosisDate.toISOString(),
}))
}
}

const body = (
<>
<form>
{errorMessage && <Alert color="danger" title={t('states.error')} message={errorMessage} />}
<div className="row">
<div className="col-md-12">
<div className="form-group">
<TextInputWithLabelFormGroup
name="name"
label={t('patient.diagnoses.diagnosisName')}
isEditable
placeholder={t('patient.diagnoses.diagnosisName')}
value={diagnosis.name}
onChange={onNameChange}
/>
</div>
</div>
</div>
<div className="row">
<div className="col-md-12">
<DatePickerWithLabelFormGroup
name="diagnosisDate"
label={t('patient.diagnoses.diagnosisDate')}
value={new Date(diagnosis.diagnosisDate)}
isEditable
onChange={onDiagnosisDateChange}
/>
</div>
</div>
</form>
</>
)
return (
<Modal
show={show}
toggle={onCloseButtonClick}
title={t('patient.diagnoses.new')}
body={body}
closeButton={{
children: t('actions.cancel'),
color: 'danger',
onClick: onCloseButtonClick,
}}
successButton={{
children: t('patient.diagnoses.new'),
color: 'success',
icon: 'add',
iconLocation: 'left',
onClick: onSaveButtonClick,
}}
/>
)
}

export default AddDiagnosisModal

0 comments on commit e1ce6c9

Please sign in to comment.