@kenyaemr/esm-patient-registration-app 5.2.2 → 6.0.0

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 (122) hide show
  1. package/dist/130.js +1 -1
  2. package/dist/130.js.map +1 -1
  3. package/dist/271.js +1 -0
  4. package/dist/319.js +1 -1
  5. package/dist/330.js +1 -1
  6. package/dist/460.js +1 -1
  7. package/dist/537.js +1 -1
  8. package/dist/574.js +1 -1
  9. package/dist/59.js +1 -1
  10. package/dist/59.js.map +1 -1
  11. package/dist/619.js +1 -0
  12. package/dist/619.js.map +1 -0
  13. package/dist/644.js +1 -0
  14. package/dist/735.js +1 -1
  15. package/dist/757.js +1 -1
  16. package/dist/784.js +1 -1
  17. package/dist/788.js +1 -1
  18. package/dist/807.js +1 -1
  19. package/dist/833.js +1 -1
  20. package/dist/895.js +2 -0
  21. package/dist/{388.js.LICENSE.txt → 895.js.LICENSE.txt} +4 -2
  22. package/dist/895.js.map +1 -0
  23. package/dist/{openmrs-esm-patient-registration-app.js → kenyaemr-esm-patient-registration-app.js} +1 -1
  24. package/dist/{openmrs-esm-patient-registration-app.js.buildmanifest.json → kenyaemr-esm-patient-registration-app.js.buildmanifest.json} +117 -73
  25. package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -0
  26. package/dist/main.js +1 -1
  27. package/dist/main.js.LICENSE.txt +4 -2
  28. package/dist/main.js.map +1 -1
  29. package/dist/routes.json +1 -1
  30. package/package.json +6 -5
  31. package/src/add-patient-link.test.tsx +9 -7
  32. package/src/config-schema.ts +31 -38
  33. package/src/constants.ts +1 -1
  34. package/src/offline.resources.ts +13 -18
  35. package/src/offline.ts +8 -6
  36. package/src/patient-registration/before-save-prompt.tsx +2 -1
  37. package/src/patient-registration/field/__mocks__/field.resource.ts +1 -1
  38. package/src/patient-registration/field/address/address-field.component.tsx +5 -4
  39. package/src/patient-registration/field/address/address-hierarchy.resource.tsx +2 -2
  40. package/src/patient-registration/field/address/address-search.component.tsx +1 -14
  41. package/src/patient-registration/field/address/custom-address-field.component.tsx +1 -1
  42. package/src/patient-registration/field/address/tests/address-hierarchy.test.tsx +3 -3
  43. package/src/patient-registration/field/address/tests/address-search-component.test.tsx +14 -7
  44. package/src/patient-registration/field/custom-field.component.tsx +1 -1
  45. package/src/patient-registration/field/dob/dob.component.tsx +2 -2
  46. package/src/patient-registration/field/dob/dob.test.tsx +0 -3
  47. package/src/patient-registration/field/field.component.tsx +4 -1
  48. package/src/patient-registration/field/field.resource.ts +4 -4
  49. package/src/patient-registration/field/field.test.tsx +98 -95
  50. package/src/patient-registration/field/gender/gender-field.component.tsx +4 -4
  51. package/src/patient-registration/field/gender/gender-field.test.tsx +7 -14
  52. package/src/patient-registration/field/id/id-field.component.tsx +6 -6
  53. package/src/patient-registration/field/id/id-field.test.tsx +9 -7
  54. package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +1 -1
  55. package/src/patient-registration/field/name/name-field.component.tsx +1 -1
  56. package/src/patient-registration/field/obs/obs-field.component.tsx +35 -33
  57. package/src/patient-registration/field/obs/obs-field.test.tsx +106 -28
  58. package/src/patient-registration/field/person-attributes/coded-attributes.component.tsx +1 -1
  59. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +70 -24
  60. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +54 -30
  61. package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +4 -3
  62. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +1 -1
  63. package/src/patient-registration/field/person-attributes/{person-attributes.resource.tsx → person-attributes.resource.ts} +3 -3
  64. package/src/patient-registration/field/person-attributes/text-person-attribute-field.component.tsx +1 -1
  65. package/src/patient-registration/field/phone/phone-field.component.tsx +16 -0
  66. package/src/patient-registration/form-manager.test.ts +1 -1
  67. package/src/patient-registration/form-manager.ts +22 -24
  68. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +3 -3
  69. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +5 -5
  70. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +70 -58
  71. package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +2 -2
  72. package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +2 -5
  73. package/src/patient-registration/input/custom-input/identifier/utils.ts +1 -1
  74. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +1 -1
  75. package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +6 -6
  76. package/src/patient-registration/patient-registration-context.ts +3 -4
  77. package/src/patient-registration/patient-registration-hooks.ts +25 -20
  78. package/src/patient-registration/patient-registration-utils.ts +7 -7
  79. package/src/patient-registration/patient-registration.component.tsx +20 -10
  80. package/src/patient-registration/patient-registration.resource.test.tsx +3 -3
  81. package/src/patient-registration/{patient-registration.resource.tsx → patient-registration.resource.ts} +15 -15
  82. package/src/patient-registration/patient-registration.test.tsx +270 -251
  83. package/src/patient-registration/{patient-registration.types.tsx → patient-registration.types.ts} +12 -3
  84. package/src/patient-registration/section/death-info/death-info-section.test.tsx +33 -45
  85. package/src/patient-registration/section/demographics/demographics-section.test.tsx +1 -2
  86. package/src/patient-registration/section/generic-section.component.tsx +1 -1
  87. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +3 -3
  88. package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +17 -5
  89. package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +4 -4
  90. package/src/patient-registration/section/section-wrapper.component.tsx +1 -1
  91. package/src/patient-registration/section/section.component.tsx +1 -1
  92. package/src/patient-registration/validation/patient-registration-validation.test.tsx +140 -126
  93. package/src/patient-registration/validation/patient-registration-validation.tsx +54 -46
  94. package/src/patient-verification/patient-verification-hook.tsx +13 -4
  95. package/src/patient-verification/patient-verification-utils.ts +20 -12
  96. package/src/patient-verification/patient-verification.component.tsx +13 -6
  97. package/src/patient-verification/patient-verification.scss +0 -1
  98. package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +2 -11
  99. package/src/routes.json +1 -0
  100. package/src/widgets/cancel-patient-edit.test.tsx +7 -4
  101. package/src/widgets/delete-identifier-confirmation-modal.test.tsx +7 -4
  102. package/src/widgets/display-photo.test.tsx +1 -1
  103. package/src/widgets/edit-patient-details-button.test.tsx +12 -7
  104. package/translations/am.json +30 -14
  105. package/translations/ar.json +30 -14
  106. package/translations/en.json +11 -11
  107. package/translations/es.json +34 -22
  108. package/translations/fr.json +48 -40
  109. package/translations/he.json +22 -2
  110. package/translations/km.json +22 -2
  111. package/translations/zh.json +97 -0
  112. package/translations/zh_CN.json +97 -0
  113. package/tsconfig.json +1 -1
  114. package/__mocks__/autogenerationoptions.mock.ts +0 -34
  115. package/dist/388.js +0 -2
  116. package/dist/388.js.map +0 -1
  117. package/dist/598.js +0 -1
  118. package/dist/598.js.map +0 -1
  119. package/dist/openmrs-esm-patient-registration-app.js.map +0 -1
  120. package/src/patient-registration/field/__mocks__/identifier-types.mock.ts +0 -76
  121. package/src/patient-registration/field/__mocks__/identifiers.mock.ts +0 -27
  122. package/src/patient-registration/field/address/tests/mocks.ts +0 -98
@@ -1,15 +1,21 @@
1
- import { FetchResponse, openmrsFetch, queueSynchronizationItem, Session } from '@openmrs/esm-framework';
1
+ import {
2
+ type FetchResponse,
3
+ openmrsFetch,
4
+ queueSynchronizationItem,
5
+ type Session,
6
+ restBaseUrl,
7
+ } from '@openmrs/esm-framework';
2
8
  import { patientRegistration } from '../constants';
3
9
  import {
4
- FormValues,
5
- AttributeValue,
6
- PatientUuidMapType,
7
- Patient,
8
- CapturePhotoProps,
9
- PatientIdentifier,
10
- PatientRegistration,
11
- RelationshipValue,
12
- Encounter,
10
+ type FormValues,
11
+ type AttributeValue,
12
+ type PatientUuidMapType,
13
+ type Patient,
14
+ type CapturePhotoProps,
15
+ type PatientIdentifier,
16
+ type PatientRegistration,
17
+ type RelationshipValue,
18
+ type Encounter,
13
19
  } from './patient-registration.types';
14
20
  import {
15
21
  addPatientIdentifier,
@@ -24,7 +30,7 @@ import {
24
30
  updatePatientIdentifier,
25
31
  saveEncounter,
26
32
  } from './patient-registration.resource';
27
- import { RegistrationConfig } from '../config-schema';
33
+ import { type RegistrationConfig } from '../config-schema';
28
34
 
29
35
  export type SavePatientForm = (
30
36
  isNewPatient: boolean,
@@ -128,7 +134,7 @@ export class FormManager {
128
134
  await savePatientPhoto(
129
135
  savePatientResponse.data.uuid,
130
136
  capturePhotoProps.imageData,
131
- '/ws/rest/v1/obs',
137
+ `${restBaseUrl}/obs`,
132
138
  capturePhotoProps.dateTime || new Date().toISOString(),
133
139
  config.concepts.patientPhotoUuid,
134
140
  );
@@ -252,7 +258,7 @@ export class FormManager {
252
258
  });
253
259
 
254
260
  /*
255
- If there was initially an identifier assigned to the patient,
261
+ If there was initially an identifier assigned to the patient,
256
262
  which is now not present in the patientIdentifiers(values.identifiers),
257
263
  this means that the identifier is meant to be deleted, hence we need
258
264
  to delete the respective identifiers.
@@ -302,7 +308,7 @@ export class FormManager {
302
308
  person: {
303
309
  uuid: values.patientUuid,
304
310
  names: FormManager.getNames(values, patientUuidMap),
305
- gender: values.gender.charAt(0),
311
+ gender: values.gender.charAt(0).toUpperCase(),
306
312
  birthdate,
307
313
  birthdateEstimated: values.birthdateEstimated,
308
314
  attributes: FormManager.getPatientAttributes(isNewPatient, values, patientUuidMap),
@@ -354,7 +360,7 @@ export class FormManager {
354
360
  .filter(([, value]) => !value)
355
361
  .forEach(async ([key]) => {
356
362
  const attributeUuid = patientUuidMap[`attribute.${key}`];
357
- await openmrsFetch(`/ws/rest/v1/person/${values.patientUuid}/attribute/${attributeUuid}`, {
363
+ await openmrsFetch(`${restBaseUrl}/person/${values.patientUuid}/attribute/${attributeUuid}`, {
358
364
  method: 'DELETE',
359
365
  }).catch((err) => {
360
366
  console.error(err);
@@ -383,21 +389,13 @@ export class FormManager {
383
389
  // provide a valid fhir.Patient object. The various patient chart modules should be able to handle
384
390
  // such missing props correctly (and should be updated if they don't).
385
391
 
386
- // Gender in the original object only uses a single letter. fhir.Patient expects a full string.
387
- const genderMap = {
388
- M: 'male',
389
- F: 'female',
390
- O: 'other',
391
- U: 'unknown',
392
- };
393
-
394
392
  // Mapping inspired by:
395
393
  // https://github.com/openmrs/openmrs-module-fhir/blob/669b3c52220bb9abc622f815f4dc0d8523687a57/api/src/main/java/org/openmrs/module/fhir/api/util/FHIRPatientUtil.java#L36
396
394
  // https://github.com/openmrs/openmrs-esm-patient-management/blob/94e6f637fb37cf4984163c355c5981ea6b8ca38c/packages/esm-patient-search-app/src/patient-search-result/patient-search-result.component.tsx#L21
397
395
  // Update as required.
398
396
  return {
399
397
  id: patient.uuid,
400
- gender: genderMap[patient.person?.gender],
398
+ gender: patient.person?.gender,
401
399
  birthDate: patient.person?.birthdate,
402
400
  deceasedBoolean: patient.person.dead,
403
401
  deceasedDateTime: patient.person.deathDate,
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { render, waitFor, screen } from '@testing-library/react';
2
+ import { render, screen } from '@testing-library/react';
3
3
  import userEvent from '@testing-library/user-event';
4
4
  import { Formik, Form } from 'formik';
5
5
  import { SelectInput } from './select-input.component';
@@ -28,7 +28,7 @@ describe('the select input', () => {
28
28
 
29
29
  await user.selectOptions(input, expected);
30
30
 
31
- await waitFor(() => expect(input.value).toEqual(expected));
31
+ await expect(input.value).toEqual(expected);
32
32
  });
33
33
 
34
34
  it('should show optional label if the input is not required', async () => {
@@ -40,7 +40,7 @@ describe('the select input', () => {
40
40
  </Formik>,
41
41
  );
42
42
 
43
- await waitFor(() => expect(screen.findByRole('combobox')));
43
+ await expect(screen.findByRole('combobox'));
44
44
 
45
45
  const selectInput = screen.getByRole('combobox', { name: 'Select (optional)' }) as HTMLSelectElement;
46
46
  expect(selectInput.labels).toHaveLength(1);
@@ -1,10 +1,11 @@
1
- import React, { HTMLAttributes, useEffect, useRef, useState } from 'react';
2
- import { Layer, Search, SearchProps } from '@carbon/react';
1
+ import React, { type HTMLAttributes, useEffect, useRef, useState } from 'react';
2
+ import { Layer, Search, type SearchProps } from '@carbon/react';
3
3
  import classNames from 'classnames';
4
4
  import styles from './autosuggest.scss';
5
5
 
6
6
  // FIXME Temporarily included types from Carbon
7
7
  type InputPropsBase = Omit<HTMLAttributes<HTMLInputElement>, 'onChange'>;
8
+
8
9
  interface SearchProps extends InputPropsBase {
9
10
  /**
10
11
  * Specify an optional value for the `autocomplete` property on the underlying
@@ -136,6 +137,7 @@ export const Autosuggest: React.FC<AutosuggestProps> = ({
136
137
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
137
138
  const query = e.target.value;
138
139
  onSuggestionSelected(name, undefined);
140
+
139
141
  if (query) {
140
142
  getSearchResults(query).then((suggestions) => {
141
143
  setSuggestions(suggestions);
@@ -173,9 +175,7 @@ export const Autosuggest: React.FC<AutosuggestProps> = ({
173
175
  {suggestions.length > 0 && (
174
176
  <ul className={styles.suggestions}>
175
177
  {suggestions.map((suggestion, index) => (
176
- <li //eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
177
- key={index}
178
- onClick={(e) => handleClick(index)}>
178
+ <li key={index} onClick={(e) => handleClick(index)}>
179
179
  {getDisplayValue(suggestion)}
180
180
  </li>
181
181
  ))}
@@ -1,9 +1,8 @@
1
1
  import React from 'react';
2
2
  import { Autosuggest } from './autosuggest.component';
3
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
4
3
  import { BrowserRouter } from 'react-router-dom';
5
- import '@testing-library/jest-dom/extend-expect';
6
- import '@testing-library/jest-dom';
4
+ import { render, screen } from '@testing-library/react';
5
+ import userEvent from '@testing-library/user-event';
7
6
 
8
7
  const mockPersons = [
9
8
  {
@@ -24,97 +23,110 @@ const mockPersons = [
24
23
  },
25
24
  ];
26
25
 
27
- const mockGetSearchResults = async (query: string) => {
26
+ const mockedGetSearchResults = async (query: string) => {
28
27
  return mockPersons.filter((person) => {
29
28
  return person.display.toUpperCase().includes(query.toUpperCase());
30
29
  });
31
30
  };
32
31
 
33
- const handleSuggestionSelected = jest.fn((field, value) => [field, value]);
34
-
35
- describe('autosuggest', () => {
36
- const setup = () => {
37
- render(
38
- <BrowserRouter>
39
- <Autosuggest
40
- labelText=""
41
- id="person"
42
- placeholder="Find Person"
43
- onSuggestionSelected={handleSuggestionSelected}
44
- getSearchResults={mockGetSearchResults}
45
- getDisplayValue={(item) => item.display}
46
- getFieldValue={(item) => item.uuid}
47
- />
48
- </BrowserRouter>,
49
- );
50
- };
51
-
52
- it('should render a search box', () => {
53
- setup();
32
+ const mockedHandleSuggestionSelected = jest.fn((field, value) => [field, value]);
33
+
34
+ describe('Autosuggest', () => {
35
+ afterEach(() => mockedHandleSuggestionSelected.mockClear());
36
+
37
+ it('renders a search box', () => {
38
+ renderAutosuggest();
39
+
54
40
  expect(screen.getByRole('searchbox')).toBeInTheDocument();
55
41
  expect(screen.queryByRole('list')).toBeNull();
56
42
  });
57
43
 
58
- it('should show the search results in a list', async () => {
59
- setup();
44
+ it('renders matching search results in a list when the user types a query', async () => {
45
+ const user = userEvent.setup();
46
+
47
+ renderAutosuggest();
48
+
60
49
  const searchbox = screen.getByRole('searchbox');
61
- fireEvent.change(searchbox, { target: { value: 'john' } });
62
- const list = await waitFor(() => screen.getByRole('list'));
50
+ await user.type(searchbox, 'john');
51
+
52
+ const list = screen.getByRole('list');
53
+
63
54
  expect(list).toBeInTheDocument();
64
55
  expect(list.children).toHaveLength(2);
56
+ expect(screen.getAllByRole('listitem')[0]).toHaveTextContent('John Doe');
57
+ expect(screen.getAllByRole('listitem')[1]).toHaveTextContent('John Smith');
65
58
  });
66
59
 
67
- it('should creates the li items whose inner text is gotten through getDisplayValue', async () => {
68
- setup();
69
- const searchbox = screen.getByRole('searchbox');
70
- fireEvent.change(searchbox, { target: { value: 'john' } });
71
- const list = await waitFor(() => screen.getAllByRole('listitem'));
72
- expect(list[0].textContent).toBe('John Doe');
73
- expect(list[1].textContent).toBe('John Smith');
74
- });
60
+ it('clears the list of suggestions when a suggestion is selected', async () => {
61
+ const user = userEvent.setup();
75
62
 
76
- it('should trigger the onSuggestionSelected with correct values when li is clicked', async () => {
77
- setup();
78
- const searchbox = screen.getByRole('searchbox');
79
- fireEvent.change(searchbox, { target: { value: 'john' } });
80
- const listitems = await waitFor(() => screen.getAllByRole('listitem'));
81
- fireEvent.click(listitems[0]);
82
- expect(handleSuggestionSelected).toHaveBeenNthCalledWith(4, 'person', 'randomuuid1');
83
- });
63
+ renderAutosuggest();
84
64
 
85
- it('should clear the suggestions when a suggestion is selected', async () => {
86
- setup();
87
65
  let list = screen.queryByRole('list');
88
66
  expect(list).toBeNull();
67
+
89
68
  const searchbox = screen.getByRole('searchbox');
90
- fireEvent.change(searchbox, { target: { value: 'john' } });
91
- list = await waitFor(() => screen.getByRole('list'));
69
+ await user.type(searchbox, 'john');
70
+
71
+ list = screen.getByRole('list');
92
72
  expect(list).toBeInTheDocument();
73
+
93
74
  const listitems = screen.getAllByRole('listitem');
94
- fireEvent.click(listitems[0]);
75
+ await user.click(listitems[0]);
76
+
77
+ expect(mockedHandleSuggestionSelected).toHaveBeenLastCalledWith('person', 'randomuuid1');
78
+
95
79
  list = screen.queryByRole('list');
96
80
  expect(list).toBeNull();
97
81
  });
98
82
 
99
- it('should change suggestions when a search input is changed', async () => {
100
- setup();
83
+ it('changes suggestions when a search input is changed', async () => {
84
+ const user = userEvent.setup();
85
+
86
+ renderAutosuggest();
87
+
101
88
  let list = screen.queryByRole('list');
102
89
  expect(list).toBeNull();
90
+
103
91
  const searchbox = screen.getByRole('searchbox');
104
- fireEvent.change(searchbox, { target: { value: 'john' } });
92
+ await user.type(searchbox, 'john');
93
+
105
94
  const suggestion = await screen.findByText('John Doe');
106
95
  expect(suggestion).toBeInTheDocument();
107
- fireEvent.change(searchbox, { target: { value: '' } });
96
+
97
+ await user.clear(searchbox);
98
+
108
99
  list = screen.queryByRole('list');
109
100
  expect(list).toBeNull();
110
101
  });
111
102
 
112
- it('should hide suggestions when clicked outside of component', async () => {
113
- setup();
103
+ it('hides the list of suggestions when the user clicks outside of the component', async () => {
104
+ const user = userEvent.setup();
105
+
106
+ renderAutosuggest();
107
+
114
108
  const input = screen.getByRole('searchbox');
115
- fireEvent.change(input, { target: { value: 'john' } });
109
+
110
+ await user.type(input, 'john');
116
111
  await screen.findByText('John Doe');
117
- fireEvent.mouseDown(document.body);
112
+ await user.click(document.body);
113
+
118
114
  expect(screen.queryByText('John Doe')).not.toBeInTheDocument();
119
115
  });
120
116
  });
117
+
118
+ function renderAutosuggest() {
119
+ render(
120
+ <BrowserRouter>
121
+ <Autosuggest
122
+ getSearchResults={mockedGetSearchResults}
123
+ getDisplayValue={(item) => item.display}
124
+ getFieldValue={(item) => item.uuid}
125
+ id="person"
126
+ labelText=""
127
+ onSuggestionSelected={mockedHandleSuggestionSelected}
128
+ placeholder="Find Person"
129
+ />
130
+ </BrowserRouter>,
131
+ );
132
+ }
@@ -7,7 +7,7 @@ import { ResourcesContext } from '../../../../offline.resources';
7
7
  import { showModal, useConfig, UserHasAccess } from '@openmrs/esm-framework';
8
8
  import { shouldBlockPatientIdentifierInOfflineMode } from './utils';
9
9
  import { deleteIdentifierType, setIdentifierSource } from '../../../field/id/id-field.component';
10
- import { PatientIdentifierValue } from '../../../patient-registration.types';
10
+ import { type PatientIdentifierValue } from '../../../patient-registration.types';
11
11
  import { PatientRegistrationContext } from '../../../patient-registration-context';
12
12
  import { Input } from '../../basic-input/input/input.component';
13
13
  import styles from '../../input.scss';
@@ -141,7 +141,7 @@ export const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentif
141
141
  <UserHasAccess privilege="Delete Patient Identifiers">
142
142
  <Button
143
143
  size="md"
144
- kind="danger--ghost"
144
+ kind="ghost"
145
145
  onClick={handleDelete}
146
146
  iconDescription={t('deleteIdentifierTooltip', 'Delete')}
147
147
  disabled={disabled}
@@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react';
3
3
  import { Formik, Form } from 'formik';
4
4
  import { IdentifierInput } from './identifier-input.component';
5
5
  import { initialFormValues } from '../../../patient-registration.component';
6
- import { PatientIdentifierType } from '../../../patient-registration-types';
6
+ import { type PatientIdentifierType } from '../../../patient-registration-types';
7
7
 
8
8
  jest.mock('@openmrs/esm-framework', () => {
9
9
  const originalModule = jest.requireActual('@openmrs/esm-framework');
@@ -52,10 +52,7 @@ describe.skip('identifier input', () => {
52
52
  </Formik>,
53
53
  );
54
54
  const identifierInput = screen.getByLabelText(identifierType.fieldName) as HTMLInputElement;
55
- let identifierSourceSelectInput = undefined;
56
- try {
57
- identifierSourceSelectInput = screen.getByLabelText('source-for-' + identifierType.fieldName) as HTMLInputElement;
58
- } catch (e) {}
55
+ let identifierSourceSelectInput = screen.getByLabelText('source-for-' + identifierType.fieldName);
59
56
  return {
60
57
  identifierInput,
61
58
  identifierSourceSelectInput,
@@ -1,4 +1,4 @@
1
- import { FetchedPatientIdentifierType, PatientIdentifierType } from '../../../patient-registration.types';
1
+ import { type FetchedPatientIdentifierType, type PatientIdentifierType } from '../../../patient-registration.types';
2
2
 
3
3
  export function shouldBlockPatientIdentifierInOfflineMode(identifierType: PatientIdentifierType) {
4
4
  // Patient Identifiers which are unique and can be manually entered are prohibited while offline because
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import classNames from 'classnames';
3
3
  import { v4 } from 'uuid';
4
- import { FormValues } from '../../patient-registration.types';
4
+ import { type FormValues } from '../../patient-registration.types';
5
5
  import styles from './../input.scss';
6
6
 
7
7
  interface DummyDataInputProps {
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
- import { render, fireEvent, screen, waitFor } from '@testing-library/react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
3
4
  import { DummyDataInput, dummyFormValues } from './dummy-data-input.component';
4
5
  import { initialFormValues } from '../../patient-registration.component';
5
- import { FormValues } from '../../patient-registration-types';
6
+ import { type FormValues } from '../../patient-registration-types';
6
7
 
7
8
  jest.mock('@openmrs/esm-framework', () => {
8
9
  const originalModule = jest.requireActual('@openmrs/esm-framework');
@@ -33,11 +34,10 @@ describe('dummy data input', () => {
33
34
  });
34
35
 
35
36
  it('can input data on button click', async () => {
37
+ const user = userEvent.setup();
36
38
  const input = await setupInput();
37
39
 
38
- fireEvent.click(input);
39
- waitFor(() => {
40
- expect(formValues).toEqual(dummyFormValues);
41
- });
40
+ await user.click(input);
41
+ expect(formValues).toEqual(dummyFormValues);
42
42
  });
43
43
  });
@@ -1,13 +1,12 @@
1
1
  import { useConfig } from '@openmrs/esm-framework';
2
- import { createContext, SetStateAction } from 'react';
3
- import { RegistrationConfig } from '../config-schema';
4
- import { FormValues, CapturePhotoProps } from './patient-registration.types';
2
+ import { createContext, type SetStateAction } from 'react';
3
+ import { type RegistrationConfig } from '../config-schema';
4
+ import { type FormValues, type CapturePhotoProps } from './patient-registration.types';
5
5
 
6
6
  export interface PatientRegistrationContextProps {
7
7
  identifierTypes: Array<any>;
8
8
  values: FormValues;
9
9
  validationSchema: any;
10
- setValidationSchema(value: any): void;
11
10
  inEditMode: boolean;
12
11
  setFieldValue(field: string, value: any, shouldValidate?: boolean): void;
13
12
  setCapturePhotoProps(value: SetStateAction<CapturePhotoProps>): void;
@@ -1,28 +1,29 @@
1
1
  import {
2
- FetchResponse,
3
- OpenmrsResource,
2
+ type FetchResponse,
3
+ type OpenmrsResource,
4
4
  getSynchronizationItems,
5
5
  openmrsFetch,
6
6
  useConfig,
7
7
  usePatient,
8
+ restBaseUrl,
8
9
  } from '@openmrs/esm-framework';
9
10
  import last from 'lodash-es/last';
10
11
  import camelCase from 'lodash-es/camelCase';
11
- import { Dispatch, useEffect, useMemo, useState } from 'react';
12
+ import { type Dispatch, useEffect, useMemo, useState } from 'react';
12
13
  import useSWR from 'swr';
13
14
  import { v4 } from 'uuid';
14
- import { RegistrationConfig } from '../config-schema';
15
+ import { type RegistrationConfig } from '../config-schema';
15
16
  import { patientRegistration } from '../constants';
16
17
  import { useConceptAnswers, useGlobalProperties } from '../patient-verification/patient-verification-hook';
17
18
  import {
18
- FormValues,
19
- PatientRegistration,
20
- PatientUuidMapType,
21
- PersonAttributeResponse,
22
- PatientIdentifierResponse,
23
- ObsResponse,
24
- ConceptAnswers,
25
- Encounter,
19
+ type FormValues,
20
+ type PatientRegistration,
21
+ type PatientUuidMapType,
22
+ type PersonAttributeResponse,
23
+ type PatientIdentifierResponse,
24
+ type Encounter,
25
+ type ConceptAnswers,
26
+ type ObsResponse,
26
27
  } from './patient-registration.types';
27
28
  import {
28
29
  getAddressFieldValuesFromFhirPatient,
@@ -35,7 +36,7 @@ import { useInitialPatientRelationships } from './section/patient-relationships/
35
36
  import dayjs from 'dayjs';
36
37
 
37
38
  export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch<FormValues>] {
38
- const { martialStatus, education, occupation, educationLoad, loadingStatus } = useConcepts();
39
+ const { martialStatus, education, occupation, educationLoad } = useConcepts();
39
40
  const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid);
40
41
  const { data: attributes, isLoading: isLoadingAttributes } = useInitialPersonAttributes(patientUuid);
41
42
  const { data: identifiers, isLoading: isLoadingIdentifiers } = useInitialPatientIdentifiers(patientUuid);
@@ -157,13 +158,13 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
157
158
  // Set Initial encounter
158
159
 
159
160
  useEffect(() => {
160
- if (!educationLoad || !loadingStatus) {
161
+ if (!educationLoad) {
161
162
  setInitialFormValues((initialFormValues) => ({
162
163
  ...initialFormValues,
163
164
  concepts: [...occupation, ...martialStatus, ...education],
164
165
  }));
165
166
  }
166
- }, [educationLoad, loadingStatus]);
167
+ }, [educationLoad]);
167
168
 
168
169
  return [initialFormValues, setInitialFormValues];
169
170
  }
@@ -232,7 +233,7 @@ export function useInitialPatientIdentifiers(patientUuid: string): {
232
233
 
233
234
  const { data, error, isLoading } = useSWR<FetchResponse<{ results: Array<PatientIdentifierResponse> }>, Error>(
234
235
  shouldFetch
235
- ? `/ws/rest/v1/patient/${patientUuid}/identifier?v=custom:(uuid,identifier,identifierType:(uuid,required,name),preferred)`
236
+ ? `${restBaseUrl}/patient/${patientUuid}/identifier?v=custom:(uuid,identifier,identifierType:(uuid,required,name),preferred)`
236
237
  : null,
237
238
  openmrsFetch,
238
239
  );
@@ -269,7 +270,7 @@ function useInitialEncounters(patientUuid: string, patientToEdit: fhir.Patient)
269
270
  const { registrationObs } = useConfig() as RegistrationConfig;
270
271
  const { data, error, isLoading } = useSWR<FetchResponse<{ results: Array<Encounter> }>>(
271
272
  patientToEdit && registrationObs.encounterTypeUuid
272
- ? `/ws/rest/v1/encounter?patient=${patientUuid}&v=custom:(encounterDatetime,obs:(concept:ref,value:ref))&encounterType=${registrationObs.encounterTypeUuid}`
273
+ ? `${restBaseUrl}/encounter?patient=${patientUuid}&v=custom:(encounterDatetime,obs:(concept:ref,value:ref))&encounterType=${registrationObs.encounterTypeUuid}`
273
274
  : null,
274
275
  openmrsFetch,
275
276
  );
@@ -287,7 +288,7 @@ function useInitialPersonAttributes(personUuid: string) {
287
288
  const shouldFetch = !!personUuid;
288
289
  const { data, error, isLoading } = useSWR<FetchResponse<{ results: Array<PersonAttributeResponse> }>, Error>(
289
290
  shouldFetch
290
- ? `/ws/rest/v1/person/${personUuid}/attribute?v=custom:(uuid,display,attributeType:(uuid,display,format),value)`
291
+ ? `${restBaseUrl}/person/${personUuid}/attribute?v=custom:(uuid,display,attributeType:(uuid,display,format),value)`
291
292
  : null,
292
293
  openmrsFetch,
293
294
  );
@@ -322,7 +323,7 @@ export function usePatientObs(patientUuid: string) {
322
323
  }
323
324
 
324
325
  function useConcepts() {
325
- const { data: martialStatus, isLoading: loadingStatus } = useConceptAnswers('1054AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
326
+ const config = useConfig<RegistrationConfig>();
326
327
  const { data: education, isLoading: educationLoad } = useConceptAnswers('1712AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
327
328
  const occupation: Array<ConceptAnswers> = [
328
329
  {
@@ -355,5 +356,9 @@ function useConcepts() {
355
356
  },
356
357
  ];
357
358
 
358
- return { martialStatus, education, occupation, loadingStatus, educationLoad };
359
+ const martialStatus: Array<ConceptAnswers> = config.fieldDefinitions
360
+ .find((fieldDefinition) => fieldDefinition.id === 'maritalStatus')
361
+ .customConceptAnswers.map((concept) => ({ uuid: concept.uuid, display: concept.label }));
362
+
363
+ return { martialStatus, education, occupation, educationLoad };
359
364
  }
@@ -1,11 +1,11 @@
1
1
  import * as Yup from 'yup';
2
2
  import {
3
- AddressValidationSchemaType,
4
- FormValues,
5
- PatientIdentifier,
6
- PatientUuidMapType,
7
- PatientIdentifierValue,
8
- Encounter,
3
+ type AddressValidationSchemaType,
4
+ type FormValues,
5
+ type PatientIdentifier,
6
+ type PatientUuidMapType,
7
+ type PatientIdentifierValue,
8
+ type Encounter,
9
9
  } from './patient-registration.types';
10
10
  import { parseDate } from '@openmrs/esm-framework';
11
11
  import camelCase from 'lodash-es/camelCase';
@@ -120,7 +120,7 @@ export function getFormValuesFromFhirPatient(patient: fhir.Patient) {
120
120
  result.additionalMiddleName = additionalPatientName?.given[1];
121
121
  result.additionalFamilyName = additionalPatientName?.family;
122
122
 
123
- result.gender = capitalize(patient.gender);
123
+ result.gender = patient.gender;
124
124
  result.birthdate = patient.birthDate ? parseDate(patient.birthDate) : undefined;
125
125
  result.telephoneNumber = patient.telecom ? patient.telecom[0].value : '';
126
126
 
@@ -4,18 +4,28 @@ import { Button, Link, InlineLoading } from '@carbon/react';
4
4
  import { XAxis, ShareKnowledge } from '@carbon/react/icons';
5
5
  import { useLocation, useParams } from 'react-router-dom';
6
6
  import { useTranslation } from 'react-i18next';
7
- import { Formik, Form, FormikHelpers } from 'formik';
7
+ import { Formik, Form, type FormikHelpers } from 'formik';
8
8
  import { createErrorHandler, showSnackbar, useConfig, interpolateUrl, usePatient } from '@openmrs/esm-framework';
9
- import { validationSchema as initialSchema } from './validation/patient-registration-validation';
10
- import { FormValues, CapturePhotoProps } from './patient-registration.types';
9
+ import { getValidationSchema } from './validation/patient-registration-validation';
10
+ import { type FormValues, type CapturePhotoProps } from './patient-registration.types';
11
11
  import { PatientRegistrationContext } from './patient-registration-context';
12
- import { SavePatientForm, SavePatientTransactionManager } from './form-manager';
12
+ import { type SavePatientForm, SavePatientTransactionManager } from './form-manager';
13
13
  import { usePatientPhoto } from './patient-registration.resource';
14
14
  import { DummyDataInput } from './input/dummy-data/dummy-data-input.component';
15
- import { cancelRegistration, filterUndefinedPatientIdenfier, scrollIntoView } from './patient-registration-utils';
16
- import { useInitialAddressFieldValues, useInitialFormValues, usePatientUuidMap } from './patient-registration-hooks';
15
+ import {
16
+ cancelRegistration,
17
+ filterUndefinedPatientIdenfier,
18
+ parseAddressTemplateXml,
19
+ scrollIntoView,
20
+ } from './patient-registration-utils';
21
+ import {
22
+ useInitialAddressFieldValues,
23
+ useInitialFormValues,
24
+ usePatientObs,
25
+ usePatientUuidMap,
26
+ } from './patient-registration-hooks';
17
27
  import { ResourcesContext } from '../offline.resources';
18
- import { builtInSections, RegistrationConfig, SectionDefinition } from '../config-schema';
28
+ import { builtInSections, type RegistrationConfig, type SectionDefinition } from '../config-schema';
19
29
  import { SectionWrapper } from './section/section-wrapper.component';
20
30
  import BeforeSavePrompt from './before-save-prompt';
21
31
  import styles from './patient-registration.scss';
@@ -34,7 +44,6 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
34
44
  const { search } = useLocation();
35
45
  const config = useConfig() as RegistrationConfig;
36
46
  const [target, setTarget] = useState<undefined | string>();
37
- const [validationSchema, setValidationSchema] = useState(initialSchema);
38
47
  const { patientUuid: uuidOfPatientToEdit } = useParams();
39
48
  const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(uuidOfPatientToEdit);
40
49
  const { t } = useTranslation();
@@ -48,6 +57,7 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
48
57
  const { data: photo } = usePatientPhoto(patientToEdit?.id);
49
58
  const savePatientTransactionManager = useRef(new SavePatientTransactionManager());
50
59
  const fieldDefinition = config?.fieldDefinitions?.filter((def) => def.type === 'address');
60
+ const validationSchema = getValidationSchema(config);
51
61
  const [enableClientRegistry, setEnableClientRegistry] = useState(
52
62
  inEditMode ? initialFormValues.identifiers['nationalUniquePatientIdentifier']?.identifierValue : false,
53
63
  );
@@ -139,6 +149,7 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
139
149
  title: t('incompleteForm', 'The following field has errors:'),
140
150
  kind: 'warning',
141
151
  isLowContrast: true,
152
+ timeoutInMs: 5000,
142
153
  });
143
154
  });
144
155
  }
@@ -211,7 +222,6 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
211
222
  value={{
212
223
  identifierTypes: identifierTypes,
213
224
  validationSchema,
214
- setValidationSchema,
215
225
  values: props.values,
216
226
  inEditMode,
217
227
  setFieldValue: props.setFieldValue,
@@ -221,7 +231,7 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
221
231
  initialFormValues: props.initialValues,
222
232
  setInitialFormValues,
223
233
  }}>
224
- <PatientVerification props={props} />
234
+ <PatientVerification props={props} setInitialFormValues={setInitialFormValues} />
225
235
  {sections.map((section, index) => (
226
236
  <SectionWrapper
227
237
  key={`registration-section-${section.id}`}