@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
@@ -0,0 +1,193 @@
1
+ import React from 'react';
2
+ import { Form, Formik } from 'formik';
3
+ import { render, screen } from '@testing-library/react';
4
+ import { type FieldDefinition } from '../../../config-schema';
5
+ import { usePersonAttributeType } from './person-attributes.resource';
6
+ import { useConceptAnswers } from '../field.resource';
7
+ import { PersonAttributeField } from './person-attribute-field.component';
8
+
9
+ jest.mock('./person-attributes.resource', () => ({
10
+ ...jest.requireActual('./person-attributes.resource'),
11
+ usePersonAttributeType: jest.fn(),
12
+ }));
13
+
14
+ jest.mock('../field.resource', () => ({
15
+ ...jest.requireActual('../field.resource'),
16
+ useConceptAnswers: jest.fn(),
17
+ }));
18
+
19
+ const mockUsePersonAttributeType = jest.mocked(usePersonAttributeType);
20
+ const mockUseConceptAnswers = jest.mocked(useConceptAnswers);
21
+
22
+ const mockPersonAttributeType = {
23
+ format: 'java.lang.String',
24
+ display: 'Referred by',
25
+ uuid: '4dd56a75-14ab-4148-8700-1f4f704dc5b0',
26
+ name: 'Referred by',
27
+ description: 'The person who referred the patient',
28
+ };
29
+
30
+ let fieldDefinition: FieldDefinition = {
31
+ id: 'referredby',
32
+ label: 'Referred by',
33
+ type: 'person attribute',
34
+ uuid: '4dd56a75-14ab-4148-8700-1f4f704dc5b0',
35
+ answerConceptSetUuid: '6682d17f-0777-45e4-a39b-93f77eb3531c',
36
+ validation: {
37
+ matches: '',
38
+ required: true,
39
+ },
40
+ showHeading: true,
41
+ };
42
+
43
+ describe('PersonAttributeField', () => {
44
+ beforeEach(() => {
45
+ mockUsePersonAttributeType.mockReturnValue({
46
+ data: mockPersonAttributeType,
47
+ isLoading: false,
48
+ error: null,
49
+ });
50
+ });
51
+
52
+ it('renders the text input field for String format', () => {
53
+ render(
54
+ <Formik initialValues={{}} onSubmit={() => {}}>
55
+ <Form>
56
+ <PersonAttributeField fieldDefinition={fieldDefinition} />
57
+ </Form>
58
+ </Formik>,
59
+ );
60
+
61
+ const input = screen.getByLabelText(/Referred by/i) as HTMLInputElement;
62
+ expect(screen.getByRole('heading')).toBeInTheDocument();
63
+ expect(input).toBeInTheDocument();
64
+ expect(input.type).toBe('text');
65
+ });
66
+
67
+ it('should not show heading if showHeading is false', () => {
68
+ fieldDefinition = {
69
+ ...fieldDefinition,
70
+ showHeading: false,
71
+ };
72
+
73
+ render(
74
+ <Formik initialValues={{}} onSubmit={() => {}}>
75
+ <Form>
76
+ <PersonAttributeField fieldDefinition={fieldDefinition} />
77
+ </Form>
78
+ </Formik>,
79
+ );
80
+
81
+ expect(screen.queryByRole('heading')).not.toBeInTheDocument();
82
+ });
83
+
84
+ it('renders the coded attribute field for Concept format', () => {
85
+ fieldDefinition = {
86
+ id: 'referredby',
87
+ ...fieldDefinition,
88
+ label: 'Referred by',
89
+ };
90
+
91
+ mockUsePersonAttributeType.mockReturnValue({
92
+ data: { ...mockPersonAttributeType, format: 'org.openmrs.Concept' },
93
+ isLoading: false,
94
+ error: null,
95
+ });
96
+
97
+ mockUseConceptAnswers.mockReturnValueOnce({
98
+ data: [
99
+ { uuid: '1', display: 'Option 1' },
100
+ { uuid: '2', display: 'Option 2' },
101
+ ],
102
+ error: null,
103
+ isLoading: false,
104
+ });
105
+
106
+ render(
107
+ <Formik initialValues={{}} onSubmit={() => {}}>
108
+ <Form>
109
+ <PersonAttributeField fieldDefinition={fieldDefinition} />
110
+ </Form>
111
+ </Formik>,
112
+ );
113
+
114
+ const input = screen.getByLabelText(/Referred by/i) as HTMLInputElement;
115
+ expect(input).toBeInTheDocument();
116
+ expect(input.type).toBe('select-one');
117
+ expect(screen.getByText('Option 1')).toBeInTheDocument();
118
+ expect(screen.getByText('Option 2')).toBeInTheDocument();
119
+ });
120
+
121
+ it('renders an error notification if attribute type has unknown format', () => {
122
+ mockUsePersonAttributeType.mockReturnValue({
123
+ data: { ...mockPersonAttributeType, format: 'unknown' },
124
+ isLoading: false,
125
+ error: null,
126
+ });
127
+
128
+ render(
129
+ <Formik initialValues={{}} onSubmit={() => {}}>
130
+ <Form>
131
+ <PersonAttributeField fieldDefinition={fieldDefinition} />
132
+ </Form>
133
+ </Formik>,
134
+ );
135
+
136
+ expect(screen.getByText('Error')).toBeInTheDocument();
137
+ expect(screen.getByText(/Patient attribute type has unknown format/i)).toBeInTheDocument();
138
+ });
139
+
140
+ it('renders an error notification if unable to fetch attribute type', () => {
141
+ mockUsePersonAttributeType.mockReturnValue({
142
+ data: null,
143
+ isLoading: false,
144
+ error: new Error('Failed to fetch attribute type'),
145
+ });
146
+
147
+ fieldDefinition = {
148
+ id: 'referredBy',
149
+ uuid: 'attribute-uuid',
150
+ label: 'Attribute',
151
+ showHeading: false,
152
+ type: 'person attribute',
153
+ };
154
+
155
+ render(
156
+ <Formik initialValues={{}} onSubmit={() => {}}>
157
+ <Form>
158
+ <PersonAttributeField fieldDefinition={fieldDefinition} />
159
+ </Form>
160
+ </Formik>,
161
+ );
162
+
163
+ expect(screen.getByText('Error')).toBeInTheDocument();
164
+ expect(screen.getByText(/Unable to fetch person attribute type/i)).toBeInTheDocument();
165
+ });
166
+
167
+ it('renders a skeleton if attribute type is loading', async () => {
168
+ mockUsePersonAttributeType.mockReturnValue({
169
+ data: null,
170
+ isLoading: true,
171
+ error: null,
172
+ });
173
+
174
+ fieldDefinition = {
175
+ id: 'referredBy',
176
+ uuid: 'attribute-uuid',
177
+ label: 'Attribute',
178
+ showHeading: true,
179
+ type: 'person attribute',
180
+ };
181
+
182
+ render(
183
+ <Formik initialValues={{}} onSubmit={() => {}}>
184
+ <Form>
185
+ <PersonAttributeField fieldDefinition={fieldDefinition} />
186
+ </Form>
187
+ </Formik>,
188
+ );
189
+
190
+ await screen.findByRole('heading', { name: /attribute/i });
191
+ expect(screen.queryByLabelText(/referred by/i)).not.toBeInTheDocument();
192
+ });
193
+ });
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { Form, Formik } from 'formik';
5
+ import { TextPersonAttributeField } from './text-person-attribute-field.component';
6
+
7
+ describe('TextPersonAttributeField', () => {
8
+ const mockPersonAttributeType = {
9
+ format: 'java.lang.String',
10
+ display: 'Referred by',
11
+ uuid: '4dd56a75-14ab-4148-8700-1f4f704dc5b0',
12
+ description: 'Referred by',
13
+ name: 'Referred by',
14
+ };
15
+
16
+ it('renders the input field with a label', () => {
17
+ render(
18
+ <Formik initialValues={{}} onSubmit={() => {}}>
19
+ <Form>
20
+ <TextPersonAttributeField
21
+ id="attributeId"
22
+ personAttributeType={mockPersonAttributeType}
23
+ label="Custom Label"
24
+ />
25
+ </Form>
26
+ </Formik>,
27
+ );
28
+
29
+ expect(screen.getByRole('textbox', { name: /custom label \(optional\)/i })).toBeInTheDocument();
30
+ });
31
+
32
+ it('renders the input field with the default label if label prop is not provided', () => {
33
+ render(
34
+ <Formik initialValues={{}} onSubmit={() => {}}>
35
+ <Form>
36
+ <TextPersonAttributeField id="attributeId" personAttributeType={mockPersonAttributeType} />
37
+ </Form>
38
+ </Formik>,
39
+ );
40
+
41
+ expect(screen.getByRole('textbox', { name: /referred by \(optional\)/i })).toBeInTheDocument();
42
+ });
43
+
44
+ it('validates the input with the provided validationRegex', async () => {
45
+ const user = userEvent.setup();
46
+ const validationRegex = '^[A-Z]+$'; // Accepts only uppercase letters
47
+
48
+ render(
49
+ <Formik initialValues={{}} onSubmit={() => {}}>
50
+ <Form>
51
+ <TextPersonAttributeField
52
+ id="attributeId"
53
+ personAttributeType={mockPersonAttributeType}
54
+ validationRegex={validationRegex}
55
+ />
56
+ </Form>
57
+ </Formik>,
58
+ );
59
+
60
+ const textbox = screen.getByRole('textbox', { name: /referred by \(optional\)/i });
61
+ expect(textbox).toBeInTheDocument();
62
+
63
+ // Valid input: "ABC"
64
+ await user.type(textbox, 'ABC');
65
+ await user.tab();
66
+
67
+ expect(screen.queryByText(/invalid input/i)).not.toBeInTheDocument();
68
+ await user.clear(textbox);
69
+
70
+ // // Invalid input: "abc" (contains lowercase letters)
71
+ await user.type(textbox, 'abc');
72
+ await user.tab();
73
+ expect(screen.getByText(/invalid input/i)).toBeInTheDocument();
74
+ });
75
+
76
+ it('renders the input field as required when required prop is true', () => {
77
+ render(
78
+ <Formik initialValues={{}} onSubmit={() => {}}>
79
+ <Form>
80
+ <TextPersonAttributeField id="attributeId" personAttributeType={mockPersonAttributeType} required />
81
+ </Form>
82
+ </Formik>,
83
+ );
84
+ const textbox = screen.getByRole('textbox', { name: /referred by/i });
85
+
86
+ // Required attribute should be truthy on the input element
87
+ expect(textbox).toBeInTheDocument();
88
+ expect(textbox).toBeRequired();
89
+ });
90
+ });
@@ -0,0 +1,91 @@
1
+ import { FormManager } from './form-manager';
2
+ import { type FormValues } from './patient-registration.types';
3
+ import { generateIdentifier } from './patient-registration.resource';
4
+
5
+ jest.mock('./patient-registration.resource', () => ({
6
+ ...jest.requireActual('./patient-registration.resource'),
7
+ generateIdentifier: jest.fn(),
8
+ }));
9
+
10
+ const mockGenerateIdentifier = generateIdentifier as jest.Mock;
11
+
12
+ const formValues: FormValues = {
13
+ patientUuid: '',
14
+ givenName: '',
15
+ middleName: '',
16
+ familyName: '',
17
+ additionalGivenName: '',
18
+ additionalMiddleName: '',
19
+ additionalFamilyName: '',
20
+ addNameInLocalLanguage: false,
21
+ gender: '',
22
+ birthdate: '',
23
+ yearsEstimated: 1000,
24
+ monthsEstimated: 11,
25
+ birthdateEstimated: false,
26
+ telephoneNumber: '',
27
+ isDead: false,
28
+ deathDate: 'string',
29
+ deathTime: '',
30
+ deathTimeFormat: 'AM',
31
+ deathCause: 'string',
32
+ nonCodedCauseOfDeath: '',
33
+ relationships: [],
34
+ address: {
35
+ address1: '',
36
+ address2: '',
37
+ cityVillage: '',
38
+ stateProvince: 'New York',
39
+ country: 'string',
40
+ postalCode: 'string',
41
+ },
42
+ identifiers: {
43
+ foo: {
44
+ identifierUuid: 'aUuid',
45
+ identifierName: 'Foo',
46
+ required: false,
47
+ initialValue: 'foo',
48
+ identifierValue: 'foo',
49
+ identifierTypeUuid: 'identifierType',
50
+ preferred: true,
51
+ autoGeneration: false,
52
+ selectedSource: {
53
+ uuid: 'some-uuid',
54
+ name: 'unique',
55
+ autoGenerationOption: { manualEntryEnabled: true, automaticGenerationEnabled: false },
56
+ },
57
+ },
58
+ },
59
+ };
60
+
61
+ describe('FormManager', () => {
62
+ describe('createIdentifiers', () => {
63
+ it('uses the uuid of a field name if it exists', async () => {
64
+ const result = await FormManager.savePatientIdentifiers(true, undefined, formValues.identifiers, {}, 'Nyc');
65
+ expect(result).toEqual([
66
+ {
67
+ uuid: 'aUuid',
68
+ identifier: 'foo',
69
+ identifierType: 'identifierType',
70
+ location: 'Nyc',
71
+ preferred: true,
72
+ },
73
+ ]);
74
+ });
75
+
76
+ it('should generate identifier if it has autoGeneration and manual entry disabled', async () => {
77
+ formValues.identifiers.foo.autoGeneration = true;
78
+ formValues.identifiers.foo.selectedSource.autoGenerationOption.manualEntryEnabled = false;
79
+ mockGenerateIdentifier.mockResolvedValue({ data: { identifier: '10001V' } });
80
+ await FormManager.savePatientIdentifiers(true, undefined, formValues.identifiers, {}, 'Nyc');
81
+ expect(mockGenerateIdentifier.mock.calls).toHaveLength(1);
82
+ });
83
+
84
+ it('should not generate identifiers if manual entry enabled and identifier value given', async () => {
85
+ formValues.identifiers.foo.autoGeneration = true;
86
+ formValues.identifiers.foo.selectedSource.autoGenerationOption.manualEntryEnabled = true;
87
+ await FormManager.savePatientIdentifiers(true, undefined, formValues.identifiers, {}, 'Nyc');
88
+ expect(mockGenerateIdentifier.mock.calls).toHaveLength(0);
89
+ });
90
+ });
91
+ });
@@ -1,35 +1,39 @@
1
1
  import {
2
2
  type FetchResponse,
3
+ getConfig,
3
4
  openmrsFetch,
4
5
  queueSynchronizationItem,
5
- type Session,
6
6
  restBaseUrl,
7
- getConfig,
7
+ type Session,
8
+ type StyleguideConfigObject,
9
+ toOmrsIsoString,
8
10
  } from '@openmrs/esm-framework';
9
11
  import { patientRegistration } from '../constants';
10
12
  import {
11
- type FormValues,
12
13
  type AttributeValue,
13
- type PatientUuidMapType,
14
- type Patient,
15
14
  type CapturePhotoProps,
15
+ type Encounter,
16
+ type FormValues,
17
+ type Patient,
16
18
  type PatientIdentifier,
17
19
  type PatientRegistration,
20
+ type PatientUuidMapType,
18
21
  type RelationshipValue,
19
- type Encounter,
20
22
  } from './patient-registration.types';
21
23
  import {
22
24
  addPatientIdentifier,
23
25
  deletePatientIdentifier,
24
26
  deletePersonName,
25
27
  deleteRelationship,
28
+ generateAmrsUniversalIdentifier,
26
29
  generateIdentifier,
30
+ getDatetime,
31
+ saveEncounter,
27
32
  savePatient,
28
33
  savePatientPhoto,
29
34
  saveRelationship,
30
- updateRelationship,
31
35
  updatePatientIdentifier,
32
- saveEncounter,
36
+ updateRelationship,
33
37
  } from './patient-registration.resource';
34
38
  import { type RegistrationConfig } from '../config-schema';
35
39
 
@@ -61,7 +65,7 @@ export class FormManager {
61
65
  ) => {
62
66
  const syncItem: PatientRegistration = {
63
67
  fhirPatient: FormManager.mapPatientToFhirPatient(
64
- FormManager.getPatientToCreate(isNewPatient, values, patientUuidMap, initialAddressFieldValues, []),
68
+ FormManager.getPatientToCreate(isNewPatient, values, patientUuidMap, initialAddressFieldValues, [], config),
65
69
  ),
66
70
  _patientRegistrationData: {
67
71
  isNewPatient,
@@ -114,6 +118,7 @@ export class FormManager {
114
118
  patientUuidMap,
115
119
  initialAddressFieldValues,
116
120
  patientIdentifiers,
121
+ config,
117
122
  );
118
123
 
119
124
  FormManager.getDeletedNames(values.patientUuid, patientUuidMap).forEach(async (name) => {
@@ -131,14 +136,15 @@ export class FormManager {
131
136
 
132
137
  await this.saveObservations(values.obs, savePatientResponse, currentLocation, currentUser, config);
133
138
 
134
- const { patientPhotoUuid } = await getConfig('@openmrs/esm-styleguide');
135
- if (patientPhotoUuid && capturePhotoProps?.imageData) {
139
+ const { patientPhotoConceptUuid } = await getConfig<StyleguideConfigObject>('@openmrs/esm-styleguide');
140
+
141
+ if (patientPhotoConceptUuid && capturePhotoProps?.imageData) {
136
142
  await savePatientPhoto(
137
143
  savePatientResponse.data.uuid,
138
144
  capturePhotoProps.imageData,
139
145
  `${restBaseUrl}/obs`,
140
146
  capturePhotoProps.dateTime || new Date().toISOString(),
141
- patientPhotoUuid,
147
+ patientPhotoConceptUuid,
142
148
  );
143
149
  }
144
150
  }
@@ -189,6 +195,7 @@ export class FormManager {
189
195
  );
190
196
  } else {
191
197
  const encounterToSave: Encounter = {
198
+ encounterDatetime: new Date(),
192
199
  patient: savePatientResponse.data.uuid,
193
200
  encounterType: config.registrationObs.encounterTypeUuid,
194
201
  location: currentLocation,
@@ -234,11 +241,19 @@ export class FormManager {
234
241
  initialValue,
235
242
  } = patientIdentifier;
236
243
 
237
- const identifier = !autoGeneration
238
- ? identifierValue
239
- : await (
240
- await generateIdentifier(selectedSource.uuid)
241
- ).data.identifier;
244
+ const autoGenerationManualEntry =
245
+ autoGeneration && selectedSource?.autoGenerationOption?.manualEntryEnabled && !!identifierValue;
246
+ let identifier;
247
+ if (identifierTypeUuid === '58a4732e-1359-11df-a1f1-0026b9348838') {
248
+ identifier = await generateAmrsUniversalIdentifier();
249
+ } else {
250
+ identifier =
251
+ !autoGeneration || autoGenerationManualEntry
252
+ ? identifierValue
253
+ : await (
254
+ await generateIdentifier(selectedSource.uuid)
255
+ ).data.identifier;
256
+ }
242
257
  const identifierToCreate = {
243
258
  uuid: identifierUuid,
244
259
  identifier,
@@ -294,6 +309,7 @@ export class FormManager {
294
309
  patientUuidMap: PatientUuidMapType,
295
310
  initialAddressFieldValues: Record<string, any>,
296
311
  identifiers: Array<PatientIdentifier>,
312
+ config?: RegistrationConfig,
297
313
  ): Patient {
298
314
  let birthdate;
299
315
  if (values.birthdate instanceof Date) {
@@ -314,7 +330,7 @@ export class FormManager {
314
330
  birthdateEstimated: values.birthdateEstimated,
315
331
  attributes: FormManager.getPatientAttributes(isNewPatient, values, patientUuidMap),
316
332
  addresses: [values.address],
317
- ...FormManager.getPatientDeathInfo(values),
333
+ ...FormManager.getPatientDeathInfo(values, config),
318
334
  },
319
335
  identifiers,
320
336
  };
@@ -373,12 +389,22 @@ export class FormManager {
373
389
  return attributes;
374
390
  }
375
391
 
376
- static getPatientDeathInfo(values: FormValues) {
377
- const { isDead, deathDate, deathCause } = values;
392
+ static getPatientDeathInfo(values: FormValues, config?: RegistrationConfig) {
393
+ const { isDead, deathDate, deathTime, deathTimeFormat, deathCause, nonCodedCauseOfDeath } = values;
394
+
395
+ if (!isDead) {
396
+ return {
397
+ dead: false,
398
+ };
399
+ }
400
+ const dateTimeOfDeath = toOmrsIsoString(getDatetime(deathDate, deathTime, deathTimeFormat));
401
+
378
402
  return {
379
- dead: isDead,
380
- deathDate: isDead ? deathDate : undefined,
381
- causeOfDeath: isDead ? deathCause : undefined,
403
+ dead: true,
404
+ deathDate: dateTimeOfDeath,
405
+ ...(deathCause === config?.freeTextFieldConceptUuid
406
+ ? { causeOfDeathNonCoded: nonCodedCauseOfDeath, causeOfDeath: null }
407
+ : { causeOfDeath: deathCause, causeOfDeathNonCoded: null }),
382
408
  };
383
409
  }
384
410
 
@@ -66,7 +66,7 @@ export interface TextInputProps
66
66
  * `true` to use the light version. For use on $ui-01 backgrounds only.
67
67
  * Don't use this to make tile background color same as container background color.
68
68
  * 'The `light` prop for `TextInput` has ' +
69
- 'been deprecated in favor of the new `Layer` component. It will be removed in the next major release.'
69
+ 'been deprecated in favor of the new `Layer` component. It will be removed in the next major release.'
70
70
  */
71
71
  light?: boolean;
72
72
 
@@ -145,10 +145,14 @@ export const Input: React.FC<InputProps> = ({ checkWarning, ...props }) => {
145
145
  t('invalidEmail')
146
146
  t('numberInNameDubious')
147
147
  t('yearsEstimateRequired')
148
+ t('deathdayIsRequired', 'Death date is required when the patient is marked as deceased.')
149
+ t('deathdayInvalidDate', 'Date of death is invalid')
150
+ t('deathCauseRequired', 'Cause of death is required')
151
+ t('nonCodedCauseOfDeathRequired', 'Non-coded cause of death is required')
148
152
  */
149
153
 
150
154
  const value = field.value || '';
151
- const invalidText = meta.error && t(meta.error);
155
+ const invalidText = meta.error || '';
152
156
  const warnText = useMemo(() => {
153
157
  if (!invalidText && typeof checkWarning === 'function') {
154
158
  const warning = checkWarning(value);
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { Formik, Form } from 'formik';
5
+ import { SelectInput } from './select-input.component';
6
+
7
+ describe('the select input', () => {
8
+ const setupSelect = async () => {
9
+ render(
10
+ <Formik initialValues={{ select: '' }} onSubmit={null}>
11
+ <Form>
12
+ <SelectInput label="Select" name="select" options={['A Option', 'B Option']} required />
13
+ </Form>
14
+ </Formik>,
15
+ );
16
+ return screen.getByLabelText('Select') as HTMLInputElement;
17
+ };
18
+
19
+ it('exists', async () => {
20
+ const input = await setupSelect();
21
+ expect(input.type).toEqual('select-one');
22
+ });
23
+
24
+ it('can input data', async () => {
25
+ const user = userEvent.setup();
26
+ const input = await setupSelect();
27
+ const expected = 'A Option';
28
+
29
+ await user.selectOptions(input, expected);
30
+
31
+ await expect(input.value).toEqual(expected);
32
+ });
33
+
34
+ it('should show optional label if the input is not required', async () => {
35
+ render(
36
+ <Formik initialValues={{ select: '' }} onSubmit={null}>
37
+ <Form>
38
+ <SelectInput label="Select" name="select" options={['A Option', 'B Option']} />
39
+ </Form>
40
+ </Formik>,
41
+ );
42
+
43
+ await screen.findByRole('combobox');
44
+
45
+ const selectInput = screen.getByRole('combobox', { name: 'Select (optional)' }) as HTMLSelectElement;
46
+ expect(selectInput.labels).toHaveLength(1);
47
+ expect(selectInput.labels[0]).toHaveTextContent('Select (optional)');
48
+ });
49
+ });
@@ -1,6 +1,6 @@
1
- @use '@carbon/styles/scss/spacing';
2
- @use '@carbon/styles/scss/type';
3
- @import '~@openmrs/esm-styleguide/src/vars';
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@openmrs/esm-styleguide/src/vars' as *;
4
4
 
5
5
  .label01 {
6
6
  @include type.type-style('label-01');
@@ -18,12 +18,12 @@
18
18
  position: absolute;
19
19
  left: 0;
20
20
  background-color: #fff;
21
- margin-bottom: 20px;
21
+ margin-bottom: 1.25rem;
22
22
  z-index: 99;
23
23
  }
24
24
 
25
25
  .suggestions li {
26
- padding: spacing.$spacing-05;
26
+ padding: layout.$spacing-05;
27
27
  line-height: 1.29;
28
28
  color: #525252;
29
29
  border-bottom: 1px solid #8d8d8d;