diff --git a/package.json b/package.json index 65701f7cf..31bc64daf 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ }, "devDependencies": { "@babel/core": "^7.11.6", - "@carbon/react": "~1.37.0", + "@carbon/react": "^1.71.0", "@openmrs/esm-framework": "next", "@openmrs/esm-patient-common-lib": "next", "@playwright/test": "1.48.2", diff --git a/packages/esm-active-visits-app/package.json b/packages/esm-active-visits-app/package.json index 4a9e4bb51..2bbad0817 100644 --- a/packages/esm-active-visits-app/package.json +++ b/packages/esm-active-visits-app/package.json @@ -37,7 +37,7 @@ "url": "https://github.com/openmrs/openmrs-esm-patient-management/issues" }, "dependencies": { - "@carbon/react": "~1.37.0", + "@carbon/react": "^1.71.0", "lodash-es": "^4.17.15" }, "peerDependencies": { diff --git a/packages/esm-appointments-app/package.json b/packages/esm-appointments-app/package.json index 2c67163be..c91ba8a0e 100644 --- a/packages/esm-appointments-app/package.json +++ b/packages/esm-appointments-app/package.json @@ -37,7 +37,7 @@ "url": "https://github.com/openmrs/openmrs-esm-patient-management/issues" }, "dependencies": { - "@carbon/react": "~1.37.0", + "@carbon/react": "^1.71.0", "formik": "^2.2.9", "lodash-es": "^4.17.15", "yup": "^0.32.11" diff --git a/packages/esm-appointments-app/src/appointments.test.tsx b/packages/esm-appointments-app/src/appointments.test.tsx index 972843323..9af425edf 100644 --- a/packages/esm-appointments-app/src/appointments.test.tsx +++ b/packages/esm-appointments-app/src/appointments.test.tsx @@ -9,7 +9,9 @@ describe('Appointments', () => { await screen.findByText(/^appointments$/i); expect(screen.getByRole('button', { name: /appointments calendar/i })).toBeInTheDocument(); expect(screen.getByPlaceholderText(/dd-mmm-yyyy/i)).toBeInTheDocument(); - expect(screen.getByRole('combobox', { name: /view/i })).toBeInTheDocument(); + screen.getByRole('combobox', { + name: /select service type/i, + }); expect(screen.getByText(/appointment metrics/i)).toBeInTheDocument(); }); }); diff --git a/packages/esm-appointments-app/src/form/appointments-form.component.tsx b/packages/esm-appointments-app/src/form/appointments-form.component.tsx index 97f20f5e7..bc92d335f 100644 --- a/packages/esm-appointments-app/src/form/appointments-form.component.tsx +++ b/packages/esm-appointments-app/src/form/appointments-form.component.tsx @@ -354,7 +354,9 @@ const AppointmentsForm: React.FC appointmentRequest: appointmentPayload, recurringPattern: constructRecurringPattern(data), }; + const abortController = new AbortController(); + (isRecurringAppointment ? saveRecurringAppointments(recurringAppointmentPayload, abortController) : saveAppointment(appointmentPayload, abortController) diff --git a/packages/esm-appointments-app/src/form/appointments-form.test.tsx b/packages/esm-appointments-app/src/form/appointments-form.test.tsx index 033bb185e..59e9963d3 100644 --- a/packages/esm-appointments-app/src/form/appointments-form.test.tsx +++ b/packages/esm-appointments-app/src/form/appointments-form.test.tsx @@ -20,6 +20,8 @@ const defaultProps = { closeWorkspace: jest.fn(), patientUuid: mockPatient.id, promptBeforeClosing: jest.fn(), + closeWorkspaceWithSavedChanges: jest.fn(), + setTitle: jest.fn(), }; const mockCreateAppointment = jest.mocked(saveAppointment); @@ -206,6 +208,7 @@ describe('AppointmentForm', () => { await user.selectOptions(timeFormat, 'AM'); await user.selectOptions(serviceSelect, ['Outpatient']); await user.selectOptions(appointmentTypeSelect, ['Scheduled']); + // TODO: Fix the `Error: Not implemented: HTMLFormElement.prototype.requestSubmit` error shown after the form is submitted await user.click(saveButton); }); }); diff --git a/packages/esm-bed-management-app/package.json b/packages/esm-bed-management-app/package.json index fe6cb162f..2772a26dc 100644 --- a/packages/esm-bed-management-app/package.json +++ b/packages/esm-bed-management-app/package.json @@ -37,7 +37,7 @@ "url": "https://github.com/openmrs/openmrs-esm-patient-management/issues" }, "dependencies": { - "@carbon/react": "~1.37.0", + "@carbon/react": "^1.71.0", "lodash-es": "^4.17.15" }, "peerDependencies": { diff --git a/packages/esm-patient-list-management-app/package.json b/packages/esm-patient-list-management-app/package.json index 02cfa77bc..7fb7edcdd 100644 --- a/packages/esm-patient-list-management-app/package.json +++ b/packages/esm-patient-list-management-app/package.json @@ -37,7 +37,7 @@ "url": "https://github.com/openmrs/openmrs-esm-patient-management/issues" }, "dependencies": { - "@carbon/react": "~1.37.0", + "@carbon/react": "^1.71.0", "dexie": "^3.0.3", "fuzzy": "^0.1.3", "lodash-es": "^4.17.15" diff --git a/packages/esm-patient-registration-app/package.json b/packages/esm-patient-registration-app/package.json index 5c0cbd36b..204e1ec42 100644 --- a/packages/esm-patient-registration-app/package.json +++ b/packages/esm-patient-registration-app/package.json @@ -37,7 +37,7 @@ "url": "https://github.com/openmrs/openmrs-esm-patient-management/issues" }, "dependencies": { - "@carbon/react": "~1.37.0", + "@carbon/react": "^1.71.0", "formik": "^2.1.5", "lodash-es": "^4.17.15", "uuid": "^8.3.2", diff --git a/packages/esm-patient-registration-app/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx b/packages/esm-patient-registration-app/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx index 0a27740f4..95b04468c 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx @@ -35,7 +35,7 @@ function DeathDateField() { selectedDate ? dayjs(selectedDate).hour(0).minute(0).second(0).millisecond(0).toDate() : undefined, ); }, - [deathDate], + [setFieldValue], ); return ( diff --git a/packages/esm-patient-registration-app/src/patient-registration/field/dob/dob.component.tsx b/packages/esm-patient-registration-app/src/patient-registration/field/dob/dob.component.tsx index 6599103ec..efbaa6b9b 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/field/dob/dob.component.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/field/dob/dob.component.tsx @@ -41,7 +41,7 @@ export const DobField: React.FC = () => { setFieldValue('monthsEstimated', ''); setFieldTouched('birthdateEstimated', true, false); }, - [setFieldValue], + [setFieldTouched, setFieldValue], ); const onDateChange = useCallback( diff --git a/packages/esm-patient-registration-app/src/patient-registration/field/gender/gender-field.component.tsx b/packages/esm-patient-registration-app/src/patient-registration/field/gender/gender-field.component.tsx index 75fa4663a..b2964df44 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/field/gender/gender-field.component.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/field/gender/gender-field.component.tsx @@ -30,8 +30,12 @@ export const GenderField: React.FC = () => {

{t('sexFieldLabelText', 'Sex')}

-

{t('genderLabelText', 'Sex')}

- + {fieldConfigs.map((option) => ( { setFieldTouched('photo', true, false); } }, - [setCapturePhotoProps], + [setCapturePhotoProps, setFieldTouched], ); const toggleNameKnown = (e) => { diff --git a/packages/esm-patient-registration-app/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx b/packages/esm-patient-registration-app/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx index e5b06a050..d72e935cd 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx @@ -3,10 +3,10 @@ import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import { Field } from 'formik'; import { Layer, Select, SelectItem } from '@carbon/react'; +import { reportError } from '@openmrs/esm-framework'; import { type PersonAttributeTypeResponse } from '../../patient-registration.types'; import { useConceptAnswers } from '../field.resource'; import styles from './../field.scss'; -import { reportError } from '@openmrs/esm-framework'; export interface CodedPersonAttributeFieldProps { id: string; @@ -44,7 +44,7 @@ export function CodedPersonAttributeField({ ); setError(true); } - }, [answerConceptSetUuid, customConceptAnswers]); + }, [answerConceptSetUuid, customConceptAnswers, id, t]); useEffect(() => { if (!isLoadingConceptAnswers && !customConceptAnswers.length) { @@ -72,7 +72,7 @@ export function CodedPersonAttributeField({ setError(true); } } - }, [isLoadingConceptAnswers, conceptAnswers, customConceptAnswers]); + }, [isLoadingConceptAnswers, conceptAnswers, customConceptAnswers, t, id, answerConceptSetUuid]); const answers = useMemo(() => { if (customConceptAnswers.length) { diff --git a/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts b/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts index ff50d4b20..b89aed9c6 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts +++ b/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts @@ -1,14 +1,15 @@ +import { type Dispatch, useEffect, useMemo, useState } from 'react'; import { type FetchResponse, + type OpenmrsResource, getSynchronizationItems, openmrsFetch, - type OpenmrsResource, restBaseUrl, useConfig, usePatient, } from '@openmrs/esm-framework'; import camelCase from 'lodash-es/camelCase'; -import { type Dispatch, useEffect, useMemo, useState } from 'react'; +import dayjs from 'dayjs'; import useSWR from 'swr'; import { v4 } from 'uuid'; import { type RegistrationConfig } from '../config-schema'; @@ -29,7 +30,15 @@ import { latestFirstEncounter, } from './patient-registration-utils'; import { useInitialPatientRelationships } from './section/patient-relationships/relationships.resource'; -import dayjs from 'dayjs'; + +interface DeathInfoResults { + uuid: string; + display: string; + causeOfDeath: OpenmrsResource | null; + dead: boolean; + deathDate: string; + causeOfDeathNonCoded: string | null; +} export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch] { const { freeTextFieldConceptUuid } = useConfig(); @@ -99,7 +108,7 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch setInitialFormValues(registration._patientRegistrationData.formValues); } })(); - }, [isLoadingPatientToEdit, patientToEdit, patientUuid]); + }, [initialFormValues, isLoadingPatientToEdit, patientToEdit, patientUuid]); // Set initial patient death info useEffect(() => { @@ -118,7 +127,7 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch nonCodedCauseOfDeath: deathInfo.causeOfDeathNonCoded, })); } - }, [isLoadingDeathInfo, deathInfo, setInitialFormValues]); + }, [isLoadingDeathInfo, deathInfo, setInitialFormValues, freeTextFieldConceptUuid]); // Set initial patient relationships useEffect(() => { @@ -187,7 +196,7 @@ export function useInitialAddressFieldValues(patientUuid: string, fallback = {}) setInitialAddressFieldValues(registration?._patientRegistrationData.initialAddressFieldValues ?? fallback); } })(); - }, [isLoading, patient, patientUuid]); + }, [fallback, initialAddressFieldValues, isLoading, patient, patientUuid]); return [initialAddressFieldValues, setInitialAddressFieldValues]; } @@ -208,7 +217,7 @@ export function usePatientUuidMap( setPatientUuidMap(registration?._patientRegistrationData.initialAddressFieldValues ?? fallback), ); } - }, [isLoadingPatientToEdit, patientToEdit, patientUuid]); + }, [fallback, isLoadingPatientToEdit, patientToEdit, patientUuid, patientUuidMap]); useEffect(() => { if (attributes) { @@ -263,7 +272,7 @@ export function useInitialPatientIdentifiers(patientUuid: string): { data: identifiers, isLoading, }; - }, [data, error]); + }, [data?.data?.results, isLoading]); return result; } @@ -299,19 +308,10 @@ function useInitialPersonAttributes(personUuid: string) { data: data?.data?.results, isLoading, }; - }, [data, error]); + }, [data?.data?.results, isLoading]); return result; } -interface DeathInfoResults { - uuid: string; - display: string; - causeOfDeath: OpenmrsResource | null; - dead: boolean; - deathDate: string; - causeOfDeathNonCoded: string | null; -} - function useInitialPersonDeathInfo(personUuid: string) { const { data, error, isLoading } = useSWR, Error>( !!personUuid @@ -325,7 +325,7 @@ function useInitialPersonDeathInfo(personUuid: string) { data: data?.data, isLoading, }; - }, [data, error]); + }, [data?.data, isLoading]); return result; } diff --git a/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx b/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx index f2c93fd80..caa603f0e 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx @@ -11,21 +11,24 @@ import { useConfig, usePatient, } from '@openmrs/esm-framework'; +import type { AddressTemplate, Encounter, FormValues } from './patient-registration.types'; import { mockedAddressTemplate } from '__mocks__'; import { mockPatient } from 'tools'; import { saveEncounter, savePatient } from './patient-registration.resource'; import { esmPatientRegistrationSchema, type RegistrationConfig } from '../config-schema'; -import type { AddressTemplate, Encounter } from './patient-registration.types'; import { ResourcesContext } from '../offline.resources'; import { FormManager } from './form-manager'; import { PatientRegistration } from './patient-registration.component'; +import { useInitialFormValues } from './patient-registration-hooks'; +const mockOpenmrsDatePicker = jest.mocked(OpenmrsDatePicker); const mockSaveEncounter = jest.mocked(saveEncounter); const mockSavePatient = savePatient as jest.Mock; const mockShowSnackbar = jest.mocked(showSnackbar); const mockUseConfig = jest.mocked(useConfig); const mockUsePatient = jest.mocked(usePatient); -const mockOpenmrsDatePicker = jest.mocked(OpenmrsDatePicker); +const mockUseParams = useParams as jest.Mock; +const mockUseInitialFormValues = jest.mocked(useInitialFormValues); jest.mock('./field/field.resource', () => ({ useConcept: jest.fn().mockImplementation((uuid: string) => { @@ -85,7 +88,7 @@ jest.mock('./field/field.resource', () => ({ })); jest.mock('react-router-dom', () => ({ - ...(jest.requireActual('react-router-dom') as any), + ...jest.requireActual('react-router-dom'), useLocation: () => ({ pathname: 'openmrs/spa/patient-registration', }), @@ -99,6 +102,13 @@ jest.mock('./patient-registration.resource', () => ({ savePatient: jest.fn(), })); +jest.mock('./patient-registration-hooks', () => ({ + ...jest.requireActual('./patient-registration-hooks'), + useInitialFormValues: jest.fn().mockReturnValue([{}, jest.fn()]), + useInitialAddressFieldValues: jest.fn().mockReturnValue([{}, jest.fn()]), + usePatientUuidMap: jest.fn().mockReturnValue([{}, jest.fn()]), +})); + mockOpenmrsDatePicker.mockImplementation(({ id, labelText, value, onChange }) => { return ( <> @@ -126,7 +136,7 @@ const mockResourcesContextValue = { identifierTypes: [], }; -let mockOpenmrsConfig: RegistrationConfig = { +const mockOpenmrsConfig: RegistrationConfig = { sections: ['demographics', 'contact'], sectionDefinitions: [ { id: 'demographics', name: 'Demographics', fields: ['name', 'gender', 'dob'] }, @@ -159,6 +169,10 @@ let mockOpenmrsConfig: RegistrationConfig = { value: 'male', label: 'Male', }, + { + value: 'female', + label: 'Female', + }, ], address: { useAddressHierarchy: { @@ -180,11 +194,10 @@ let mockOpenmrsConfig: RegistrationConfig = { encounterProviderRoleUuid: 'asdf', registrationFormUuid: null, }, + freeTextFieldConceptUuid: '', }; - -const path = `/patient/:patientUuid/edit`; - const configWithObs = JSON.parse(JSON.stringify(mockOpenmrsConfig)); + configWithObs.fieldDefinitions = [ { id: 'weight', @@ -217,7 +230,6 @@ configWithObs.fieldDefinitions = [ customConceptAnswers: [], }, ]; - configWithObs.sectionDefinitions?.push({ id: 'custom', name: 'Custom', @@ -238,16 +250,14 @@ const fillRequiredFields = async () => { await user.type(familyNameInput, 'Gaihre'); await user.clear(dateOfBirthInput); await user.type(dateOfBirthInput, '02/08/1993'); - user.click(genderInput); + await user.click(genderInput); }; -function Wrapper({ children }) { - return ( - - {children} - - ); -} +const Wrapper = ({ children }) => ( + + {children} + +); describe('Registering a new patient', () => { beforeEach(() => { @@ -258,15 +268,30 @@ describe('Registering a new patient', () => { mockSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true }); }); - it('renders without crashing', () => { + it('should render all the required fields and sections', async () => { render(, { wrapper: Wrapper }); - }); - it('has the expected sections', async () => { - render(, { wrapper: Wrapper }); + await screen.findByRole('heading', { name: /create new patient/i }); + + const demographicSection = screen.getByRole('region', { name: /demographics section/i }); + const contactSection = screen.getByRole('region', { name: /contact info section/i }); + + expect(demographicSection).toBeInTheDocument(); + expect(contactSection).toBeInTheDocument(); + expect(screen.getByText(/jump to/i)).toBeInTheDocument(); + expect(within(demographicSection).getByLabelText(/first name/i)).toBeInTheDocument(); + expect(within(demographicSection).getByLabelText(/middle name \(optional\)/i)).toBeInTheDocument(); + expect(within(demographicSection).getByLabelText(/family name/i)).toBeInTheDocument(); + expect(within(demographicSection).getByLabelText(/date of birth/i)).toBeInTheDocument(); + expect(within(demographicSection).getByRole('radio', { name: /^male$/i })).toBeInTheDocument(); + expect(within(demographicSection).getByRole('radio', { name: /^female$/i })).toBeInTheDocument(); + expect(within(demographicSection).getByText(/date of birth known\?/i)).toBeInTheDocument(); + expect(within(demographicSection).getByLabelText(/date of birth/i)).toBeInTheDocument(); - expect(screen.getByRole('region', { name: /demographics section/i })).toBeInTheDocument(); - expect(screen.getByRole('region', { name: /contact info section/i })).toBeInTheDocument(); + expect(within(contactSection).getByRole('heading', { name: /address/i })).toBeInTheDocument(); + + expect(screen.getByRole('button', { name: /register patient/i })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument(); }); // TODO O3-3482: Fix this test case when OpenmrsDatePicker gets fixed on core @@ -300,14 +325,12 @@ describe('Registering a new patient', () => { it('should not save the patient if validation fails', async () => { const user = userEvent.setup(); - const mockSavePatientForm = jest.fn(); - render(, { wrapper: Wrapper }); - const givenNameInput = (await screen.findByLabelText('First Name')) as HTMLInputElement; + render(, { wrapper: Wrapper }); - await user.type(givenNameInput, '5'); - await user.click(screen.getByText(/Register Patient/i)); + await screen.findByRole('heading', { name: /create new patient/i }); + await user.click(screen.getByRole('button', { name: /register patient/i })); expect(mockSavePatientForm).not.toHaveBeenCalled(); }); @@ -386,76 +409,149 @@ describe('Registering a new patient', () => { describe('Updating an existing patient record', () => { beforeEach(() => { - mockUseConfig.mockReturnValue(mockOpenmrsConfig); + mockUseConfig.mockReturnValue({ + ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema), + ...mockOpenmrsConfig, + }); + mockUsePatient.mockImplementation(() => { + return { + error: null, + isLoading: false, + patient: mockPatient, + patientUuid: mockPatient.id, + }; + }); mockSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true }); + mockUseParams.mockReturnValue({ patientUuid: mockPatient.id }); }); it('edits patient demographics', async () => { const user = userEvent.setup(); - mockSavePatient.mockResolvedValue({} as FetchResponse); + const mockSavePatientForm = jest.fn(); - const mockUseParams = useParams as jest.Mock; + mockUseInitialFormValues.mockReturnValue([ + { + additionalFamilyName: '', + additionalGivenName: '', + additionalMiddleName: '', + addNameInLocalLanguage: false, + address: {}, + birthdate: mockPatient.birthDate, + birthdateEstimated: false, + deathCause: '', + deathDate: undefined, + deathTime: undefined, + deathTimeFormat: 'AM', + familyName: mockPatient.name[0].family, + gender: mockPatient.gender, + givenName: mockPatient.name[0].given[0], + identifiers: { + openMrsId: { + autoGeneration: false, + identifierName: 'OpenMRS ID', + identifierTypeUuid: '05a29f94-c0ed-11e2-94be-8c13b969e334', + identifierUuid: '1f0ad7a1-430f-4397-b571-59ea654a52db', + identifierValue: '100GEJ', + initialValue: '100GEJ', + preferred: true, + required: true, + selectedSource: null, + }, + idCard: { + autoGeneration: false, + identifierName: 'ID Card', + identifierTypeUuid: 'b4143563-16cd-4439-b288-f83d61670fc8', + identifierUuid: '346d09b1-8509-43c6-9697-3b4d1ce06ad6', + identifierValue: '1234567890', + initialValue: '1234567890', + preferred: false, + required: false, + selectedSource: null, + }, + }, + isDead: false, + middleName: '', + monthsEstimated: 0, + nonCodedCauseOfDeath: '', + patientUuid: mockPatient.id, + relationships: [], + telephoneNumber: '', + yearsEstimated: 0, + } as FormValues, + jest.fn(), + ]); - mockUseParams.mockReturnValue({ patientUuid: mockPatient.id }); + render(, { wrapper: Wrapper }); - mockUsePatient.mockReturnValue({ - isLoading: false, - patient: mockPatient, - patientUuid: mockPatient.id, - error: null, - }); + await screen.findByRole('heading', { name: /edit patient details/i }); - render(, { wrapper: Wrapper }); - - const givenNameInput: HTMLInputElement = screen.getByLabelText(/First Name/); - const familyNameInput: HTMLInputElement = screen.getByLabelText(/Family Name/); - const middleNameInput: HTMLInputElement = screen.getByLabelText(/Middle Name/); - const dateOfBirthInput: HTMLInputElement = screen.getByLabelText(/Date of Birth/i); - const genderInput: HTMLInputElement = screen.getByLabelText(/Male/); - - // assert initial values - expect(givenNameInput.value).toBe('John'); - expect(familyNameInput.value).toBe('Wilson'); - expect(middleNameInput.value).toBeFalsy(); - expect(dateOfBirthInput.value).toBe('04/04/1972'); - expect(genderInput.value).toBe('male'); - - // do some edits - await user.clear(givenNameInput); - await user.clear(middleNameInput); - await user.clear(familyNameInput); - await user.type(givenNameInput, 'Eric'); - await user.type(middleNameInput, 'Johnson'); - await user.type(familyNameInput, 'Smith'); - await user.click(screen.getByText(/Update patient/i)); + expect(screen.queryByRole('button', { name: /register patient/i })).not.toBeInTheDocument(); + expect(screen.getByRole('button', { name: /update patient/i })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument(); - expect(mockSavePatient).toHaveBeenCalledWith( + expect(screen.getByLabelText(/first name/i)).toHaveValue(mockPatient.name[0].given[0]); + expect(screen.getByLabelText(/family name/i)).toHaveValue(mockPatient.name[0].family); + expect(screen.getByLabelText(/date of birth/i)).toHaveValue('04/04/1972'); + expect( + screen.getByRole('radio', { + name: /^male$/i, + }), + ).toBeChecked(); + expect( + screen.getByRole('radio', { + name: /^female$/i, + }), + ).not.toBeChecked(); + expect(screen.getAllByRole('tab', { name: /yes/i })).toHaveLength(2); + + await user.click(screen.getByRole('button', { name: /update patient/i })); + + expect(mockSavePatientForm).toHaveBeenCalledWith( false, { - '0': { - oldIdentificationNumber: '100732HE', - }, - '1': { - openMrsId: '100GEJ', - }, - addNameInLocalLanguage: undefined, + addNameInLocalLanguage: false, additionalFamilyName: '', additionalGivenName: '', additionalMiddleName: '', - address: {}, - birthdate: new Date('1972-04-04T00:00:00.000Z'), + address: { + country: 'កម្ពុជា (Cambodia)', + }, + birthdate: '1972-04-04', birthdateEstimated: false, deathCause: '', nonCodedCauseOfDeath: '', deathDate: undefined, deathTime: undefined, deathTimeFormat: 'AM', - familyName: 'Smith', - gender: expect.stringMatching(/male/i), - givenName: 'Eric', - identifiers: {}, + familyName: 'Wilson', + gender: 'male', + givenName: 'John', + identifiers: { + idCard: { + autoGeneration: false, + identifierName: 'ID Card', + identifierTypeUuid: 'b4143563-16cd-4439-b288-f83d61670fc8', + identifierUuid: '346d09b1-8509-43c6-9697-3b4d1ce06ad6', + identifierValue: '1234567890', + initialValue: '1234567890', + preferred: false, + required: false, + selectedSource: null, + }, + openMrsId: { + autoGeneration: false, + identifierName: 'OpenMRS ID', + identifierTypeUuid: '05a29f94-c0ed-11e2-94be-8c13b969e334', + identifierUuid: '1f0ad7a1-430f-4397-b571-59ea654a52db', + identifierValue: '100GEJ', + initialValue: '100GEJ', + preferred: true, + required: true, + selectedSource: null, + }, + }, isDead: false, - middleName: 'Johnson', + middleName: '', monthsEstimated: 0, patientUuid: '8673ee4f-e2ab-4077-ba55-4980f408773e', relationships: [], diff --git a/packages/esm-patient-registration-app/src/patient-registration/section/demographics/demographics-section.component.tsx b/packages/esm-patient-registration-app/src/patient-registration/section/demographics/demographics-section.component.tsx index 3ab1adb22..cf2e9074e 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/section/demographics/demographics-section.component.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/section/demographics/demographics-section.component.tsx @@ -1,8 +1,8 @@ import React, { useContext, useEffect } from 'react'; -import styles from './../section.scss'; import { useField } from 'formik'; -import { PatientRegistrationContext } from '../../patient-registration-context'; import { Field } from '../../field/field.component'; +import { PatientRegistrationContext } from '../../patient-registration-context'; +import styles from './../section.scss'; export interface DemographicsSectionProps { fields: Array; @@ -18,7 +18,7 @@ export const DemographicsSection: React.FC = ({ fields setFieldValue('additionalMiddleName', ''); setFieldValue('additionalFamilyName', ''); } - }, [field.value, meta.touched]); + }, [field.value, meta.touched, setFieldValue]); return (
diff --git a/packages/esm-patient-registration-app/src/patient-registration/section/patient-relationships/relationships.resource.tsx b/packages/esm-patient-registration-app/src/patient-registration/section/patient-relationships/relationships.resource.tsx index 643639167..810f374ac 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/section/patient-relationships/relationships.resource.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/section/patient-relationships/relationships.resource.tsx @@ -4,6 +4,33 @@ import { type FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-fram import { type RelationshipValue } from '../../patient-registration.types'; import { personRelationshipRepresentation } from '../../../constants'; +export interface Relationship { + display: string; + uuid: string; + personA: { + age: number; + display: string; + birthdate: string; + uuid: string; + }; + personB: { + age: number; + display: string; + birthdate: string; + uuid: string; + }; + relationshipType: { + uuid: string; + display: string; + aIsToB: string; + bIsToA: string; + }; +} + +interface RelationshipsResponse { + results: Array; +} + export function useInitialPatientRelationships(patientUuid: string): { data: Array; isLoading: boolean; @@ -45,34 +72,7 @@ export function useInitialPatientRelationships(patientUuid: string): { error, isLoading, }; - }, [patientUuid, data, error]); + }, [data?.data?.results, error, isLoading, patientUuid]); return result; } - -export interface Relationship { - display: string; - uuid: string; - personA: { - age: number; - display: string; - birthdate: string; - uuid: string; - }; - personB: { - age: number; - display: string; - birthdate: string; - uuid: string; - }; - relationshipType: { - uuid: string; - display: string; - aIsToB: string; - bIsToA: string; - }; -} - -interface RelationshipsResponse { - results: Array; -} diff --git a/packages/esm-patient-search-app/package.json b/packages/esm-patient-search-app/package.json index e70177005..2f03061e9 100644 --- a/packages/esm-patient-search-app/package.json +++ b/packages/esm-patient-search-app/package.json @@ -37,7 +37,7 @@ "url": "https://github.com/openmrs/openmrs-esm-patient-management/issues" }, "dependencies": { - "@carbon/react": "~1.37.0", + "@carbon/react": "^1.71.0", "lodash-es": "^4.17.15" }, "peerDependencies": { diff --git a/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-banner.component.tsx b/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-banner.component.tsx index 7cd9e425b..c64b8f3b0 100644 --- a/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-banner.component.tsx +++ b/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-banner.component.tsx @@ -1,6 +1,6 @@ import React, { forwardRef, useContext, useMemo } from 'react'; -import classNames from 'classnames'; import { v4 as uuidv4 } from 'uuid'; +import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import { Tag } from '@carbon/react'; import { @@ -173,7 +173,7 @@ const ClickablePatientContainer = ({ patient, children }: ClickablePatientContai const IdentifierTag: React.FC = ({ identifier }) => { return ( <> - + {identifier.identifierType.display} {identifier.identifier} diff --git a/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-search.component.tsx b/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-search.component.tsx index 7973a5bc9..8e74237d2 100644 --- a/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-search.component.tsx +++ b/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-search.component.tsx @@ -96,7 +96,7 @@ const CompactPatientSearchComponent: React.FC = ({ }); } }, - [config.search.patientChartUrl, user, currentLocation], + [addViewedPatientAndCloseSearchResults, config.search.patientChartUrl], ); const focusedResult = useArrowNavigation( !recentPatients ? searchedPatients?.length ?? 0 : recentPatients?.length ?? 0, @@ -134,7 +134,7 @@ const CompactPatientSearchComponent: React.FC = ({ subtitle: errorFetchingUserProperties?.message, }); } - }, [fetchError, errorFetchingUserProperties]); + }, [fetchError, errorFetchingUserProperties, t]); const handleSubmit = useCallback( (debouncedSearchTerm) => { diff --git a/packages/esm-patient-search-app/src/patient-search-button/patient-search-button.component.tsx b/packages/esm-patient-search-app/src/patient-search-button/patient-search-button.component.tsx index 5382abad5..23f63176a 100644 --- a/packages/esm-patient-search-app/src/patient-search-button/patient-search-button.component.tsx +++ b/packages/esm-patient-search-app/src/patient-search-button/patient-search-button.component.tsx @@ -1,7 +1,7 @@ +import React, { useCallback, useEffect } from 'react'; import { Button } from '@carbon/react'; import { Search } from '@carbon/react/icons'; import { launchWorkspace } from '@openmrs/esm-framework'; -import React, { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { type PatientSearchWorkspaceProps } from '../patient-search-workspace/patient-search.workspace'; @@ -37,7 +37,7 @@ const PatientSearchButton: React.FC = ({ }) => { const { t } = useTranslation(); - const launchPatientSearchWorkspace = () => { + const launchPatientSearchWorkspace = useCallback(() => { const workspaceProps: PatientSearchWorkspaceProps = { handleSearchTermUpdated: searchQueryUpdatedAction, initialQuery: searchQuery, @@ -47,13 +47,13 @@ const PatientSearchButton: React.FC = ({ ...workspaceProps, workspaceTitle: overlayHeader, }); - }; + }, [overlayHeader, searchQuery, searchQueryUpdatedAction, selectPatientAction]); useEffect(() => { if (isOpen) { launchPatientSearchWorkspace(); } - }, [isOpen]); + }, [isOpen, launchPatientSearchWorkspace]); return (