@kenyaemr/esm-patient-registration-app 4.5.3 → 4.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/117.js +2 -0
  2. package/dist/117.js.map +1 -0
  3. package/dist/130.js +1 -1
  4. package/dist/130.js.map +1 -1
  5. package/dist/208.js +1 -1
  6. package/dist/218.js +1 -0
  7. package/dist/218.js.map +1 -0
  8. package/dist/275.js +1 -0
  9. package/dist/275.js.map +1 -0
  10. package/dist/319.js +1 -1
  11. package/dist/{821.js → 348.js} +1 -1
  12. package/dist/{821.js.map → 348.js.map} +1 -1
  13. package/dist/574.js +1 -1
  14. package/dist/68.js +1 -1
  15. package/dist/68.js.map +1 -1
  16. package/dist/693.js +1 -0
  17. package/dist/693.js.map +1 -0
  18. package/dist/757.js +1 -1
  19. package/dist/788.js +1 -1
  20. package/dist/807.js +1 -1
  21. package/dist/833.js +1 -1
  22. package/dist/879.js +1 -0
  23. package/dist/879.js.map +1 -0
  24. package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
  25. package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +171 -122
  26. package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -1
  27. package/dist/main.js +1 -1
  28. package/dist/main.js.map +1 -1
  29. package/dist/routes.json +1 -1
  30. package/jest.config.js +3 -0
  31. package/package.json +5 -2
  32. package/src/config-schema.ts +11 -5
  33. package/src/index.ts +9 -2
  34. package/src/offline.resources.ts +8 -4
  35. package/src/offline.ts +1 -1
  36. package/src/patient-registration/field/__mocks__/field.resource.ts +1 -1
  37. package/src/patient-registration/field/address/address-field.component.tsx +25 -70
  38. package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +11 -9
  39. package/src/patient-registration/field/address/address-hierarchy.resource.tsx +57 -3
  40. package/src/patient-registration/field/address/address-search.component.tsx +1 -1
  41. package/src/patient-registration/field/address/tests/address-hierarchy.test.tsx +137 -63
  42. package/src/patient-registration/field/address/tests/address-search-component.test.tsx +128 -0
  43. package/src/patient-registration/field/address/tests/mocks.ts +93 -99
  44. package/src/patient-registration/field/dob/dob.component.tsx +48 -44
  45. package/src/patient-registration/field/dob/dob.test.tsx +6 -1
  46. package/src/patient-registration/field/field.resource.ts +1 -1
  47. package/src/patient-registration/field/id/id-field.component.tsx +1 -1
  48. package/src/patient-registration/field/id/identifier-selection-overlay.tsx +1 -1
  49. package/src/patient-registration/field/name/name-field.component.tsx +38 -22
  50. package/src/patient-registration/field/obs/obs-field.component.tsx +14 -13
  51. package/src/patient-registration/field/person-attributes/coded-attributes.component.tsx +1 -1
  52. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +1 -1
  53. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +103 -0
  54. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +187 -0
  55. package/src/patient-registration/field/person-attributes/person-attributes.resource.tsx +1 -1
  56. package/src/patient-registration/field/person-attributes/text-person-attribute-field.component.tsx +3 -3
  57. package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +88 -0
  58. package/src/patient-registration/form-manager.test.ts +1 -2
  59. package/src/patient-registration/form-manager.ts +1 -9
  60. package/src/patient-registration/input/basic-input/input/input.test.tsx +0 -135
  61. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +8 -4
  62. package/src/patient-registration/input/combo-input/combo-input.component.tsx +8 -6
  63. package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +1 -1
  64. package/src/patient-registration/input/custom-input/identifier/utils.test.ts +81 -0
  65. package/src/patient-registration/input/custom-input/identifier/utils.ts +1 -1
  66. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +1 -2
  67. package/src/patient-registration/patient-registration-context.ts +1 -1
  68. package/src/patient-registration/patient-registration-hooks.ts +14 -2
  69. package/src/patient-registration/patient-registration-utils.ts +1 -4
  70. package/src/patient-registration/patient-registration.component.tsx +1 -12
  71. package/src/patient-registration/patient-registration.resource.tsx +1 -72
  72. package/src/patient-registration/patient-registration.test.tsx +250 -247
  73. package/src/patient-registration/{patient-registration-types.tsx → patient-registration.types.tsx} +45 -1
  74. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +83 -79
  75. package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +88 -0
  76. package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +1 -1
  77. package/src/patient-registration/validation/patient-registration-validation.tsx +1 -1
  78. package/src/patient-verification/patient-verification-hook.tsx +12 -1
  79. package/src/patient-verification/patient-verification-utils.ts +1 -1
  80. package/src/patient-verification/patient-verification.component.tsx +1 -1
  81. package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +11 -0
  82. package/src/root.component.tsx +1 -1
  83. package/src/routes.json +62 -51
  84. package/src/widgets/cancel-patient-edit.test.tsx +24 -0
  85. package/src/widgets/delete-identifier-confirmation-modal.test.tsx +31 -0
  86. package/src/widgets/display-photo.test.tsx +37 -0
  87. package/src/widgets/edit-patient-details-button.test.tsx +36 -0
  88. package/translations/am.json +0 -7
  89. package/translations/en.json +2 -5
  90. package/translations/es.json +0 -7
  91. package/translations/fr.json +0 -7
  92. package/translations/he.json +0 -7
  93. package/translations/km.json +0 -7
  94. package/__mocks__/react-i18next.js +0 -49
  95. package/dist/196.js +0 -1
  96. package/dist/196.js.map +0 -1
  97. package/dist/59.js +0 -1
  98. package/dist/59.js.map +0 -1
  99. package/dist/9.js +0 -2
  100. package/dist/9.js.map +0 -1
  101. /package/dist/{9.js.LICENSE.txt → 117.js.LICENSE.txt} +0 -0
@@ -15,7 +15,7 @@ import { Autosuggest } from '../../input/custom-input/autosuggest/autosuggest.co
15
15
  import { PatientRegistrationContext } from '../../patient-registration-context';
16
16
  import { ResourcesContext } from '../../../offline.resources';
17
17
  import { fetchPerson } from '../../patient-registration.resource';
18
- import { RelationshipValue } from '../../patient-registration-types';
18
+ import { RelationshipValue } from '../../patient-registration.types';
19
19
  import sectionStyles from '../section.scss';
20
20
  import styles from './relationships.scss';
21
21
 
@@ -25,82 +25,6 @@ interface RelationshipType {
25
25
  direction: string;
26
26
  }
27
27
 
28
- export const RelationshipsSection = () => {
29
- const { relationshipTypes } = useContext(ResourcesContext);
30
- const [displayRelationshipTypes, setDisplayRelationshipTypes] = useState<RelationshipType[]>([]);
31
- const { t } = useTranslation();
32
-
33
- useEffect(() => {
34
- if (relationshipTypes) {
35
- const tmp: RelationshipType[] = [];
36
- relationshipTypes.results.forEach((type) => {
37
- const aIsToB = {
38
- display: type.aIsToB,
39
- uuid: type.uuid,
40
- direction: 'aIsToB',
41
- };
42
- const bIsToA = {
43
- display: type.bIsToA,
44
- uuid: type.uuid,
45
- direction: 'bIsToA',
46
- };
47
- aIsToB.display === bIsToA.display ? tmp.push(aIsToB) : tmp.push(aIsToB, bIsToA);
48
- });
49
- setDisplayRelationshipTypes(tmp);
50
- }
51
- }, [relationshipTypes]);
52
-
53
- if (!relationshipTypes) {
54
- return (
55
- <section aria-label="Relationships Section">
56
- <SkeletonText />
57
- </section>
58
- );
59
- }
60
-
61
- return (
62
- <section aria-label="Relationships Section">
63
- <FieldArray name="relationships">
64
- {({
65
- push,
66
- remove,
67
- form: {
68
- values: { relationships },
69
- },
70
- }) => (
71
- <div>
72
- {relationships && relationships.length > 0
73
- ? relationships.map((relationship: RelationshipValue, index) => (
74
- <div key={index} className={sectionStyles.formSection}>
75
- <RelationshipView
76
- relationship={relationship}
77
- index={index}
78
- displayRelationshipTypes={displayRelationshipTypes}
79
- key={index}
80
- remove={remove}
81
- />
82
- </div>
83
- ))
84
- : null}
85
- <div className={styles.actions}>
86
- <Button
87
- kind="ghost"
88
- onClick={() =>
89
- push({
90
- relatedPersonUuid: '',
91
- action: 'ADD',
92
- })
93
- }>
94
- {t('addRelationshipButtonText', 'Add Relationship')}
95
- </Button>
96
- </div>
97
- </div>
98
- )}
99
- </FieldArray>
100
- </section>
101
- );
102
- };
103
-
104
28
  interface RelationshipViewProps {
105
29
  relationship: RelationshipValue;
106
30
  index: number;
@@ -205,8 +129,12 @@ const RelationshipView: React.FC<RelationshipViewProps> = ({
205
129
  value="placeholder-item"
206
130
  text={t('relationshipToPatient', 'Relationship to patient')}
207
131
  />
208
- {displayRelationshipTypes.map((type) => (
209
- <SelectItem text={type.display} value={`${type.uuid}/${type.direction}`} key={type.display} />
132
+ {displayRelationshipTypes.map((relationshipType, index) => (
133
+ <SelectItem
134
+ text={relationshipType.display}
135
+ value={`${relationshipType.uuid}/${relationshipType.direction}`}
136
+ key={`relationship-${relationshipType.uuid}-${index}`}
137
+ />
210
138
  ))}
211
139
  </Select>
212
140
  </Layer>
@@ -224,3 +152,79 @@ const RelationshipView: React.FC<RelationshipViewProps> = ({
224
152
  />
225
153
  );
226
154
  };
155
+
156
+ export const RelationshipsSection = () => {
157
+ const { relationshipTypes } = useContext(ResourcesContext);
158
+ const [displayRelationshipTypes, setDisplayRelationshipTypes] = useState<RelationshipType[]>([]);
159
+ const { t } = useTranslation();
160
+
161
+ useEffect(() => {
162
+ if (relationshipTypes) {
163
+ const tmp: RelationshipType[] = [];
164
+ relationshipTypes.results.forEach((type) => {
165
+ const aIsToB = {
166
+ display: type.aIsToB,
167
+ uuid: type.uuid,
168
+ direction: 'aIsToB',
169
+ };
170
+ const bIsToA = {
171
+ display: type.bIsToA,
172
+ uuid: type.uuid,
173
+ direction: 'bIsToA',
174
+ };
175
+ aIsToB.display === bIsToA.display ? tmp.push(aIsToB) : tmp.push(aIsToB, bIsToA);
176
+ });
177
+ setDisplayRelationshipTypes(tmp);
178
+ }
179
+ }, [relationshipTypes]);
180
+
181
+ if (!relationshipTypes) {
182
+ return (
183
+ <section aria-label="Loading relationships section">
184
+ <SkeletonText role="progressbar" />
185
+ </section>
186
+ );
187
+ }
188
+
189
+ return (
190
+ <section aria-label="Relationships section">
191
+ <FieldArray name="relationships">
192
+ {({
193
+ push,
194
+ remove,
195
+ form: {
196
+ values: { relationships },
197
+ },
198
+ }) => (
199
+ <div>
200
+ {relationships && relationships.length > 0
201
+ ? relationships.map((relationship: RelationshipValue, index) => (
202
+ <div key={index} className={sectionStyles.formSection}>
203
+ <RelationshipView
204
+ relationship={relationship}
205
+ index={index}
206
+ displayRelationshipTypes={displayRelationshipTypes}
207
+ key={index}
208
+ remove={remove}
209
+ />
210
+ </div>
211
+ ))
212
+ : null}
213
+ <div className={styles.actions}>
214
+ <Button
215
+ kind="ghost"
216
+ onClick={() =>
217
+ push({
218
+ relatedPersonUuid: '',
219
+ action: 'ADD',
220
+ })
221
+ }>
222
+ {t('addRelationshipButtonText', 'Add Relationship')}
223
+ </Button>
224
+ </div>
225
+ </div>
226
+ )}
227
+ </FieldArray>
228
+ </section>
229
+ );
230
+ };
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import { Form, Formik } from 'formik';
3
+ import { render, screen } from '@testing-library/react';
4
+ import { PatientRegistrationContext } from '../../patient-registration-context';
5
+ import { Resources, ResourcesContext } from '../../../offline.resources';
6
+ import { RelationshipsSection } from './relationships-section.component';
7
+
8
+ jest.mock('../../patient-registration.resource', () => ({
9
+ fetchPerson: jest.fn().mockResolvedValue({
10
+ data: {
11
+ results: [
12
+ { uuid: '42ae5ce0-d64b-11ea-9064-5adc43bbdd24', display: 'Person 1' },
13
+ { uuid: '691eed12-c0f1-11e2-94be-8c13b969e334', display: 'Person 2' },
14
+ ],
15
+ },
16
+ }),
17
+ }));
18
+
19
+ let mockResourcesContextValue = {
20
+ addressTemplate: [],
21
+ currentSession: {
22
+ authenticated: true,
23
+ sessionId: 'JSESSION',
24
+ currentProvider: { uuid: '45ce6c2e-dd5a-11e6-9d9c-0242ac150002', identifier: 'PRO-123' },
25
+ },
26
+ identifierTypes: [],
27
+ relationshipTypes: null,
28
+ } as Resources;
29
+
30
+ describe('RelationshipsSection', () => {
31
+ it('renders a loader when relationshipTypes are not available', () => {
32
+ render(
33
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
34
+ <Formik initialValues={{}} onSubmit={null}>
35
+ <Form>
36
+ <RelationshipsSection />
37
+ </Form>
38
+ </Formik>
39
+ </ResourcesContext.Provider>,
40
+ );
41
+
42
+ expect(screen.getByLabelText(/loading relationships section/i)).toBeInTheDocument();
43
+ expect(screen.getByRole(/progressbar/i)).toBeInTheDocument();
44
+ expect(screen.queryByText(/add relationship/i)).not.toBeInTheDocument();
45
+ });
46
+
47
+ it('renders relationships when relationshipTypes are available', () => {
48
+ const relationshipTypes = {
49
+ results: [
50
+ { aIsToB: 'Mother', bIsToA: 'Child', uuid: '42ae5ce0-d64b-11ea-9064-5adc43bbdd34' },
51
+ { aIsToB: 'Father', bIsToA: 'Child', uuid: '52ae5ce0-d64b-11ea-9064-5adc43bbdd24' },
52
+ ],
53
+ };
54
+ mockResourcesContextValue = {
55
+ ...mockResourcesContextValue,
56
+ relationshipTypes: relationshipTypes,
57
+ };
58
+
59
+ render(
60
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
61
+ <Formik
62
+ initialValues={{
63
+ relationships: [{ action: 'ADD', relatedPersonUuid: '11524ae7-3ef6-4ab6-aff6-804ffc58704a' }],
64
+ }}
65
+ onSubmit={null}>
66
+ <Form>
67
+ <PatientRegistrationContext.Provider
68
+ value={{
69
+ setFieldValue: jest.fn(),
70
+ setInitialFormValues: jest.fn(),
71
+ }}>
72
+ <RelationshipsSection />
73
+ </PatientRegistrationContext.Provider>
74
+ </Form>
75
+ </Formik>
76
+ </ResourcesContext.Provider>,
77
+ );
78
+
79
+ expect(screen.getByLabelText(/relationships section/i)).toBeInTheDocument();
80
+ expect(screen.getByRole('heading', { name: /relationship/i })).toBeInTheDocument();
81
+ expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument();
82
+ expect(screen.getByRole('button', { name: /add relationship/i })).toBeInTheDocument();
83
+ expect(screen.getByRole('searchbox', { name: /full name/i })).toBeInTheDocument();
84
+ expect(screen.getByRole('option', { name: /mother/i })).toBeInTheDocument();
85
+ expect(screen.getByRole('option', { name: /father/i })).toBeInTheDocument();
86
+ expect(screen.getAllByRole('option', { name: /child/i }).length).toEqual(2);
87
+ });
88
+ });
@@ -1,5 +1,5 @@
1
1
  import { FetchResponse, openmrsFetch, showToast } from '@openmrs/esm-framework';
2
- import { RelationshipValue } from '../../patient-registration-types';
2
+ import { RelationshipValue } from '../../patient-registration.types';
3
3
  import useSWR from 'swr';
4
4
  import { useMemo } from 'react';
5
5
  import { personRelationshipRepresentation } from '../../../constants';
@@ -1,6 +1,6 @@
1
1
  import * as Yup from 'yup';
2
2
  import mapValues from 'lodash/mapValues';
3
- import { FormValues } from '../patient-registration-types';
3
+ import { FormValues } from '../patient-registration.types';
4
4
 
5
5
  export const validationSchema = Yup.object({
6
6
  givenName: Yup.string().required('givenNameRequired'),
@@ -1,8 +1,8 @@
1
1
  import { FetchResponse, openmrsFetch, showNotification, showToast } from '@openmrs/esm-framework';
2
- import { ConceptAnswers, ConceptResponse, FormValues } from '../patient-registration/patient-registration-types';
3
2
  import { generateNUPIPayload, handleClientRegistryResponse } from './patient-verification-utils';
4
3
  import useSWR from 'swr';
5
4
  import useSWRImmutable from 'swr/immutable';
5
+ import { ConceptAnswers, ConceptResponse, FormValues } from '../patient-registration/patient-registration.types';
6
6
 
7
7
  export function searchClientRegistry(identifierType: string, searchTerm: string, token: string) {
8
8
  const url = `https://afyakenyaapi.health.go.ke/partners/registry/search/KE/${identifierType}/${searchTerm}`;
@@ -154,3 +154,14 @@ async function postToRegistry(
154
154
  showNotification({ kind: 'error', title: 'NUPI Post failed', description: JSON.stringify(error) });
155
155
  }
156
156
  }
157
+
158
+ export const useFacilityName = (facilityCode) => {
159
+ const apiUrl = `/ws/rest/v1/kenyaemr/facilityName?facilityCode=${facilityCode}`;
160
+ const { data, error, isLoading } = useSWRImmutable<FetchResponse>(apiUrl, openmrsFetch);
161
+
162
+ return {
163
+ facilityName: data ? data?.data : null,
164
+ isLoading: isLoading,
165
+ isError: error,
166
+ };
167
+ };
@@ -1,8 +1,8 @@
1
1
  import { showModal } from '@openmrs/esm-framework';
2
2
  import { FormikProps } from 'formik';
3
- import { FormValues } from '../patient-registration/patient-registration-types';
4
3
  import { ClientRegistryPatient, RegistryPatient } from './verification-types';
5
4
  import counties from './assets/counties.json';
5
+ import { FormValues } from '../patient-registration/patient-registration.types';
6
6
 
7
7
  export function handleClientRegistryResponse(
8
8
  clientResponse: ClientRegistryPatient,
@@ -6,8 +6,8 @@ import { countries, verificationIdentifierTypes } from './assets/verification-as
6
6
  import { searchClientRegistry, useGlobalProperties } from './patient-verification-hook';
7
7
  import { showToast } from '@openmrs/esm-framework';
8
8
  import { handleClientRegistryResponse } from './patient-verification-utils';
9
- import { FormValues } from '../patient-registration/patient-registration-types';
10
9
  import { FormikProps } from 'formik';
10
+ import { FormValues } from '../patient-registration/patient-registration.types';
11
11
 
12
12
  interface PatientVerificationProps {
13
13
  props: FormikProps<FormValues>;
@@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';
3
3
  import { Button } from '@carbon/react';
4
4
  import { age, ExtensionSlot, formatDate } from '@openmrs/esm-framework';
5
5
  import capitalize from 'lodash-es/capitalize';
6
+ import { useFacilityName } from '../patient-verification-hook';
6
7
 
7
8
  const PatientInfo: React.FC<{ label: string; value: string }> = ({ label, value }) => {
8
9
  return (
@@ -21,6 +22,8 @@ interface ConfirmPromptProps {
21
22
 
22
23
  const ConfirmPrompt: React.FC<ConfirmPromptProps> = ({ close, onConfirm, patient }) => {
23
24
  const { t } = useTranslation();
25
+ const { facilityName, isLoading } = useFacilityName(patient?.originFacilityKmflCode);
26
+
24
27
  return (
25
28
  <>
26
29
  <div className="cds--modal-header">
@@ -54,6 +57,14 @@ const ConfirmPrompt: React.FC<ConfirmPromptProps> = ({ close, onConfirm, patient
54
57
  <PatientInfo label={t('dateOfBirth', 'Date of birth')} value={formatDate(new Date(patient?.dateOfBirth))} />
55
58
  <PatientInfo label={t('gender', 'Gender')} value={capitalize(patient?.gender)} />
56
59
  <PatientInfo label={t('nascopNumber', 'Nascop facility no')} value={capitalize(patient?.nascopCCCNumber)} />
60
+ <PatientInfo
61
+ label={t('originFacilityCode', 'Origin facility code')}
62
+ value={patient?.originFacilityKmflCode}
63
+ />
64
+ <PatientInfo
65
+ label={t('originFacilityName', 'Origin facility name')}
66
+ value={isLoading ? '--' : facilityName?.name}
67
+ />
57
68
  </div>
58
69
  </div>
59
70
  </div>
@@ -9,7 +9,7 @@ import {
9
9
  fetchPatientIdentifierTypesWithSources,
10
10
  } from './offline.resources';
11
11
  import { FormManager } from './patient-registration/form-manager';
12
- import { PatientRegistration, PatientRegistrationProps } from './patient-registration/patient-registration.component';
12
+ import { PatientRegistration } from './patient-registration/patient-registration.component';
13
13
  import useSWRImmutable from 'swr/immutable';
14
14
  import styles from './root.scss';
15
15
 
package/src/routes.json CHANGED
@@ -3,54 +3,65 @@
3
3
  "backendDependencies": {
4
4
  "webservices.rest": "^2.24.0"
5
5
  },
6
- "pages": [{
7
- "component": "root",
8
- "route": "patient-registration",
9
- "online": true,
10
- "offline": true
11
- }, {
12
- "component": "editPatient",
13
- "routeRegex": "patient\\/([a-zA-Z0-9\\-]+)\\/edit",
14
- "online": true,
15
- "offline": true
16
- }],
17
- "extensions": [{
18
- "component": "addPatientLink",
19
- "name": "add-patient-action",
20
- "slot": "top-nav-actions-slot",
21
- "online": true,
22
- "offline": true
23
- }, {
24
- "component": "cancelPatientEditModal",
25
- "name": "cancel-patient-edit-modal",
26
- "online": true,
27
- "offline": true
28
- }, {
29
- "component": "patientPhoto",
30
- "name": "patient-photo-widget",
31
- "slot": "patient-photo-slot",
32
- "online": true,
33
- "offline": true
34
- }, {
35
- "component": "editPatientDetailsButton",
36
- "name": "edit-patient-details-button",
37
- "slot": "patient-actions-slot",
38
- "online": true,
39
- "offline": true
40
- }, {
41
- "component": "deleteIdentifierConfirmationModal",
42
- "name": "delete-identifier-confirmation-modal",
43
- "online": true,
44
- "offline": true
45
- }, {
46
- "component": "emptyVerificationModal",
47
- "name":"empty-client-registry-modal",
48
- "online": true,
49
- "offline": false
50
- },{
51
- "component": "confirmationVerificationModal",
52
- "name": "confirm-client-registry-modal",
53
- "online": true,
54
- "offline": false
55
- }]
56
- }
6
+ "pages": [
7
+ {
8
+ "component": "root",
9
+ "route": "patient-registration",
10
+ "online": true,
11
+ "offline": true
12
+ },
13
+ {
14
+ "component": "editPatient",
15
+ "routeRegex": "patient\\/([a-zA-Z0-9\\-]+)\\/edit",
16
+ "online": true,
17
+ "offline": true
18
+ }
19
+ ],
20
+ "extensions": [
21
+ {
22
+ "component": "addPatientLink",
23
+ "name": "add-patient-action",
24
+ "slot": "top-nav-actions-slot",
25
+ "online": true,
26
+ "offline": true
27
+ },
28
+ {
29
+ "component": "cancelPatientEditModal",
30
+ "name": "cancel-patient-edit-modal",
31
+ "online": true,
32
+ "offline": true
33
+ },
34
+ {
35
+ "component": "patientPhoto",
36
+ "name": "patient-photo-widget",
37
+ "slot": "patient-photo-slot",
38
+ "online": true,
39
+ "offline": true
40
+ },
41
+ {
42
+ "component": "editPatientDetailsButton",
43
+ "name": "edit-patient-details-button",
44
+ "slot": "patient-actions-slot",
45
+ "online": true,
46
+ "offline": true
47
+ },
48
+ {
49
+ "component": "deleteIdentifierConfirmationModal",
50
+ "name": "delete-identifier-confirmation-modal",
51
+ "online": true,
52
+ "offline": true
53
+ },
54
+ {
55
+ "component": "emptyClientRegistryModal",
56
+ "name": "empty-client-registry-modal",
57
+ "online": true,
58
+ "offline": true
59
+ },
60
+ {
61
+ "component": "confirmClientRegistryModal",
62
+ "name": "confirm-client-registry-modal",
63
+ "online": true,
64
+ "offline": true
65
+ }
66
+ ]
67
+ }
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { screen, render, fireEvent } from '@testing-library/react';
3
+ import CancelPatientEdit from './cancel-patient-edit.component';
4
+
5
+ describe('CancelPatientEdit component', () => {
6
+ const mockClose = jest.fn();
7
+ const mockOnConfirm = jest.fn();
8
+
9
+ beforeEach(() => {
10
+ jest.clearAllMocks();
11
+ });
12
+
13
+ it('renders the modal and triggers close and onConfirm functions', () => {
14
+ render(<CancelPatientEdit close={mockClose} onConfirm={mockOnConfirm} />);
15
+
16
+ const cancelButton = screen.getByRole('button', { name: /Cancel/i });
17
+ fireEvent.click(cancelButton);
18
+ expect(mockClose).toHaveBeenCalledTimes(1);
19
+
20
+ const discardButton = screen.getByRole('button', { name: /discard/i });
21
+ fireEvent.click(discardButton);
22
+ expect(mockOnConfirm).toHaveBeenCalledTimes(1);
23
+ });
24
+ });
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen } from '@testing-library/react';
3
+ import DeleteIdentifierConfirmationModal from './delete-identifier-confirmation-modal';
4
+
5
+ describe('DeleteIdentifierConfirmationModal component', () => {
6
+ const mockDeleteIdentifier = jest.fn();
7
+ const mockIdentifierName = 'Identifier Name';
8
+ const mockIdentifierValue = 'Identifier Value';
9
+
10
+ beforeEach(() => {
11
+ jest.clearAllMocks();
12
+ });
13
+
14
+ it('renders the modal and triggers deleteIdentifier function', () => {
15
+ render(
16
+ <DeleteIdentifierConfirmationModal
17
+ deleteIdentifier={mockDeleteIdentifier}
18
+ identifierName={mockIdentifierName}
19
+ identifierValue={mockIdentifierValue}
20
+ />,
21
+ );
22
+
23
+ const cancelButton = screen.getByRole('button', { name: /cancel/i });
24
+ fireEvent.click(cancelButton);
25
+ expect(mockDeleteIdentifier).toHaveBeenCalledWith(false);
26
+
27
+ const removeButton = screen.getByRole('button', { name: /remove identifier/i });
28
+ fireEvent.click(removeButton);
29
+ expect(mockDeleteIdentifier).toHaveBeenCalledWith(true);
30
+ });
31
+ });
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import DisplayPatientPhoto from './display-photo.component';
4
+ import { mockPatient } from '../../../../__mocks__/appointments.mock';
5
+
6
+ jest.mock('../patient-registration/patient-registration.resource', () => ({
7
+ usePatientPhoto: jest.fn().mockReturnValue({ data: { imageSrc: 'test-image-src' } }),
8
+ }));
9
+
10
+ jest.mock('geopattern', () => ({
11
+ generate: jest.fn().mockReturnValue({
12
+ toDataUri: jest.fn().mockReturnValue('https://example.com'),
13
+ }),
14
+ }));
15
+
16
+ const patientUuid = mockPatient.uuid;
17
+ const patientName = mockPatient.name;
18
+
19
+ describe('DisplayPatientPhoto Component', () => {
20
+ it('should render the component with the patient photo and size should not be small', () => {
21
+ render(<DisplayPatientPhoto patientUuid={patientUuid} patientName={patientName} />);
22
+
23
+ const avatarImage = screen.getByTitle(`${patientName}`);
24
+
25
+ expect(avatarImage).toBeInTheDocument();
26
+ expect(avatarImage).toHaveAttribute('style', expect.stringContaining('width: 80px; height: 80px'));
27
+ });
28
+
29
+ it('should render the component with the patient photo and size should be small i.e. 48px', () => {
30
+ render(<DisplayPatientPhoto patientUuid={patientUuid} patientName={patientName} size="small" />);
31
+
32
+ const avatarImage = screen.getByTitle(`${patientName}`);
33
+
34
+ expect(avatarImage).toBeInTheDocument();
35
+ expect(avatarImage).toHaveAttribute('style', expect.stringContaining('width: 48px; height: 48px'));
36
+ });
37
+ });
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen } from '@testing-library/react';
3
+ import EditPatientDetailsButton from './edit-patient-details-button.component';
4
+ import { navigate } from '@openmrs/esm-framework';
5
+ import { mockPatient } from '../../../../__mocks__/appointments.mock';
6
+
7
+ describe('EditPatientDetailsButton', () => {
8
+ const patientUuid = mockPatient.uuid;
9
+ it('should navigate to the edit page when clicked', () => {
10
+ const mockNavigate = navigate as jest.Mock;
11
+
12
+ jest.mock('@openmrs/esm-framework', () => {
13
+ const originalModule = jest.requireActual('@openmrs/esm-framework');
14
+ return {
15
+ ...originalModule,
16
+ };
17
+ });
18
+
19
+ render(<EditPatientDetailsButton patientUuid={patientUuid} />);
20
+
21
+ const button = screen.getByRole('menuitem');
22
+ fireEvent.click(button);
23
+
24
+ expect(mockNavigate).toHaveBeenCalledWith({ to: expect.stringContaining(`/patient/${patientUuid}/edit`) });
25
+ });
26
+
27
+ it('should call the onTransition function when provided', () => {
28
+ const onTransitionMock = jest.fn();
29
+ render(<EditPatientDetailsButton patientUuid={patientUuid} onTransition={onTransitionMock} />);
30
+
31
+ const button = screen.getByRole('menuitem');
32
+ fireEvent.click(button);
33
+
34
+ expect(onTransitionMock).toHaveBeenCalled();
35
+ });
36
+ });
@@ -1,7 +1,5 @@
1
1
  {
2
2
  "addRelationshipButtonText": "Add Relationship",
3
- "address1": "Address line 1",
4
- "address2": "Address line 2",
5
3
  "addressHeader": "Address",
6
4
  "allFieldsRequiredText": "All fields are required unless marked optional",
7
5
  "autoGeneratedPlaceholderText": "Auto-generated",
@@ -10,11 +8,8 @@
10
8
  "birthFieldLabelText": "Birth",
11
9
  "cancel": "Cancel",
12
10
  "causeOfDeathInputLabel": "Cause of Death",
13
- "cityVillage": "city",
14
11
  "configure": "Configure",
15
12
  "contactSection": "Contact Details",
16
- "country": "Country",
17
- "countyDistrict": "District",
18
13
  "createNew": "Create New",
19
14
  "dateOfBirthLabelText": "Date of Birth",
20
15
  "deathDateInputLabel": "Date of Death",
@@ -63,7 +58,6 @@
63
58
  "other": "Other",
64
59
  "patient": "Patient",
65
60
  "patientNameKnown": "Patient's Name is Known?",
66
- "postalCode": "Postal code",
67
61
  "registerPatient": "Register Patient",
68
62
  "registrationSuccessToastDescription": "The patient can now be found by searching for them using their name or ID number",
69
63
  "registrationSuccessToastTitle": "New Patient Created",
@@ -79,7 +73,6 @@
79
73
  "searchAddress": "Search address",
80
74
  "selectAnOption": "Select an option",
81
75
  "sexFieldLabelText": "Sex",
82
- "stateProvince": "State",
83
76
  "stroke": "Stroke",
84
77
  "unableToFetch": "Unable to fetch person attribute type - {personattributetype}",
85
78
  "unknown": "Unknown",