From a0be28b129ea67deb7d3934db08d68bd745bfdf1 Mon Sep 17 00:00:00 2001 From: Jack Meyer Date: Sun, 2 Feb 2020 22:55:24 -0600 Subject: [PATCH] feat(appointments): refactored appointment detail form to own component --- .../AppointmentDetailForm.test.tsx | 239 ++++++++++++++++++ .../appointments/new/NewAppointment.test.tsx | 171 ++----------- .../appointments/new/NewAppointment.tsx | 105 +------- 3 files changed, 273 insertions(+), 242 deletions(-) create mode 100644 src/__tests__/scheduling/appointments/AppointmentDetailForm.test.tsx diff --git a/src/__tests__/scheduling/appointments/AppointmentDetailForm.test.tsx b/src/__tests__/scheduling/appointments/AppointmentDetailForm.test.tsx new file mode 100644 index 0000000000..a3b7881831 --- /dev/null +++ b/src/__tests__/scheduling/appointments/AppointmentDetailForm.test.tsx @@ -0,0 +1,239 @@ +import '../../../__mocks__/matchMediaMock' +import React from 'react' +import { mount, ReactWrapper } from 'enzyme' +import AppointmentDetailForm from 'scheduling/appointments/AppointmentDetailForm' +import Appointment from 'model/Appointment' +import { roundToNearestMinutes, addMinutes } from 'date-fns' +import { Typeahead, Button } from '@hospitalrun/components' +import PatientRepository from 'clients/db/PatientRepository' +import Patient from 'model/Patient' +import { act } from '@testing-library/react' + +describe('AppointmentDetailForm', () => { + describe('layout', () => { + let wrapper: ReactWrapper + + beforeEach(() => { + wrapper = mount( + , + ) + }) + + it('should render a typeahead for patients', () => { + const patientTypeahead = wrapper.find(Typeahead) + + expect(patientTypeahead).toHaveLength(1) + expect(patientTypeahead.prop('placeholder')).toEqual('scheduling.appointment.patient') + }) + + it('should render as start date date time picker', () => { + const startDateTimePicker = wrapper.findWhere((w) => w.prop('name') === 'startDate') + + expect(startDateTimePicker).toHaveLength(1) + expect(startDateTimePicker.prop('label')).toEqual('scheduling.appointment.startDate') + expect(startDateTimePicker.prop('value')).toEqual( + roundToNearestMinutes(new Date(), { nearestTo: 15 }), + ) + }) + + it('should render an end date time picker', () => { + const endDateTimePicker = wrapper.findWhere((w) => w.prop('name') === 'endDate') + + expect(endDateTimePicker).toHaveLength(1) + expect(endDateTimePicker.prop('label')).toEqual('scheduling.appointment.endDate') + expect(endDateTimePicker.prop('value')).toEqual( + addMinutes(roundToNearestMinutes(new Date(), { nearestTo: 15 }), 60), + ) + }) + + it('should render a location text input box', () => { + const locationTextInputBox = wrapper.findWhere((w) => w.prop('name') === 'location') + + expect(locationTextInputBox).toHaveLength(1) + expect(locationTextInputBox.prop('label')).toEqual('scheduling.appointment.location') + }) + + it('should render a type select box', () => { + const typeSelect = wrapper.findWhere((w) => w.prop('name') === 'type') + + expect(typeSelect).toHaveLength(1) + expect(typeSelect.prop('label')).toEqual('scheduling.appointment.type') + expect(typeSelect.prop('options')[0].label).toEqual('scheduling.appointment.types.checkup') + expect(typeSelect.prop('options')[0].value).toEqual('checkup') + expect(typeSelect.prop('options')[1].label).toEqual('scheduling.appointment.types.emergency') + expect(typeSelect.prop('options')[1].value).toEqual('emergency') + expect(typeSelect.prop('options')[2].label).toEqual('scheduling.appointment.types.followUp') + expect(typeSelect.prop('options')[2].value).toEqual('follow up') + expect(typeSelect.prop('options')[3].label).toEqual('scheduling.appointment.types.routine') + expect(typeSelect.prop('options')[3].value).toEqual('routine') + expect(typeSelect.prop('options')[4].label).toEqual('scheduling.appointment.types.walkUp') + expect(typeSelect.prop('options')[4].value).toEqual('walk up') + }) + + it('should render a reason text field input', () => { + const reasonTextField = wrapper.findWhere((w) => w.prop('name') === 'reason') + + expect(reasonTextField).toHaveLength(1) + expect(reasonTextField.prop('label')).toEqual('scheduling.appointment.reason') + }) + }) + + describe('change handlers', () => { + let wrapper: ReactWrapper + let appointment = { + startDateTime: roundToNearestMinutes(new Date(), { nearestTo: 15 }).toISOString(), + endDateTime: addMinutes( + roundToNearestMinutes(new Date(), { nearestTo: 15 }), + 30, + ).toISOString(), + } as Appointment + const onAppointmentChangeSpy = jest.fn() + + beforeEach(() => { + wrapper = mount( + , + ) + }) + + it('should call the onAppointmentChange when patient input changes', () => { + const expectedPatientId = '123' + + act(() => { + const patientTypeahead = wrapper.find(Typeahead) + patientTypeahead.prop('onChange')([{ id: expectedPatientId }] as Patient[]) + }) + wrapper.update() + + expect(onAppointmentChangeSpy).toHaveBeenLastCalledWith({ + patientId: expectedPatientId, + startDateTime: appointment.startDateTime, + endDateTime: appointment.endDateTime, + }) + }) + + it('should call the onAppointmentChange when start date time changes', () => { + const expectedStartDateTime = roundToNearestMinutes(new Date(), { nearestTo: 15 }) + + act(() => { + const startDateTimePicker = wrapper.findWhere((w) => w.prop('name') === 'startDate') + startDateTimePicker.prop('onChange')(expectedStartDateTime) + }) + wrapper.update() + + expect(onAppointmentChangeSpy).toHaveBeenLastCalledWith({ + startDateTime: expectedStartDateTime.toISOString(), + endDateTime: appointment.endDateTime, + }) + }) + + it('should call the onAppointmentChange when end date time changes', () => { + const expectedStartDateTime = roundToNearestMinutes(new Date(), { nearestTo: 15 }) + const expectedEndDateTime = addMinutes(expectedStartDateTime, 30) + + act(() => { + const endDateTimePicker = wrapper.findWhere((w) => w.prop('name') === 'endDate') + endDateTimePicker.prop('onChange')(expectedEndDateTime) + }) + wrapper.update() + + expect(onAppointmentChangeSpy).toHaveBeenLastCalledWith({ + startDateTime: appointment.startDateTime, + endDateTime: expectedEndDateTime.toISOString(), + }) + }) + + it('should call the onAppointmentChange when location changes', () => { + const expectedLocation = 'location' + + act(() => { + const locationTextInputBox = wrapper.findWhere((w) => w.prop('name') === 'location') + locationTextInputBox.prop('onChange')({ target: { value: expectedLocation } }) + }) + wrapper.update() + + expect(onAppointmentChangeSpy).toHaveBeenLastCalledWith({ + startDateTime: appointment.startDateTime, + endDateTime: appointment.endDateTime, + location: expectedLocation, + }) + }) + + it('should call the onAppointmentChange when type changes', () => { + const expectedType = 'follow up' + + act(() => { + const typeSelect = wrapper.findWhere((w) => w.prop('name') === 'type') + typeSelect.prop('onChange')({ currentTarget: { value: expectedType } }) + }) + wrapper.update() + + expect(onAppointmentChangeSpy).toHaveBeenLastCalledWith({ + startDateTime: appointment.startDateTime, + endDateTime: appointment.endDateTime, + type: expectedType, + }) + }) + + it('should call the onAppointmentChange when reason changes', () => { + const expectedReason = 'reason' + + act(() => { + const reasonTextField = wrapper.findWhere((w) => w.prop('name') === 'reason') + reasonTextField.prop('onChange')({ target: { value: expectedReason } }) + }) + wrapper.update() + + expect(onAppointmentChangeSpy).toHaveBeenLastCalledWith({ + startDateTime: appointment.startDateTime, + endDateTime: appointment.endDateTime, + reason: expectedReason, + }) + }) + }) + + describe('typeahead search', () => { + let wrapper: ReactWrapper + beforeEach(() => { + wrapper = mount( + , + ) + }) + + it('should call the PatientRepository search when typeahead changes', () => { + const patientTypeahead = wrapper.find(Typeahead) + const patientRepositorySearch = jest.spyOn(PatientRepository, 'search') + const expectedSearchString = 'search' + + act(() => { + patientTypeahead.prop('onSearch')(expectedSearchString) + }) + + expect(patientRepositorySearch).toHaveBeenCalledWith(expectedSearchString) + }) + }) +}) diff --git a/src/__tests__/scheduling/appointments/new/NewAppointment.test.tsx b/src/__tests__/scheduling/appointments/new/NewAppointment.test.tsx index 3d50f419b0..bbcaadd833 100644 --- a/src/__tests__/scheduling/appointments/new/NewAppointment.test.tsx +++ b/src/__tests__/scheduling/appointments/new/NewAppointment.test.tsx @@ -15,6 +15,7 @@ import PatientRepository from 'clients/db/PatientRepository' import AppointmentRepository from 'clients/db/AppointmentsRepository' import { mocked } from 'ts-jest/utils' import Appointment from 'model/Appointment' +import AppointmentDetailForm from 'scheduling/appointments/AppointmentDetailForm' import * as titleUtil from '../../../../page-header/useTitle' import * as appointmentsSlice from '../../../../scheduling/appointments/appointments-slice' @@ -51,136 +52,30 @@ describe('New Appointment', () => { }) describe('layout', () => { - it('should render a typeahead for patients', () => { - const patientTypeahead = wrapper.find(Typeahead) - - expect(patientTypeahead).toHaveLength(1) - expect(patientTypeahead.prop('placeholder')).toEqual('scheduling.appointment.patient') - }) - - it('should render as start date date time picker', () => { - const startDateTimePicker = wrapper.findWhere((w) => w.prop('name') === 'startDate') - - expect(startDateTimePicker).toHaveLength(1) - expect(startDateTimePicker.prop('label')).toEqual('scheduling.appointment.startDate') - expect(startDateTimePicker.prop('value')).toEqual( - roundToNearestMinutes(new Date(), { nearestTo: 15 }), - ) - }) - - it('should render an end date time picker', () => { - const endDateTimePicker = wrapper.findWhere((w) => w.prop('name') === 'endDate') - - expect(endDateTimePicker).toHaveLength(1) - expect(endDateTimePicker.prop('label')).toEqual('scheduling.appointment.endDate') - expect(endDateTimePicker.prop('value')).toEqual( - addMinutes(roundToNearestMinutes(new Date(), { nearestTo: 15 }), 60), - ) - }) - - it('should render a location text input box', () => { - const locationTextInputBox = wrapper.findWhere((w) => w.prop('name') === 'location') - - expect(locationTextInputBox).toHaveLength(1) - expect(locationTextInputBox.prop('label')).toEqual('scheduling.appointment.location') - }) - - it('should render a type select box', () => { - const typeSelect = wrapper.findWhere((w) => w.prop('name') === 'type') - - expect(typeSelect).toHaveLength(1) - expect(typeSelect.prop('label')).toEqual('scheduling.appointment.type') - expect(typeSelect.prop('options')[0].label).toEqual('scheduling.appointment.types.checkup') - expect(typeSelect.prop('options')[0].value).toEqual('checkup') - expect(typeSelect.prop('options')[1].label).toEqual('scheduling.appointment.types.emergency') - expect(typeSelect.prop('options')[1].value).toEqual('emergency') - expect(typeSelect.prop('options')[2].label).toEqual('scheduling.appointment.types.followUp') - expect(typeSelect.prop('options')[2].value).toEqual('follow up') - expect(typeSelect.prop('options')[3].label).toEqual('scheduling.appointment.types.routine') - expect(typeSelect.prop('options')[3].value).toEqual('routine') - expect(typeSelect.prop('options')[4].label).toEqual('scheduling.appointment.types.walkUp') - expect(typeSelect.prop('options')[4].value).toEqual('walk up') - }) - - it('should render a reason text field input', () => { - const reasonTextField = wrapper.findWhere((w) => w.prop('name') === 'reason') - - expect(reasonTextField).toHaveLength(1) - expect(reasonTextField.prop('label')).toEqual('scheduling.appointment.reason') - }) - - it('should render a save button', () => { - const saveButton = wrapper.find(Button).at(0) - - expect(saveButton).toHaveLength(1) - expect(saveButton.text().trim()).toEqual('actions.save') - }) - - it('should render a cancel button', () => { - const cancelButton = wrapper.find(Button).at(1) - - expect(cancelButton).toHaveLength(1) - expect(cancelButton.text().trim()).toEqual('actions.cancel') - }) - }) - - describe('typeahead search', () => { - it('should call the PatientRepository search when typeahead changes', () => { - const patientTypeahead = wrapper.find(Typeahead) - const patientRepositorySearch = jest.spyOn(PatientRepository, 'search') - const expectedSearchString = 'search' - - act(() => { - patientTypeahead.prop('onSearch')(expectedSearchString) - }) - - expect(patientRepositorySearch).toHaveBeenCalledWith(expectedSearchString) + it('should render a Appointment Detail Component', () => { + expect(AppointmentDetailForm).toHaveLength(1) }) }) describe('on save click', () => { - it('should call createAppointment with the proper date', () => { + it('should call createAppointment with the proper data', () => { + const expectedAppointmentDetails = { + patientId: '123', + startDateTime: roundToNearestMinutes(new Date(), { nearestTo: 15 }).toISOString(), + endDateTime: addMinutes( + roundToNearestMinutes(new Date(), { nearestTo: 15 }), + 60, + ).toISOString(), + location: 'location', + reason: 'reason', + type: 'type', + } as Appointment const createAppointmentSpy = jest.spyOn(appointmentsSlice, 'createAppointment') - const expectedPatientId = '123' - const expectedStartDateTime = roundToNearestMinutes(new Date(), { nearestTo: 15 }) - const expectedEndDateTime = addMinutes(expectedStartDateTime, 30) - const expectedLocation = 'location' - const expectedType = 'follow up' - const expectedReason = 'reason' act(() => { - const patientTypeahead = wrapper.find(Typeahead) - patientTypeahead.prop('onChange')([{ id: expectedPatientId }] as Patient[]) - }) - wrapper.update() - - act(() => { - const startDateTimePicker = wrapper.findWhere((w) => w.prop('name') === 'startDate') - startDateTimePicker.prop('onChange')(expectedStartDateTime) - }) - wrapper.update() - - act(() => { - const endDateTimePicker = wrapper.findWhere((w) => w.prop('name') === 'endDate') - endDateTimePicker.prop('onChange')(expectedEndDateTime) - }) - wrapper.update() - - act(() => { - const locationTextInputBox = wrapper.findWhere((w) => w.prop('name') === 'location') - locationTextInputBox.prop('onChange')({ target: { value: expectedLocation } }) - }) - wrapper.update() - - act(() => { - const typeSelect = wrapper.findWhere((w) => w.prop('name') === 'type') - typeSelect.prop('onChange')({ currentTarget: { value: expectedType } }) - }) - wrapper.update() - - act(() => { - const reasonTextField = wrapper.findWhere((w) => w.prop('name') === 'reason') - reasonTextField.prop('onChange')({ target: { value: expectedReason } }) + const appointmentDetailForm = wrapper.find(AppointmentDetailForm) + const appointmentChangeHandler = appointmentDetailForm.prop('onAppointmentChange') + appointmentChangeHandler(expectedAppointmentDetails) }) wrapper.update() @@ -189,17 +84,11 @@ describe('New Appointment', () => { const onClick = saveButton.prop('onClick') as any onClick() }) + wrapper.update() expect(createAppointmentSpy).toHaveBeenCalledTimes(1) expect(createAppointmentSpy).toHaveBeenCalledWith( - { - patientId: expectedPatientId, - startDateTime: expectedStartDateTime.toISOString(), - endDateTime: expectedEndDateTime.toISOString(), - location: expectedLocation, - reason: expectedReason, - type: expectedType, - }, + expectedAppointmentDetails, expect.anything(), ) }) @@ -224,20 +113,12 @@ describe('New Appointment', () => { const expectedEndDateTime = subDays(expectedStartDateTime, 1) act(() => { - const patientTypeahead = wrapper.find(Typeahead) - patientTypeahead.prop('onChange')([{ id: expectedPatientId }] as Patient[]) - }) - wrapper.update() - - act(() => { - const startDateTimePicker = wrapper.findWhere((w) => w.prop('name') === 'startDate') - startDateTimePicker.prop('onChange')(expectedStartDateTime) - }) - wrapper.update() - - act(() => { - const endDateTimePicker = wrapper.findWhere((w) => w.prop('name') === 'endDate') - endDateTimePicker.prop('onChange')(expectedEndDateTime) + const onAppointmentChange = wrapper.find(AppointmentDetailForm).prop('onAppointmentChange') + onAppointmentChange({ + patientId: expectedPatientId, + startDateTime: expectedStartDateTime.toISOString(), + endDateTime: expectedEndDateTime.toISOString(), + } as Appointment) }) wrapper.update() diff --git a/src/scheduling/appointments/new/NewAppointment.tsx b/src/scheduling/appointments/new/NewAppointment.tsx index e434f68d3b..538ae33f98 100644 --- a/src/scheduling/appointments/new/NewAppointment.tsx +++ b/src/scheduling/appointments/new/NewAppointment.tsx @@ -1,20 +1,16 @@ import React, { useState } from 'react' import useTitle from 'page-header/useTitle' import { useTranslation } from 'react-i18next' -import DateTimePickerWithLabelFormGroup from 'components/input/DateTimePickerWithLabelFormGroup' -import { Typeahead, Label, Button, Alert } from '@hospitalrun/components' -import Patient from 'model/Patient' -import PatientRepository from 'clients/db/PatientRepository' -import TextInputWithLabelFormGroup from 'components/input/TextInputWithLabelFormGroup' -import TextFieldWithLabelFormGroup from 'components/input/TextFieldWithLabelFormGroup' -import SelectWithLabelFormGroup from 'components/input/SelectWithLableFormGroup' + import roundToNearestMinutes from 'date-fns/roundToNearestMinutes' import { useHistory } from 'react-router' import { useDispatch } from 'react-redux' import Appointment from 'model/Appointment' import addMinutes from 'date-fns/addMinutes' import { isBefore } from 'date-fns' +import { Button, Alert } from '@hospitalrun/components' import { createAppointment } from '../appointments-slice' +import AppointmentDetailForm from '../AppointmentDetailForm' const NewAppointment = () => { const { t } = useTranslation() @@ -65,96 +61,11 @@ const NewAppointment = () => { message={errorMessage} /> )} -
-
-
-
-
-
-
-
- { - setAppointment({ ...appointment, startDateTime: date.toISOString() }) - }} - /> -
-
- { - setAppointment({ ...appointment, endDateTime: date.toISOString() }) - }} - /> -
-
-
-
- { - setAppointment({ ...appointment, location: event?.target.value }) - }} - /> -
-
-
-
- ) => { - setAppointment({ ...appointment, type: event.currentTarget.value }) - }} - /> -
-
-
-
-
- { - setAppointment({ ...appointment, reason: event?.target.value }) - }} - /> -
-
-
+ +