@ampath/esm-patient-registration-app 6.0.1-pre.96 → 9.2.0-next.12

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 (288) hide show
  1. package/dist/1119.js +1 -0
  2. package/dist/1197.js +1 -0
  3. package/dist/21.js +1 -0
  4. package/dist/21.js.map +1 -0
  5. package/dist/2146.js +1 -0
  6. package/dist/2372.js +1 -0
  7. package/dist/2372.js.map +1 -0
  8. package/dist/2470.js +1 -0
  9. package/dist/2470.js.map +1 -0
  10. package/dist/2690.js +1 -0
  11. package/dist/2913.js +2 -0
  12. package/dist/{913.js.LICENSE.txt → 2913.js.LICENSE.txt} +3 -23
  13. package/dist/2913.js.map +1 -0
  14. package/dist/3093.js +1 -0
  15. package/dist/3093.js.map +1 -0
  16. package/dist/3099.js +1 -0
  17. package/dist/3144.js +2 -0
  18. package/dist/3144.js.LICENSE.txt +19 -0
  19. package/dist/3144.js.map +1 -0
  20. package/dist/320.js +2 -0
  21. package/dist/{876.js.LICENSE.txt → 320.js.LICENSE.txt} +2 -3
  22. package/dist/320.js.map +1 -0
  23. package/dist/3464.js +1 -0
  24. package/dist/3464.js.map +1 -0
  25. package/dist/3474.js +2 -0
  26. package/dist/3474.js.LICENSE.txt +8 -0
  27. package/dist/3474.js.map +1 -0
  28. package/dist/3584.js +1 -0
  29. package/dist/4041.js +2 -0
  30. package/dist/4041.js.map +1 -0
  31. package/dist/4055.js +1 -0
  32. package/dist/4132.js +1 -0
  33. package/dist/4300.js +1 -0
  34. package/dist/4335.js +1 -0
  35. package/dist/4463.js +1 -0
  36. package/dist/4463.js.map +1 -0
  37. package/dist/4618.js +1 -0
  38. package/dist/4652.js +1 -0
  39. package/dist/4944.js +1 -0
  40. package/dist/5173.js +1 -0
  41. package/dist/5220.js +2 -0
  42. package/dist/5220.js.LICENSE.txt +29 -0
  43. package/dist/5220.js.map +1 -0
  44. package/dist/5241.js +1 -0
  45. package/dist/5442.js +1 -0
  46. package/dist/5661.js +1 -0
  47. package/dist/6022.js +1 -0
  48. package/dist/6078.js +2 -0
  49. package/dist/6078.js.LICENSE.txt +9 -0
  50. package/dist/6078.js.map +1 -0
  51. package/dist/627.js +1 -0
  52. package/dist/627.js.map +1 -0
  53. package/dist/6276.js +1 -0
  54. package/dist/6276.js.map +1 -0
  55. package/dist/6468.js +1 -0
  56. package/dist/6679.js +1 -0
  57. package/dist/6737.js +2 -0
  58. package/dist/6737.js.LICENSE.txt +9 -0
  59. package/dist/6737.js.map +1 -0
  60. package/dist/6840.js +1 -0
  61. package/dist/6859.js +1 -0
  62. package/dist/7092.js +1 -0
  63. package/dist/7092.js.map +1 -0
  64. package/dist/7097.js +1 -0
  65. package/dist/7159.js +1 -0
  66. package/dist/723.js +1 -0
  67. package/dist/7495.js +2 -0
  68. package/dist/7495.js.LICENSE.txt +9 -0
  69. package/dist/7495.js.map +1 -0
  70. package/dist/7617.js +1 -0
  71. package/dist/795.js +1 -0
  72. package/dist/8163.js +1 -0
  73. package/dist/8349.js +1 -0
  74. package/dist/8404.js +2 -0
  75. package/dist/{629.js.LICENSE.txt → 8404.js.LICENSE.txt} +9 -3
  76. package/dist/8404.js.map +1 -0
  77. package/dist/8434.js +1 -0
  78. package/dist/8434.js.map +1 -0
  79. package/dist/8618.js +1 -0
  80. package/dist/89.js +2 -0
  81. package/dist/89.js.LICENSE.txt +9 -0
  82. package/dist/89.js.map +1 -0
  83. package/dist/890.js +1 -0
  84. package/dist/9214.js +1 -0
  85. package/dist/9538.js +1 -0
  86. package/dist/9569.js +1 -0
  87. package/dist/986.js +1 -0
  88. package/dist/9876.js +1 -0
  89. package/dist/9876.js.map +1 -0
  90. package/dist/9879.js +1 -0
  91. package/dist/9895.js +1 -0
  92. package/dist/9900.js +1 -0
  93. package/dist/9913.js +1 -0
  94. package/dist/main.js +1 -1
  95. package/dist/main.js.LICENSE.txt +36 -1
  96. package/dist/main.js.map +1 -1
  97. package/dist/openmrs-esm-patient-registration-app.js +1 -0
  98. package/dist/openmrs-esm-patient-registration-app.js.buildmanifest.json +1576 -0
  99. package/dist/openmrs-esm-patient-registration-app.js.map +1 -0
  100. package/dist/routes.json +1 -1
  101. package/package.json +16 -15
  102. package/src/{add-patient-link.tsx → add-patient-link.extension.tsx} +4 -2
  103. package/src/add-patient-link.test.tsx +6 -10
  104. package/src/config-schema.ts +109 -55
  105. package/src/constants.ts +1 -1
  106. package/src/declarations.d.ts +5 -4
  107. package/src/index.ts +10 -29
  108. package/src/nav-link.test.tsx +3 -3
  109. package/src/offline.resources.ts +26 -18
  110. package/src/patient-photo.extension.tsx +3 -1
  111. package/src/patient-registration/field/address/address-field.component.tsx +58 -37
  112. package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +16 -18
  113. package/src/patient-registration/field/address/address-hierarchy.resource.tsx +3 -3
  114. package/src/patient-registration/field/address/address-hierarchy.test.tsx +290 -0
  115. package/src/patient-registration/field/address/address-search.component.tsx +7 -5
  116. package/src/patient-registration/field/address/address-search.scss +5 -5
  117. package/src/patient-registration/field/address/address-search.test.tsx +140 -0
  118. package/src/patient-registration/field/cause-of-death/cause-of-death.component.tsx +98 -0
  119. package/src/patient-registration/field/custom-field.component.tsx +3 -9
  120. package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +84 -0
  121. package/src/patient-registration/field/dob/dob.component.tsx +55 -50
  122. package/src/patient-registration/field/dob/dob.test.tsx +90 -0
  123. package/src/patient-registration/field/field.component.tsx +12 -6
  124. package/src/patient-registration/field/field.resource.ts +11 -4
  125. package/src/patient-registration/field/field.scss +69 -25
  126. package/src/patient-registration/field/field.test.tsx +329 -0
  127. package/src/patient-registration/field/gender/gender-field.component.tsx +14 -9
  128. package/src/patient-registration/field/gender/gender-field.test.tsx +73 -33
  129. package/src/patient-registration/field/id/id-field.component.tsx +24 -23
  130. package/src/patient-registration/field/id/id-field.test.tsx +147 -0
  131. package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +12 -10
  132. package/src/patient-registration/field/id/identifier-selection.scss +12 -8
  133. package/src/patient-registration/field/name/name-field.component.tsx +10 -5
  134. package/src/patient-registration/field/obs/obs-field.component.tsx +59 -2
  135. package/src/patient-registration/field/obs/obs-field.test.tsx +133 -39
  136. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +3 -3
  137. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +141 -0
  138. package/src/patient-registration/field/person-attributes/location-person-attribute-field.component.tsx +105 -0
  139. package/src/patient-registration/field/person-attributes/location-person-attribute-field.resource.tsx +48 -0
  140. package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +19 -22
  141. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +193 -0
  142. package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +90 -0
  143. package/src/patient-registration/form-manager.test.ts +91 -0
  144. package/src/patient-registration/form-manager.ts +49 -23
  145. package/src/patient-registration/input/basic-input/input/input.component.tsx +6 -2
  146. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +49 -0
  147. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.scss +5 -5
  148. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +164 -0
  149. package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +73 -36
  150. package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +335 -0
  151. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +3 -0
  152. package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +2 -11
  153. package/src/patient-registration/input/input.scss +17 -13
  154. package/src/patient-registration/patient-registration-context.ts +22 -11
  155. package/src/patient-registration/patient-registration-hooks.ts +158 -193
  156. package/src/patient-registration/patient-registration-utils.test.ts +33 -0
  157. package/src/patient-registration/patient-registration-utils.ts +11 -13
  158. package/src/patient-registration/patient-registration.component.tsx +87 -103
  159. package/src/patient-registration/{patient-registration.resource.testt.tsx → patient-registration.resource.test.tsx} +0 -4
  160. package/src/patient-registration/patient-registration.resource.ts +27 -3
  161. package/src/patient-registration/patient-registration.scss +27 -38
  162. package/src/patient-registration/patient-registration.test.tsx +579 -0
  163. package/src/patient-registration/patient-registration.types.ts +23 -25
  164. package/src/patient-registration/section/death-info/death-info-section.component.tsx +22 -17
  165. package/src/patient-registration/section/death-info/death-info-section.test.tsx +47 -0
  166. package/src/patient-registration/section/demographics/demographics-section.component.tsx +5 -5
  167. package/src/patient-registration/section/demographics/demographics-section.test.tsx +98 -0
  168. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +8 -7
  169. package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +113 -0
  170. package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +28 -28
  171. package/src/patient-registration/section/patient-relationships/relationships.scss +4 -4
  172. package/src/patient-registration/section/section-wrapper.component.tsx +1 -1
  173. package/src/patient-registration/section/section.component.tsx +1 -1
  174. package/src/patient-registration/section/section.scss +21 -1
  175. package/src/patient-registration/ui-components/overlay/overlay.scss +8 -8
  176. package/src/patient-registration/validation/{patient-registration-validation.test.tsx → patient-registration-validation.test.ts} +71 -23
  177. package/src/patient-registration/validation/patient-registration-validation.ts +123 -0
  178. package/src/resources-context.ts +14 -0
  179. package/src/root.component.tsx +3 -3
  180. package/src/routes.json +10 -24
  181. package/src/widgets/cancel-patient-edit.modal.tsx +33 -0
  182. package/src/widgets/cancel-patient-edit.test.tsx +22 -0
  183. package/src/widgets/delete-identifier-confirmation.modal.tsx +48 -0
  184. package/src/widgets/{delete-identifier-confirmation-modal.testt.tsx → delete-identifier-confirmation.test.tsx} +5 -7
  185. package/src/widgets/edit-patient-details-button.component.tsx +0 -1
  186. package/src/widgets/edit-patient-details-button.test.tsx +35 -0
  187. package/translations/am.json +43 -35
  188. package/translations/ar.json +41 -33
  189. package/translations/ar_SY.json +119 -0
  190. package/translations/bn.json +119 -0
  191. package/translations/de.json +119 -0
  192. package/translations/en.json +44 -42
  193. package/translations/en_US.json +119 -0
  194. package/translations/es.json +69 -57
  195. package/translations/es_MX.json +119 -0
  196. package/translations/fr.json +74 -58
  197. package/translations/he.json +44 -40
  198. package/translations/hi.json +119 -0
  199. package/translations/hi_IN.json +119 -0
  200. package/translations/id.json +119 -0
  201. package/translations/it.json +119 -0
  202. package/translations/ka.json +119 -0
  203. package/translations/km.json +44 -40
  204. package/translations/ku.json +119 -0
  205. package/translations/ky.json +119 -0
  206. package/translations/lg.json +119 -0
  207. package/translations/ne.json +119 -0
  208. package/translations/pl.json +119 -0
  209. package/translations/pt.json +119 -0
  210. package/translations/pt_BR.json +119 -0
  211. package/translations/qu.json +119 -0
  212. package/translations/ro_RO.json +119 -0
  213. package/translations/ru_RU.json +119 -0
  214. package/translations/si.json +119 -0
  215. package/translations/sw.json +119 -0
  216. package/translations/sw_KE.json +119 -0
  217. package/translations/tr.json +119 -0
  218. package/translations/tr_TR.json +119 -0
  219. package/translations/uk.json +119 -0
  220. package/translations/uz.json +119 -0
  221. package/translations/uz@Latn.json +119 -0
  222. package/translations/uz_UZ.json +119 -0
  223. package/translations/vi.json +119 -0
  224. package/translations/zh.json +45 -23
  225. package/translations/zh_CN.json +39 -17
  226. package/.turbo/turbo-build.log +0 -40
  227. package/dist/132.js +0 -1
  228. package/dist/197.js +0 -1
  229. package/dist/236.js +0 -1
  230. package/dist/236.js.map +0 -1
  231. package/dist/300.js +0 -1
  232. package/dist/335.js +0 -1
  233. package/dist/372.js +0 -1
  234. package/dist/372.js.map +0 -1
  235. package/dist/41.js +0 -2
  236. package/dist/41.js.map +0 -1
  237. package/dist/449.js +0 -1
  238. package/dist/449.js.map +0 -1
  239. package/dist/464.js +0 -1
  240. package/dist/464.js.map +0 -1
  241. package/dist/495.js +0 -1
  242. package/dist/495.js.map +0 -1
  243. package/dist/55.js +0 -1
  244. package/dist/56.js +0 -1
  245. package/dist/56.js.map +0 -1
  246. package/dist/621.js +0 -1
  247. package/dist/621.js.map +0 -1
  248. package/dist/629.js +0 -2
  249. package/dist/629.js.map +0 -1
  250. package/dist/652.js +0 -1
  251. package/dist/661.js +0 -1
  252. package/dist/757.js +0 -1
  253. package/dist/757.js.map +0 -1
  254. package/dist/828.js +0 -1
  255. package/dist/828.js.map +0 -1
  256. package/dist/830.js +0 -1
  257. package/dist/830.js.map +0 -1
  258. package/dist/831.js +0 -2
  259. package/dist/831.js.LICENSE.txt +0 -3
  260. package/dist/831.js.map +0 -1
  261. package/dist/876.js +0 -2
  262. package/dist/876.js.map +0 -1
  263. package/dist/879.js +0 -1
  264. package/dist/913.js +0 -2
  265. package/dist/913.js.map +0 -1
  266. package/dist/927.js +0 -1
  267. package/dist/927.js.map +0 -1
  268. package/dist/99.js +0 -1
  269. package/dist/ampath-esm-patient-registration-app.js +0 -1
  270. package/dist/ampath-esm-patient-registration-app.js.buildmanifest.json +0 -694
  271. package/dist/ampath-esm-patient-registration-app.js.map +0 -1
  272. package/src/patient-registration/date-util.ts +0 -52
  273. package/src/patient-registration/field/person-attributes/custom-person-attribute-field.component.tsx +0 -56
  274. package/src/patient-registration/validation/patient-registration-validation.tsx +0 -60
  275. package/src/patient-verification/assets/counties.json +0 -236
  276. package/src/patient-verification/assets/verification-assets.ts +0 -11
  277. package/src/patient-verification/patient-verification-hook.tsx +0 -176
  278. package/src/patient-verification/patient-verification-utils.ts +0 -179
  279. package/src/patient-verification/patient-verification.component.tsx +0 -124
  280. package/src/patient-verification/patient-verification.scss +0 -25
  281. package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +0 -72
  282. package/src/patient-verification/verification-modal/empty-prompt.component.tsx +0 -35
  283. package/src/patient-verification/verification-types.ts +0 -50
  284. package/src/widgets/cancel-patient-edit.component.tsx +0 -37
  285. package/src/widgets/delete-identifier-confirmation-modal.tsx +0 -41
  286. package/src/widgets/delete-identifier-modal.scss +0 -34
  287. /package/dist/{41.js.LICENSE.txt → 4041.js.LICENSE.txt} +0 -0
  288. /package/src/patient-registration/input/custom-input/identifier/{utils.testt.ts → utils.test.ts} +0 -0
@@ -1,35 +1,35 @@
1
1
  import React from 'react';
2
2
  import userEvent from '@testing-library/user-event';
3
3
  import { Formik, Form } from 'formik';
4
- import { render } from '@testing-library/react';
4
+ import { screen } from '@testing-library/react';
5
+ import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
6
+ import { renderWithContext } from 'tools';
7
+ import { type RegistrationConfig, esmPatientRegistrationSchema } from '../../../config-schema';
8
+ import { type FormValues } from '../../patient-registration.types';
9
+ import {
10
+ type PatientRegistrationContextProps,
11
+ PatientRegistrationContextProvider,
12
+ } from '../../patient-registration-context';
5
13
  import { GenderField } from './gender-field.component';
6
14
 
7
- jest.mock('@openmrs/esm-framework', () => ({
8
- ...(jest.requireActual('@openmrs/esm-framework') as any),
9
- useConfig: jest.fn(() => ({
10
- fieldConfigurations: {
11
- gender: [
12
- {
13
- value: 'male',
14
- label: 'Male',
15
- },
16
- ],
17
- name: {
18
- displayMiddleName: false,
19
- unidentifiedPatient: false,
20
- defaultUnknownGivenName: '',
21
- defaultUnknownFamilyName: '',
22
- },
23
- },
24
- })),
25
- }));
15
+ const mockUseConfig = jest.mocked(useConfig<RegistrationConfig>);
26
16
 
27
- jest.mock('react', () => ({
28
- ...(jest.requireActual('react') as any),
29
- useContext: jest.fn(() => ({
30
- setFieldValue: jest.fn(),
31
- })),
32
- }));
17
+ const mockContextValues: PatientRegistrationContextProps = {
18
+ currentPhoto: null,
19
+ identifierTypes: [],
20
+ inEditMode: false,
21
+ initialFormValues: {
22
+ gender: 'male',
23
+ } as FormValues,
24
+ isOffline: false,
25
+ setCapturePhotoProps: jest.fn(),
26
+ setFieldTouched: jest.fn(),
27
+ setFieldValue: jest.fn(),
28
+ validationSchema: esmPatientRegistrationSchema,
29
+ values: {
30
+ gender: 'male',
31
+ } as FormValues,
32
+ };
33
33
 
34
34
  jest.mock('formik', () => ({
35
35
  ...(jest.requireActual('formik') as any),
@@ -37,23 +37,63 @@ jest.mock('formik', () => ({
37
37
  }));
38
38
 
39
39
  describe('GenderField', () => {
40
- const renderComponent = () => {
41
- return render(
40
+ beforeEach(() => {
41
+ mockUseConfig.mockReturnValue({
42
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
43
+ fieldConfigurations: {
44
+ gender: [
45
+ {
46
+ value: 'male',
47
+ label: 'Male',
48
+ },
49
+ {
50
+ value: 'female',
51
+ label: 'Female',
52
+ },
53
+ ],
54
+ name: {
55
+ displayMiddleName: false,
56
+ allowUnidentifiedPatients: false,
57
+ defaultUnknownGivenName: '',
58
+ defaultUnknownFamilyName: '',
59
+ displayCapturePhoto: false,
60
+ displayReverseFieldOrder: false,
61
+ },
62
+ } as RegistrationConfig['fieldConfigurations'],
63
+ });
64
+ });
65
+
66
+ it('has a label', () => {
67
+ renderWithContext(
42
68
  <Formik initialValues={{}} onSubmit={null}>
43
69
  <Form>
44
70
  <GenderField />
45
71
  </Form>
46
72
  </Formik>,
73
+ PatientRegistrationContextProvider,
74
+ mockContextValues,
47
75
  );
48
- };
49
76
 
50
- it('has a label', () => {
51
- expect(renderComponent().getAllByText('Sex')).toBeTruthy();
77
+ expect(screen.getByRole('heading', { name: /sex/i })).toBeInTheDocument();
78
+ expect(screen.getByLabelText(/^male/i)).toBeInTheDocument();
79
+ expect(screen.getByLabelText(/female/i)).toBeInTheDocument();
52
80
  });
53
81
 
54
82
  it('checks an option', async () => {
55
83
  const user = userEvent.setup();
56
- const component = renderComponent();
57
- expect(component.getByLabelText('Male').getAttribute('value')).toBe('male');
84
+
85
+ renderWithContext(
86
+ <Formik initialValues={{}} onSubmit={null}>
87
+ <Form>
88
+ <GenderField />
89
+ </Form>
90
+ </Formik>,
91
+ PatientRegistrationContextProvider,
92
+ mockContextValues,
93
+ );
94
+
95
+ await user.click(screen.getByText(/female/i));
96
+ expect(screen.getByLabelText(/female/i)).toBeChecked();
97
+ expect(screen.getByLabelText(/^male/i)).not.toBeChecked();
58
98
  });
59
99
  });
@@ -1,18 +1,18 @@
1
- import React, { useCallback, useContext, useEffect, useState } from 'react';
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { Button, SkeletonText } from '@carbon/react';
4
4
  import { ArrowRight } from '@carbon/react/icons';
5
5
  import { useLayoutType, useConfig, isDesktop, UserHasAccess } from '@openmrs/esm-framework';
6
- import IdentifierSelectionOverlay from './identifier-selection-overlay.component';
7
- import { IdentifierInput } from '../../input/custom-input/identifier/identifier-input.component';
8
- import { PatientRegistrationContext } from '../../patient-registration-context';
9
- import {
10
- type FormValues,
11
- type IdentifierSource,
12
- type PatientIdentifierType,
13
- type PatientIdentifierValue,
6
+ import { usePatientRegistrationContext } from '../../patient-registration-context';
7
+ import { useResourcesContext } from '../../../resources-context';
8
+ import type {
9
+ FormValues,
10
+ IdentifierSource,
11
+ PatientIdentifierType,
12
+ PatientIdentifierValue,
14
13
  } from '../../patient-registration.types';
15
- import { ResourcesContext } from '../../../offline.resources';
14
+ import IdentifierInput from '../../input/custom-input/identifier/identifier-input.component';
15
+ import IdentifierSelectionOverlay from './identifier-selection-overlay.component';
16
16
  import styles from '../field.scss';
17
17
 
18
18
  export function setIdentifierSource(
@@ -25,14 +25,16 @@ export function setIdentifierSource(
25
25
  selectedSource: IdentifierSource;
26
26
  } {
27
27
  const autoGeneration = identifierSource?.autoGenerationOption?.automaticGenerationEnabled;
28
+ const manualEntryEnabled = identifierSource?.autoGenerationOption?.manualEntryEnabled;
28
29
  return {
29
30
  selectedSource: identifierSource,
30
31
  autoGeneration,
31
- identifierValue: autoGeneration
32
- ? 'auto-generated'
33
- : identifierValue !== 'auto-generated'
34
- ? identifierValue
35
- : initialValue,
32
+ identifierValue:
33
+ (autoGeneration && !manualEntryEnabled) || (!autoGeneration && !manualEntryEnabled)
34
+ ? 'auto-generated'
35
+ : identifierValue !== 'auto-generated'
36
+ ? identifierValue
37
+ : initialValue,
36
38
  };
37
39
  }
38
40
 
@@ -57,9 +59,9 @@ export function deleteIdentifierType(identifiers: FormValues['identifiers'], ide
57
59
  }
58
60
 
59
61
  export const Identifiers: React.FC = () => {
60
- const { identifierTypes } = useContext(ResourcesContext);
61
- const isLoading = !identifierTypes;
62
- const { values, setFieldValue, initialFormValues, isOffline } = useContext(PatientRegistrationContext);
62
+ const { identifierTypes } = useResourcesContext();
63
+ const isLoading = !identifierTypes?.length;
64
+ const { values, setFieldValue, initialFormValues, isOffline } = usePatientRegistrationContext();
63
65
  const { t } = useTranslation();
64
66
  const layout = useLayoutType();
65
67
  const [showIdentifierOverlay, setShowIdentifierOverlay] = useState(false);
@@ -67,7 +69,6 @@ export const Identifiers: React.FC = () => {
67
69
  const { defaultPatientIdentifierTypes } = config;
68
70
 
69
71
  useEffect(() => {
70
- // Initialization
71
72
  if (identifierTypes) {
72
73
  const identifiers = {};
73
74
  identifierTypes
@@ -108,11 +109,11 @@ export const Identifiers: React.FC = () => {
108
109
 
109
110
  if (isLoading && !isOffline) {
110
111
  return (
111
- <div data-testid="loading-skeleton" className={styles.halfWidthInDesktopView}>
112
+ <div className={styles.halfWidthInDesktopView}>
112
113
  <div className={styles.identifierLabelText}>
113
114
  <h4 className={styles.productiveHeading02Light}>{t('idFieldLabelText', 'Identifiers')}</h4>
114
115
  </div>
115
- <SkeletonText />
116
+ <SkeletonText role="progressbar" />
116
117
  </div>
117
118
  );
118
119
  }
@@ -124,10 +125,10 @@ export const Identifiers: React.FC = () => {
124
125
  <h4 className={styles.productiveHeading02Light}>{t('idFieldLabelText', 'Identifiers')}</h4>
125
126
  <Button
126
127
  kind="ghost"
127
- className={styles.setIDNumberButton}
128
+ className={styles.configureIdentifiersButton}
128
129
  onClick={() => setShowIdentifierOverlay(true)}
129
130
  size={isDesktop(layout) ? 'sm' : 'md'}>
130
- {t('configure', 'Configure')} <ArrowRight size={16} />
131
+ {t('configure', 'Configure')} <ArrowRight className={styles.arrowRightIcon} size={16} />
131
132
  </Button>
132
133
  </div>
133
134
  </UserHasAccess>
@@ -0,0 +1,147 @@
1
+ import React from 'react';
2
+ import { Form, Formik } from 'formik';
3
+ import { render, screen } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+ import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
6
+ import { type AddressTemplate, type IdentifierSource } from '../../patient-registration.types';
7
+ import { mockIdentifierTypes, mockOpenmrsId, mockPatient, mockSession } from '__mocks__';
8
+ import { renderWithContext } from 'tools';
9
+ import { esmPatientRegistrationSchema, type RegistrationConfig } from '../../../config-schema';
10
+ import { type Resources } from '../../../offline.resources';
11
+ import {
12
+ PatientRegistrationContextProvider,
13
+ type PatientRegistrationContextProps,
14
+ } from '../../patient-registration-context';
15
+ import { Identifiers, setIdentifierSource } from './id-field.component';
16
+ import { ResourcesContextProvider } from '../../../resources-context';
17
+
18
+ const mockUseConfig = jest.mocked(useConfig<RegistrationConfig>);
19
+
20
+ const mockResourcesContextValue = {
21
+ addressTemplate: null as unknown as AddressTemplate,
22
+ currentSession: mockSession.data,
23
+ identifierTypes: [],
24
+ relationshipTypes: [],
25
+ } as Resources;
26
+
27
+ const mockInitialFormValues = {
28
+ additionalFamilyName: '',
29
+ additionalGivenName: '',
30
+ additionalMiddleName: '',
31
+ addNameInLocalLanguage: false,
32
+ address: {},
33
+ birthdate: null,
34
+ birthdateEstimated: false,
35
+ deathCause: '',
36
+ deathDate: '',
37
+ familyName: 'Doe',
38
+ gender: 'male',
39
+ givenName: 'John',
40
+ identifiers: mockOpenmrsId,
41
+ isDead: false,
42
+ middleName: 'Test',
43
+ monthsEstimated: 0,
44
+ patientUuid: mockPatient.uuid,
45
+ relationships: [],
46
+ telephoneNumber: '',
47
+ yearsEstimated: 0,
48
+ };
49
+
50
+ const mockContextValues: PatientRegistrationContextProps = {
51
+ currentPhoto: null,
52
+ inEditMode: false,
53
+ identifierTypes: [],
54
+ initialFormValues: mockInitialFormValues,
55
+ isOffline: false,
56
+ setCapturePhotoProps: jest.fn(),
57
+ setFieldValue: jest.fn(),
58
+ setInitialFormValues: jest.fn(),
59
+ validationSchema: null,
60
+ values: mockInitialFormValues,
61
+ } as unknown as PatientRegistrationContextProps;
62
+
63
+ describe('Identifiers', () => {
64
+ beforeEach(() => {
65
+ mockUseConfig.mockReturnValue({
66
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
67
+ defaultPatientIdentifierTypes: ['OpenMRS ID'],
68
+ });
69
+ });
70
+
71
+ it('should render loading skeleton when identifier types are loading', () => {
72
+ renderWithContext(
73
+ <Formik initialValues={{}} onSubmit={null}>
74
+ <Form>
75
+ <PatientRegistrationContextProvider value={mockContextValues}>
76
+ <Identifiers />
77
+ </PatientRegistrationContextProvider>
78
+ </Form>
79
+ </Formik>,
80
+ ResourcesContextProvider,
81
+ mockResourcesContextValue,
82
+ );
83
+
84
+ expect(screen.getByRole('progressbar')).toBeInTheDocument();
85
+ });
86
+
87
+ it('should render identifier inputs when identifier types are loaded', () => {
88
+ mockResourcesContextValue.identifierTypes = mockIdentifierTypes;
89
+
90
+ renderWithContext(
91
+ <Formik initialValues={{}} onSubmit={null}>
92
+ <Form>
93
+ <PatientRegistrationContextProvider value={mockContextValues}>
94
+ <Identifiers />
95
+ </PatientRegistrationContextProvider>
96
+ </Form>
97
+ </Formik>,
98
+ ResourcesContextProvider,
99
+ mockResourcesContextValue,
100
+ );
101
+
102
+ expect(screen.getByText('Identifiers')).toBeInTheDocument();
103
+ const configureButton = screen.getByRole('button', { name: 'Configure' });
104
+ expect(configureButton).toBeInTheDocument();
105
+ expect(configureButton).toBeEnabled();
106
+ });
107
+
108
+ it('should open identifier selection overlay when "Configure" button is clicked', async () => {
109
+ const user = userEvent.setup();
110
+ mockResourcesContextValue.identifierTypes = mockIdentifierTypes;
111
+
112
+ renderWithContext(
113
+ <Formik initialValues={{}} onSubmit={null}>
114
+ <Form>
115
+ <PatientRegistrationContextProvider value={mockContextValues}>
116
+ <Identifiers />
117
+ </PatientRegistrationContextProvider>
118
+ </Form>
119
+ </Formik>,
120
+ ResourcesContextProvider,
121
+ mockResourcesContextValue,
122
+ );
123
+
124
+ const configureButton = screen.getByRole('button', { name: 'Configure' });
125
+ await user.click(configureButton);
126
+
127
+ expect(screen.getByRole('button', { name: 'Close overlay' })).toBeInTheDocument();
128
+ });
129
+ });
130
+
131
+ describe('setIdentifierSource', () => {
132
+ describe('auto-generation', () => {
133
+ it('should return auto-generated as the identifier value', () => {
134
+ const identifierSource = { autoGenerationOption: { automaticGenerationEnabled: true } } as IdentifierSource;
135
+ const { identifierValue } = setIdentifierSource(identifierSource, '', '');
136
+ expect(identifierValue).toBe('auto-generated');
137
+ });
138
+
139
+ it('should return the identifier value when manual entry enabled', () => {
140
+ const identifierSource = {
141
+ autoGenerationOption: { automaticGenerationEnabled: true, manualEntryEnabled: true },
142
+ } as IdentifierSource;
143
+ const { identifierValue } = setIdentifierSource(identifierSource, '10001V', '');
144
+ expect(identifierValue).toBe('10001V');
145
+ });
146
+ });
147
+ });
@@ -1,16 +1,16 @@
1
- import React, { useMemo, useCallback, useEffect, useState, useContext } from 'react';
1
+ import React, { useMemo, useCallback, useEffect, useState } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { Button, ButtonSet, Checkbox, Search, RadioButtonGroup, RadioButton } from '@carbon/react';
4
4
  import { isDesktop, useConfig, useLayoutType } from '@openmrs/esm-framework';
5
5
  import { type FormValues, type PatientIdentifierType, PatientIdentifierValue } from '../../patient-registration.types';
6
- import Overlay from '../../ui-components/overlay/overlay.component';
7
- import { ResourcesContext } from '../../../offline.resources';
8
- import { PatientRegistrationContext } from '../../patient-registration-context';
6
+ import { usePatientRegistrationContext } from '../../patient-registration-context';
9
7
  import {
10
8
  isUniqueIdentifierTypeForOffline,
11
9
  shouldBlockPatientIdentifierInOfflineMode,
12
10
  } from '../../input/custom-input/identifier/utils';
13
11
  import { initializeIdentifier, setIdentifierSource } from './id-field.component';
12
+ import { useResourcesContext } from '../../../resources-context';
13
+ import Overlay from '../../ui-components/overlay/overlay.component';
14
14
  import styles from './identifier-selection.scss';
15
15
 
16
16
  interface PatientIdentifierOverlayProps {
@@ -20,12 +20,13 @@ interface PatientIdentifierOverlayProps {
20
20
 
21
21
  const PatientIdentifierOverlay: React.FC<PatientIdentifierOverlayProps> = ({ closeOverlay, setFieldValue }) => {
22
22
  const layout = useLayoutType();
23
- const { identifierTypes } = useContext(ResourcesContext);
24
- const { isOffline, values, initialFormValues } = useContext(PatientRegistrationContext);
23
+ const { identifierTypes } = useResourcesContext();
24
+ const { isOffline, values, initialFormValues } = usePatientRegistrationContext();
25
25
  const [unsavedIdentifierTypes, setUnsavedIdentifierTypes] = useState<FormValues['identifiers']>(values.identifiers);
26
- const [searchString, setSearchString] = useState<string>('');
26
+ const [searchString, setSearchString] = useState('');
27
27
  const { t } = useTranslation();
28
28
  const { defaultPatientIdentifierTypes } = useConfig();
29
+
29
30
  const defaultPatientIdentifierTypesMap = useMemo(() => {
30
31
  const map = {};
31
32
  defaultPatientIdentifierTypes?.forEach((typeUuid) => {
@@ -69,7 +70,7 @@ const PatientIdentifierOverlay: React.FC<PatientIdentifierOverlayProps> = ({ clo
69
70
  [initialFormValues.identifiers, values.identifiers],
70
71
  );
71
72
 
72
- const handleSelectingIdentifierSource = (identifierType: PatientIdentifierType, sourceUuid) =>
73
+ const handleSelectingIdentifierSource = (identifierType: PatientIdentifierType, sourceUuid) => {
73
74
  setUnsavedIdentifierTypes((unsavedIdentifierTypes) => ({
74
75
  ...unsavedIdentifierTypes,
75
76
  [identifierType.fieldName]: {
@@ -81,6 +82,7 @@ const PatientIdentifierOverlay: React.FC<PatientIdentifierOverlayProps> = ({ clo
81
82
  ),
82
83
  },
83
84
  }));
85
+ };
84
86
 
85
87
  const identifierTypeFields = useMemo(
86
88
  () =>
@@ -107,11 +109,11 @@ const PatientIdentifierOverlay: React.FC<PatientIdentifierOverlayProps> = ({ clo
107
109
  />
108
110
  {patientIdentifier &&
109
111
  identifierType?.identifierSources?.length > 0 &&
110
- /*
112
+ /*
111
113
  This check are for the cases when there's an initialValue identifier is assigned
112
114
  to the patient
113
115
  The corresponding flow is like:
114
- 1. If there's no change to the actual initial identifier, then the source remains null,
116
+ 1. If there's no change to the actual initial identifier, then the source remains null,
115
117
  hence the list of the identifier sources shouldn't be displayed.
116
118
  2. If user wants to edit the patient identifier's value, hence there will be an initialValue,
117
119
  along with a source assigned to itself(only if the identifierType has sources, else there's nothing to worry about), which by
@@ -1,8 +1,8 @@
1
- @use '@carbon/styles/scss/spacing';
2
- @import '../../patient-registration.scss';
1
+ @use '@carbon/layout';
2
+ @use '@openmrs/esm-styleguide/src/vars' as *;
3
3
 
4
4
  .button {
5
- height: 4rem;
5
+ height: layout.$spacing-10;
6
6
  display: flex;
7
7
  align-content: flex-start;
8
8
  align-items: baseline;
@@ -10,28 +10,32 @@
10
10
  }
11
11
 
12
12
  .tablet {
13
- padding: 1.5rem 1rem;
13
+ padding: layout.$spacing-06 layout.$spacing-05;
14
14
  background-color: $ui-02;
15
15
  }
16
16
 
17
17
  .desktop {
18
- padding: 0rem;
18
+ padding: 0;
19
19
  }
20
20
 
21
21
  .radioGroup {
22
22
  background-color: $ui-01;
23
- padding: spacing.$spacing-05;
23
+ padding: layout.$spacing-05;
24
24
  }
25
25
 
26
26
  .radioButton {
27
27
  margin: 0 !important;
28
28
  label {
29
- height: spacing.$spacing-07;
29
+ height: layout.$spacing-07;
30
30
  }
31
31
  }
32
32
 
33
+ .space05 {
34
+ margin: layout.$spacing-05 0;
35
+ }
36
+
33
37
  :global(.omrs-breakpoint-lt-desktop) {
34
38
  .radioButton label {
35
- height: spacing.$spacing-09 !important;
39
+ height: layout.$spacing-09 !important;
36
40
  }
37
41
  }
@@ -1,12 +1,12 @@
1
- import React, { useCallback, useContext } from 'react';
1
+ import React, { useCallback } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { ContentSwitcher, Switch } from '@carbon/react';
4
4
  import { useField } from 'formik';
5
5
  import { ExtensionSlot, useConfig } from '@openmrs/esm-framework';
6
+ import { type RegistrationConfig } from '../../../config-schema';
7
+ import { usePatientRegistrationContext } from '../../patient-registration-context';
6
8
  import { Input } from '../../input/basic-input/input/input.component';
7
- import { PatientRegistrationContext } from '../../patient-registration-context';
8
9
  import styles from '../field.scss';
9
- import { type RegistrationConfig } from '../../../config-schema';
10
10
 
11
11
  export const unidentifiedPatientAttributeTypeUuid = '8b56eac7-5c76-4b9c-8c6f-1deab8d3fc47';
12
12
  const containsNoNumbers = /^([^0-9]*)$/;
@@ -21,7 +21,8 @@ function checkNumber(value: string) {
21
21
 
22
22
  export const NameField = () => {
23
23
  const { t } = useTranslation();
24
- const { setCapturePhotoProps, currentPhoto, setFieldValue } = useContext(PatientRegistrationContext);
24
+ const { setCapturePhotoProps, currentPhoto, setFieldValue, setFieldTouched } = usePatientRegistrationContext();
25
+
25
26
  const {
26
27
  fieldConfigurations: {
27
28
  name: {
@@ -48,9 +49,10 @@ export const NameField = () => {
48
49
  imageData: dataUri,
49
50
  dateTime: photoDateTime,
50
51
  });
52
+ setFieldTouched('photo', true, false);
51
53
  }
52
54
  },
53
- [setCapturePhotoProps],
55
+ [setCapturePhotoProps, setFieldTouched],
54
56
  );
55
57
 
56
58
  const toggleNameKnown = (e) => {
@@ -63,6 +65,9 @@ export const NameField = () => {
63
65
  setFieldValue('familyName', defaultUnknownFamilyName);
64
66
  setUnknownPatient('true');
65
67
  }
68
+ setFieldTouched('givenName', true);
69
+ setFieldTouched('familyName', true);
70
+ setFieldTouched(`attributes.${unidentifiedPatientAttributeTypeUuid}`, true, false);
66
71
  };
67
72
 
68
73
  const firstNameField = (
@@ -3,11 +3,12 @@ import classNames from 'classnames';
3
3
  import { Field } from 'formik';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { InlineNotification, Layer, Select, SelectItem } from '@carbon/react';
6
- import { useConfig } from '@openmrs/esm-framework';
6
+ import { OpenmrsDatePicker, useConfig } from '@openmrs/esm-framework';
7
7
  import { type ConceptResponse } from '../../patient-registration.types';
8
8
  import { type FieldDefinition, type RegistrationConfig } from '../../../config-schema';
9
9
  import { Input } from '../../input/basic-input/input/input.component';
10
10
  import { useConcept, useConceptAnswers } from '../field.resource';
11
+ import { usePatientRegistrationContext } from '../../patient-registration-context';
11
12
  import styles from './../field.scss';
12
13
 
13
14
  export interface ObsFieldProps {
@@ -17,7 +18,6 @@ export interface ObsFieldProps {
17
18
  export function ObsField({ fieldDefinition }: ObsFieldProps) {
18
19
  const { t } = useTranslation();
19
20
  const { data: concept, isLoading } = useConcept(fieldDefinition.uuid);
20
-
21
21
  const config = useConfig<RegistrationConfig>();
22
22
 
23
23
  if (!config.registrationObs.encounterTypeUuid) {
@@ -51,6 +51,16 @@ export function ObsField({ fieldDefinition }: ObsFieldProps) {
51
51
  required={fieldDefinition.validation.required}
52
52
  />
53
53
  );
54
+ case 'Date':
55
+ return (
56
+ <DateObsField
57
+ concept={concept}
58
+ label={fieldDefinition.label}
59
+ required={fieldDefinition.validation.required}
60
+ allowPastDates={fieldDefinition.allowPastDates}
61
+ allowFutureDates={fieldDefinition.allowFutureDates}
62
+ />
63
+ );
54
64
  case 'Coded':
55
65
  return (
56
66
  <CodedObsField
@@ -145,6 +155,53 @@ function NumericObsField({ concept, label, required }: NumericObsFieldProps) {
145
155
  );
146
156
  }
147
157
 
158
+ interface DateObsFieldProps {
159
+ concept: ConceptResponse;
160
+ label: string;
161
+ required?: boolean;
162
+ allowPastDates?: boolean;
163
+ allowFutureDates?: boolean;
164
+ }
165
+
166
+ function DateObsField({ concept, label, required, allowPastDates, allowFutureDates }: DateObsFieldProps) {
167
+ const { t } = useTranslation();
168
+ const fieldName = `obs.${concept.uuid}`;
169
+ const { setFieldValue } = usePatientRegistrationContext();
170
+ const futureDatesAllowed = allowFutureDates ?? true;
171
+ const pastDatesAllowed = allowPastDates ?? true;
172
+
173
+ const onDateChange = (date: Date) => {
174
+ setFieldValue(fieldName, date);
175
+ };
176
+
177
+ return (
178
+ <Layer>
179
+ <div className={styles.dobField}>
180
+ <Field name={fieldName}>
181
+ {({ field, form: { touched, errors }, meta }) => {
182
+ return (
183
+ <>
184
+ <OpenmrsDatePicker
185
+ id={fieldName}
186
+ {...field}
187
+ isRequired={required}
188
+ onChange={onDateChange}
189
+ labelText={label ?? concept.display}
190
+ isInvalid={errors[fieldName] && touched[fieldName]}
191
+ invalidText={t(meta.error)}
192
+ value={field.value}
193
+ minDate={!pastDatesAllowed ? new Date() : undefined}
194
+ maxDate={!futureDatesAllowed ? new Date() : undefined}
195
+ />
196
+ </>
197
+ );
198
+ }}
199
+ </Field>
200
+ </div>
201
+ </Layer>
202
+ );
203
+ }
204
+
148
205
  interface CodedObsFieldProps {
149
206
  concept: ConceptResponse;
150
207
  answerConceptSetUuid?: string;