@olaboot/esm-patient-registration-app 9.2.0 → 10.0.2

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 (346) hide show
  1. package/dist/1339.js +1 -0
  2. package/dist/1339.js.map +1 -0
  3. package/dist/1480.js +1 -0
  4. package/dist/1480.js.map +1 -0
  5. package/dist/1646.js +1 -0
  6. package/dist/1646.js.map +1 -0
  7. package/dist/1789.js +1 -0
  8. package/dist/1789.js.map +1 -0
  9. package/dist/1869.js +1 -0
  10. package/dist/1869.js.map +1 -0
  11. package/dist/1877.js +1 -0
  12. package/dist/1877.js.map +1 -0
  13. package/dist/2317.js +1 -0
  14. package/dist/2317.js.map +1 -0
  15. package/dist/2416.js +1 -0
  16. package/dist/2416.js.map +1 -0
  17. package/dist/2747.js +1 -0
  18. package/dist/2747.js.map +1 -0
  19. package/dist/282.js +1 -0
  20. package/dist/282.js.map +1 -0
  21. package/dist/2881.js +1 -0
  22. package/dist/2881.js.map +1 -0
  23. package/dist/3378.js +1 -0
  24. package/dist/3378.js.map +1 -0
  25. package/dist/3720.js +1 -0
  26. package/dist/3720.js.map +1 -0
  27. package/dist/3906.js +1 -0
  28. package/dist/3906.js.map +1 -0
  29. package/dist/3963.js +1 -0
  30. package/dist/3963.js.map +1 -0
  31. package/dist/3989.js +1 -0
  32. package/dist/3989.js.map +1 -0
  33. package/dist/4106.js +1 -0
  34. package/dist/4106.js.map +1 -0
  35. package/dist/4111.js +1 -0
  36. package/dist/4111.js.map +1 -0
  37. package/dist/434.js +1 -0
  38. package/dist/434.js.map +1 -0
  39. package/dist/4348.js +1 -0
  40. package/dist/4348.js.map +1 -0
  41. package/dist/4383.js +1 -0
  42. package/dist/4383.js.map +1 -0
  43. package/dist/4658.js +1 -0
  44. package/dist/4658.js.map +1 -0
  45. package/dist/466.js +1 -0
  46. package/dist/466.js.map +1 -0
  47. package/dist/4928.js +1 -0
  48. package/dist/4928.js.map +1 -0
  49. package/dist/5117.js +1 -0
  50. package/dist/5117.js.map +1 -0
  51. package/dist/5132.js +1 -0
  52. package/dist/5132.js.map +1 -0
  53. package/dist/5145.js +1 -0
  54. package/dist/5145.js.map +1 -0
  55. package/dist/5208.js +43 -0
  56. package/dist/5208.js.map +1 -0
  57. package/dist/527.js +1 -0
  58. package/dist/527.js.map +1 -0
  59. package/dist/5280.js +1 -0
  60. package/dist/5280.js.map +1 -0
  61. package/dist/5338.js +6 -0
  62. package/dist/5338.js.map +1 -0
  63. package/dist/5503.js +1 -0
  64. package/dist/5503.js.map +1 -0
  65. package/dist/555.js +1 -0
  66. package/dist/555.js.map +1 -0
  67. package/dist/556.js +1 -0
  68. package/dist/556.js.map +1 -0
  69. package/dist/5644.js +1 -0
  70. package/dist/5644.js.map +1 -0
  71. package/dist/5697.js +1 -0
  72. package/dist/{4024.js.map → 5697.js.map} +1 -1
  73. package/dist/5940.js +1 -0
  74. package/dist/5940.js.map +1 -0
  75. package/dist/6047.js +1 -0
  76. package/dist/6047.js.map +1 -0
  77. package/dist/6371.js +1 -0
  78. package/dist/6371.js.map +1 -0
  79. package/dist/6377.js +1 -0
  80. package/dist/6377.js.map +1 -0
  81. package/dist/6388.js +1 -0
  82. package/dist/6388.js.map +1 -0
  83. package/dist/6444.js +1 -0
  84. package/dist/6444.js.map +1 -0
  85. package/dist/6508.js +1 -0
  86. package/dist/6508.js.map +1 -0
  87. package/dist/6724.js +1 -0
  88. package/dist/6724.js.map +1 -0
  89. package/dist/689.js +1 -0
  90. package/dist/689.js.map +1 -0
  91. package/dist/6904.js +1 -0
  92. package/dist/6904.js.map +1 -0
  93. package/dist/7045.js +1 -0
  94. package/dist/7045.js.map +1 -0
  95. package/dist/7175.js +1 -0
  96. package/dist/7175.js.map +1 -0
  97. package/dist/7182.js +1 -0
  98. package/dist/7182.js.map +1 -0
  99. package/dist/7649.js +1 -0
  100. package/dist/7649.js.map +1 -0
  101. package/dist/7742.js +1 -0
  102. package/dist/7742.js.map +1 -0
  103. package/dist/7912.js +1 -0
  104. package/dist/7912.js.map +1 -0
  105. package/dist/8358.js +1 -0
  106. package/dist/8358.js.map +1 -0
  107. package/dist/8359.js +1 -0
  108. package/dist/8359.js.map +1 -0
  109. package/dist/8695.js +1 -0
  110. package/dist/8695.js.map +1 -0
  111. package/dist/903.js +1 -0
  112. package/dist/903.js.map +1 -0
  113. package/dist/9061.js +1 -0
  114. package/dist/9061.js.map +1 -0
  115. package/dist/9072.js +1 -0
  116. package/dist/9072.js.map +1 -0
  117. package/dist/9397.js +1 -0
  118. package/dist/9397.js.map +1 -0
  119. package/dist/9712.js +1 -0
  120. package/dist/9712.js.map +1 -0
  121. package/dist/9771.js +1 -0
  122. package/dist/9771.js.map +1 -0
  123. package/dist/9806.js +1 -0
  124. package/dist/9806.js.map +1 -0
  125. package/dist/9816.js +1 -0
  126. package/dist/9816.js.map +1 -0
  127. package/dist/main.js +7 -6
  128. package/dist/main.js.map +1 -1
  129. package/dist/openmrs-esm-patient-registration-app.js +6 -0
  130. package/dist/{olaboot-esm-patient-registration-app.js.buildmanifest.json → openmrs-esm-patient-registration-app.js.buildmanifest.json} +540 -455
  131. package/dist/openmrs-esm-patient-registration-app.js.map +1 -0
  132. package/dist/routes.json +1 -1
  133. package/package.json +8 -9
  134. package/src/add-patient-link.extension.tsx +3 -2
  135. package/src/add-patient-link.test.tsx +2 -1
  136. package/src/config-schema.ts +1 -1
  137. package/src/index.ts +2 -24
  138. package/src/nav-link.test.tsx +1 -0
  139. package/src/offline.resources.ts +97 -31
  140. package/src/patient-registration/before-save-prompt.test.tsx +199 -0
  141. package/src/patient-registration/field/__mocks__/field.resource.ts +8 -7
  142. package/src/patient-registration/field/address/address-field.component.tsx +10 -13
  143. package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +6 -1
  144. package/src/patient-registration/field/address/address-hierarchy.test.tsx +191 -198
  145. package/src/patient-registration/field/address/address-search.component.tsx +20 -8
  146. package/src/patient-registration/field/address/address-search.scss +19 -2
  147. package/src/patient-registration/field/address/address-search.test.tsx +249 -57
  148. package/src/patient-registration/field/address/custom-address-field.component.tsx +1 -1
  149. package/src/patient-registration/field/cause-of-death/cause-of-death.component.tsx +1 -1
  150. package/src/patient-registration/field/cause-of-death/cause-of-death.test.tsx +251 -0
  151. package/src/patient-registration/field/custom-field.component.tsx +1 -1
  152. package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +1 -1
  153. package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.test.tsx +144 -0
  154. package/src/patient-registration/field/dob/dob.component.tsx +2 -2
  155. package/src/patient-registration/field/dob/dob.test.tsx +370 -54
  156. package/src/patient-registration/field/field.component.tsx +1 -1
  157. package/src/patient-registration/field/field.resource.ts +2 -2
  158. package/src/patient-registration/field/field.test.tsx +25 -22
  159. package/src/patient-registration/field/gender/gender-field.test.tsx +240 -54
  160. package/src/patient-registration/field/id/id-field.component.tsx +15 -5
  161. package/src/patient-registration/field/id/id-field.test.tsx +103 -47
  162. package/src/patient-registration/field/id/identifier-selection-overlay.test.tsx +346 -0
  163. package/src/patient-registration/field/name/name-field.component.tsx +2 -2
  164. package/src/patient-registration/field/name/name-field.test.tsx +282 -0
  165. package/src/patient-registration/field/obs/obs-field.test.tsx +294 -118
  166. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +172 -108
  167. package/src/patient-registration/field/person-attributes/location-person-attribute-field.component.tsx +3 -3
  168. package/src/patient-registration/field/person-attributes/location-person-attribute-field.resource.tsx +2 -5
  169. package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +2 -2
  170. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +249 -131
  171. package/src/patient-registration/field/person-attributes/person-attributes.resource.ts +1 -1
  172. package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +98 -70
  173. package/src/patient-registration/field/phone/phone-field.test.tsx +100 -0
  174. package/src/patient-registration/form-manager.test.ts +6 -5
  175. package/src/patient-registration/form-manager.ts +5 -2
  176. package/src/patient-registration/input/basic-input/input/input.component.tsx +3 -121
  177. package/src/patient-registration/input/basic-input/input/input.test.tsx +151 -51
  178. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +113 -33
  179. package/src/patient-registration/input/combo-input/combo-input.component.tsx +60 -24
  180. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +10 -101
  181. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +144 -108
  182. package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +241 -177
  183. package/src/patient-registration/input/custom-input/identifier/utils.test.ts +47 -8
  184. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +12 -12
  185. package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +52 -20
  186. package/src/patient-registration/input/input.scss +1 -2
  187. package/src/patient-registration/patient-registration-context.ts +5 -3
  188. package/src/patient-registration/patient-registration-hooks.ts +4 -12
  189. package/src/patient-registration/patient-registration-utils.test.ts +2 -1
  190. package/src/patient-registration/patient-registration-utils.ts +2 -98
  191. package/src/patient-registration/patient-registration.component.tsx +50 -46
  192. package/src/patient-registration/patient-registration.resource.test.tsx +4 -7
  193. package/src/patient-registration/patient-registration.resource.ts +1 -4
  194. package/src/patient-registration/patient-registration.scss +16 -3
  195. package/src/patient-registration/patient-registration.test.tsx +99 -65
  196. package/src/patient-registration/patient-registration.types.ts +17 -28
  197. package/src/patient-registration/section/death-info/death-info-section.test.tsx +130 -34
  198. package/src/patient-registration/section/demographics/demographics-section.test.tsx +122 -68
  199. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +15 -15
  200. package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +278 -84
  201. package/src/patient-registration/section/section-wrapper.component.tsx +1 -1
  202. package/src/patient-registration/ui-components/overlay/overlay.test.tsx +104 -0
  203. package/src/patient-registration/validation/patient-registration-validation.test.ts +2 -1
  204. package/src/patient-registration/validation/patient-registration-validation.ts +9 -3
  205. package/src/root.component.tsx +2 -5
  206. package/src/widgets/cancel-patient-edit.test.tsx +48 -11
  207. package/src/widgets/delete-identifier-confirmation.test.tsx +77 -24
  208. package/src/widgets/edit-patient-details-button.component.tsx +14 -18
  209. package/src/widgets/edit-patient-details-button.scss +2 -2
  210. package/src/widgets/edit-patient-details-button.test.tsx +11 -13
  211. package/translations/am.json +9 -4
  212. package/translations/ar.json +9 -4
  213. package/translations/ar_SY.json +9 -4
  214. package/translations/bn.json +9 -4
  215. package/translations/cs.json +9 -4
  216. package/translations/de.json +120 -115
  217. package/translations/en.json +9 -4
  218. package/translations/en_US.json +9 -4
  219. package/translations/es.json +9 -4
  220. package/translations/es_MX.json +9 -4
  221. package/translations/fr.json +9 -4
  222. package/translations/he.json +9 -4
  223. package/translations/hi.json +9 -4
  224. package/translations/hi_IN.json +9 -4
  225. package/translations/id.json +9 -4
  226. package/translations/it.json +9 -4
  227. package/translations/ka.json +9 -4
  228. package/translations/km.json +9 -4
  229. package/translations/ku.json +9 -4
  230. package/translations/ky.json +9 -4
  231. package/translations/lg.json +9 -4
  232. package/translations/ne.json +9 -4
  233. package/translations/pl.json +9 -4
  234. package/translations/pt.json +9 -4
  235. package/translations/pt_BR.json +10 -5
  236. package/translations/qu.json +9 -4
  237. package/translations/ro_RO.json +9 -4
  238. package/translations/ru_RU.json +9 -4
  239. package/translations/si.json +9 -4
  240. package/translations/sq.json +9 -4
  241. package/translations/sw.json +9 -4
  242. package/translations/sw_KE.json +9 -4
  243. package/translations/tr.json +9 -4
  244. package/translations/tr_TR.json +9 -4
  245. package/translations/uk.json +9 -4
  246. package/translations/uz.json +9 -4
  247. package/translations/uz@Latn.json +9 -4
  248. package/translations/uz_UZ.json +9 -4
  249. package/translations/vi.json +9 -4
  250. package/translations/zh.json +50 -45
  251. package/translations/zh_CN.json +9 -4
  252. package/translations/zh_TW.json +9 -4
  253. package/vitest.config.ts +4 -0
  254. package/ADDRESS_CONFIGURATION.md +0 -152
  255. package/IDENTIFIER_CONFIGURATION.md +0 -142
  256. package/IMPLEMENTATION_SUMMARY.md +0 -111
  257. package/QUICK_START.md +0 -95
  258. package/address-required-fields-config.json +0 -26
  259. package/dist/126.js +0 -1
  260. package/dist/15.js +0 -1
  261. package/dist/1564.js +0 -1
  262. package/dist/1567.js +0 -1
  263. package/dist/1845.js +0 -1
  264. package/dist/1953.js +0 -1
  265. package/dist/200.js +0 -1
  266. package/dist/200.js.map +0 -1
  267. package/dist/215.js +0 -1
  268. package/dist/2178.js +0 -1
  269. package/dist/250.js +0 -1
  270. package/dist/250.js.map +0 -1
  271. package/dist/2523.js +0 -1
  272. package/dist/2523.js.map +0 -1
  273. package/dist/2566.js +0 -1
  274. package/dist/2586.js +0 -1
  275. package/dist/2586.js.map +0 -1
  276. package/dist/2716.js +0 -1
  277. package/dist/2716.js.map +0 -1
  278. package/dist/2759.js +0 -1
  279. package/dist/2821.js +0 -6
  280. package/dist/2821.js.map +0 -1
  281. package/dist/3089.js +0 -1
  282. package/dist/3089.js.map +0 -1
  283. package/dist/3230.js +0 -1
  284. package/dist/3441.js +0 -1
  285. package/dist/3565.js +0 -1
  286. package/dist/3571.js +0 -1
  287. package/dist/3571.js.map +0 -1
  288. package/dist/3746.js +0 -1
  289. package/dist/3925.js +0 -1
  290. package/dist/3946.js +0 -1
  291. package/dist/4024.js +0 -1
  292. package/dist/4744.js +0 -1
  293. package/dist/4744.js.map +0 -1
  294. package/dist/4809.js +0 -1
  295. package/dist/4894.js +0 -1
  296. package/dist/4970.js +0 -1
  297. package/dist/4970.js.map +0 -1
  298. package/dist/5130.js +0 -1
  299. package/dist/5187.js +0 -1
  300. package/dist/5491.js +0 -1
  301. package/dist/5491.js.map +0 -1
  302. package/dist/5595.js +0 -1
  303. package/dist/5961.js +0 -1
  304. package/dist/6133.js +0 -1
  305. package/dist/634.js +0 -1
  306. package/dist/634.js.map +0 -1
  307. package/dist/6456.js +0 -1
  308. package/dist/6466.js +0 -1
  309. package/dist/6613.js +0 -1
  310. package/dist/6783.js +0 -1
  311. package/dist/7073.js +0 -38
  312. package/dist/7073.js.map +0 -1
  313. package/dist/7154.js +0 -1
  314. package/dist/7154.js.map +0 -1
  315. package/dist/7348.js +0 -1
  316. package/dist/7439.js +0 -1
  317. package/dist/7439.js.map +0 -1
  318. package/dist/7543.js +0 -1
  319. package/dist/7607.js +0 -1
  320. package/dist/772.js +0 -1
  321. package/dist/7984.js +0 -1
  322. package/dist/7984.js.map +0 -1
  323. package/dist/8538.js +0 -1
  324. package/dist/8538.js.map +0 -1
  325. package/dist/8599.js +0 -1
  326. package/dist/8727.js +0 -1
  327. package/dist/8847.js +0 -1
  328. package/dist/9015.js +0 -1
  329. package/dist/906.js +0 -1
  330. package/dist/9065.js +0 -1
  331. package/dist/9182.js +0 -1
  332. package/dist/9339.js +0 -1
  333. package/dist/9453.js +0 -1
  334. package/dist/9833.js +0 -1
  335. package/dist/9833.js.map +0 -1
  336. package/dist/9856.js +0 -1
  337. package/dist/9856.js.map +0 -1
  338. package/dist/9920.js +0 -1
  339. package/dist/9938.js +0 -1
  340. package/dist/9943.js +0 -1
  341. package/dist/9943.js.map +0 -1
  342. package/dist/olaboot-esm-patient-registration-app.js +0 -5
  343. package/dist/olaboot-esm-patient-registration-app.js.map +0 -1
  344. package/example-config.json +0 -14
  345. package/jest.config.js +0 -3
  346. package/src/resource.ts +0 -12
@@ -1,34 +1,66 @@
1
1
  import React from 'react';
2
+ import { vi, describe, it, expect } from 'vitest';
2
3
  import { render, screen } from '@testing-library/react';
3
4
  import userEvent from '@testing-library/user-event';
4
5
  import { DummyDataInput, dummyFormValues } from './dummy-data-input.component';
5
6
  import { initialFormValues } from '../../patient-registration.component';
6
7
  import { type FormValues } from '../../patient-registration.types';
7
8
 
8
- describe('Dummy data input', () => {
9
- let formValues: FormValues = initialFormValues;
10
-
11
- const setupInput = async () => {
12
- render(
13
- <DummyDataInput
14
- setValues={(values) => {
15
- formValues = values;
16
- }}
17
- />,
18
- );
19
- return screen.getByLabelText('Dummy Data Input') as HTMLButtonElement;
9
+ /**
10
+ * Helper to render DummyDataInput component.
11
+ */
12
+ function renderDummyDataInput(onSetValues?: (values: FormValues) => void) {
13
+ let capturedValues: FormValues = initialFormValues;
14
+ const handleSetValues =
15
+ onSetValues ||
16
+ ((values: FormValues) => {
17
+ capturedValues = values;
18
+ });
19
+
20
+ const utils = render(<DummyDataInput setValues={handleSetValues} />);
21
+
22
+ return {
23
+ ...utils,
24
+ getCapturedValues: () => capturedValues,
20
25
  };
26
+ }
21
27
 
22
- it('exists', async () => {
23
- const input = await setupInput();
24
- expect(input.type).toEqual('button');
28
+ describe('DummyDataInput component', () => {
29
+ describe('Rendering', () => {
30
+ it('renders the dummy data input button', () => {
31
+ renderDummyDataInput();
32
+
33
+ const button = screen.getByLabelText('Input Dummy Data') as HTMLButtonElement;
34
+ expect(button).toBeInTheDocument();
35
+ expect(button.type).toBe('button');
36
+ });
25
37
  });
26
38
 
27
- it('can input data on button click', async () => {
28
- const user = userEvent.setup();
29
- const input = await setupInput();
39
+ describe('User interaction', () => {
40
+ it('populates form values with dummy data when button is clicked', async () => {
41
+ const user = userEvent.setup();
42
+ const mockSetValues = vi.fn();
43
+ renderDummyDataInput(mockSetValues);
44
+
45
+ const button = screen.getByLabelText('Input Dummy Data');
46
+ await user.click(button);
47
+
48
+ expect(mockSetValues).toHaveBeenCalledTimes(1);
49
+ expect(mockSetValues).toHaveBeenCalledWith(dummyFormValues);
50
+ });
51
+
52
+ it('can be clicked multiple times', async () => {
53
+ const user = userEvent.setup();
54
+ const mockSetValues = vi.fn();
55
+ renderDummyDataInput(mockSetValues);
56
+
57
+ const button = screen.getByLabelText('Input Dummy Data');
58
+ await user.click(button);
59
+ await user.click(button);
60
+ await user.click(button);
30
61
 
31
- await user.click(input);
32
- expect(formValues).toEqual(dummyFormValues);
62
+ expect(mockSetValues).toHaveBeenCalledTimes(3);
63
+ expect(mockSetValues).toHaveBeenCalledWith(dummyFormValues);
64
+ });
33
65
  });
34
66
  });
@@ -82,8 +82,7 @@
82
82
  }
83
83
 
84
84
  .dummyData {
85
- cursor: pointer;
86
- margin-left: 5px;
85
+ margin: layout.$spacing-03 0;
87
86
  }
88
87
 
89
88
  .IDInput {
@@ -1,4 +1,6 @@
1
1
  import { createContext, type SetStateAction, useContext } from 'react';
2
+ import type { FormikHelpers } from 'formik';
3
+ import type { ObjectSchema } from 'yup';
2
4
  import { type PatientIdentifierType, useConfig } from '@openmrs/esm-framework';
3
5
  import { type CapturePhotoProps, type FormValues } from './patient-registration.types';
4
6
  import { type RegistrationConfig } from '../config-schema';
@@ -10,10 +12,10 @@ export interface PatientRegistrationContextProps {
10
12
  initialFormValues: FormValues;
11
13
  isOffline: boolean;
12
14
  setCapturePhotoProps(value: SetStateAction<CapturePhotoProps>): void;
13
- setFieldTouched(field: string, isTouched?: any, shouldValidate?: boolean): void;
14
- setFieldValue(field: string, value: any, shouldValidate?: boolean): void;
15
+ setFieldTouched: FormikHelpers<FormValues>['setFieldTouched'];
16
+ setFieldValue: FormikHelpers<FormValues>['setFieldValue'];
15
17
  setInitialFormValues?: React.Dispatch<SetStateAction<FormValues>>;
16
- validationSchema: any;
18
+ validationSchema: ObjectSchema<any> | null;
17
19
  values: FormValues;
18
20
  }
19
21
 
@@ -21,6 +21,7 @@ import {
21
21
  type PatientRegistration,
22
22
  type PatientUuidMapType,
23
23
  type PersonAttributeResponse,
24
+ type AddressProperties,
24
25
  } from './patient-registration.types';
25
26
  import {
26
27
  getAddressFieldValuesFromFhirPatient,
@@ -30,17 +31,6 @@ import {
30
31
  latestFirstEncounter,
31
32
  } from './patient-registration-utils';
32
33
 
33
- interface AddressFieldValues {
34
- address?: {
35
- cityVillage?: string;
36
- country?: string;
37
- countyDistrict?: string;
38
- postalCode?: string;
39
- stateProvince?: string;
40
- [key: string]: string | undefined;
41
- };
42
- }
43
-
44
34
  interface DeathInfoResults {
45
35
  causeOfDeath: OpenmrsResource | null;
46
36
  causeOfDeathNonCoded: string | null;
@@ -192,6 +182,8 @@ export function useInitialFormValues(
192
182
  return [initialFormValues, setInitialFormValues];
193
183
  }
194
184
 
185
+ type AddressFieldValues = Partial<Record<AddressProperties, string>>;
186
+
195
187
  export function useInitialAddressFieldValues(
196
188
  fallback: AddressFieldValues = {},
197
189
  isLoadingPatientToEdit: boolean,
@@ -243,7 +235,7 @@ export function usePatientUuidMap(
243
235
  try {
244
236
  const registration = await getPatientRegistration(patientUuid);
245
237
  if (!abortController.signal.aborted) {
246
- setPatientUuidMap(registration?._patientRegistrationData.initialAddressFieldValues ?? fallbackRef.current);
238
+ setPatientUuidMap(registration?._patientRegistrationData.patientUuidMap ?? fallbackRef.current);
247
239
  }
248
240
  } catch (error) {
249
241
  if (!abortController.signal.aborted) {
@@ -1,4 +1,5 @@
1
1
  import { filterOutUndefinedPatientIdentifiers } from './patient-registration-utils';
2
+ import { vi, describe, it, expect } from 'vitest';
2
3
 
3
4
  describe('filterOutUndefinedPatientIdentifiers', () => {
4
5
  const getIdentifiers = (autoGeneration = true, manualEntryEnabled = false) => ({
@@ -21,7 +22,7 @@ describe('filterOutUndefinedPatientIdentifiers', () => {
21
22
  },
22
23
  });
23
24
 
24
- it('should fitler out undefined identifiers', () => {
25
+ it('should filter out undefined identifiers', () => {
25
26
  const filteredIdentifiers = filterOutUndefinedPatientIdentifiers(getIdentifiers());
26
27
  expect(filteredIdentifiers.OpenMRSId).not.toBeDefined();
27
28
  });
@@ -1,103 +1,16 @@
1
- import * as Yup from 'yup';
2
1
  import camelCase from 'lodash-es/camelCase';
3
2
  import { parseDate } from '@openmrs/esm-framework';
4
3
  import {
5
- type AddressValidationSchemaType,
6
4
  type Encounter,
7
5
  type FormValues,
8
- type PatientIdentifier,
9
6
  type PatientIdentifierValue,
10
7
  type PatientUuidMapType,
11
8
  } from './patient-registration.types';
12
9
 
13
- export function parseAddressTemplateXml(addressTemplate: string) {
14
- const templateXmlDoc = new DOMParser().parseFromString(addressTemplate, 'text/xml');
15
- const nameMappings = templateXmlDoc.querySelector('nameMappings');
16
- const properties = nameMappings.getElementsByTagName('entry');
17
- const validationSchemaObjs = Array.prototype.map.call(properties, (property: Element) => {
18
- const name = property.getElementsByTagName('string')[0].innerHTML;
19
- const label = property.getElementsByTagName('string')[1].innerHTML;
20
- const regex = findElementValueInXmlDoc(name, 'elementRegex', templateXmlDoc) || '.*';
21
- const regexFormat = findElementValueInXmlDoc(name, 'elementRegexFormats', templateXmlDoc) || '';
22
-
23
- return {
24
- name,
25
- label,
26
- regex,
27
- regexFormat,
28
- };
29
- });
30
-
31
- const addressValidationSchema = Yup.object(
32
- validationSchemaObjs.reduce((final, current) => {
33
- final[current.name] = Yup.string().matches(current.regex, current.regexFormat);
34
- return final;
35
- }, {}),
36
- );
37
-
38
- const addressFieldValues = Array.prototype.map.call(properties, (property: Element) => {
39
- const name = property.getElementsByTagName('string')[0].innerHTML;
40
- return {
41
- name,
42
- defaultValue: '',
43
- };
44
- });
45
- return {
46
- addressFieldValues,
47
- addressValidationSchema,
48
- };
49
- }
50
-
51
- export function parseAddressTemplateXmlOld(addressTemplate: string) {
52
- const templateXmlDoc = new DOMParser().parseFromString(addressTemplate, 'text/xml');
53
- const nameMappings = templateXmlDoc.querySelector('nameMappings').querySelectorAll('property');
54
- const validationSchemaObjs: AddressValidationSchemaType[] = Array.prototype.map.call(
55
- nameMappings,
56
- (nameMapping: Element) => {
57
- const name = nameMapping.getAttribute('name');
58
- const label = nameMapping.getAttribute('value');
59
- const regex = findElementValueInXmlDoc(name, 'elementRegex', templateXmlDoc) || '.*';
60
- const regexFormat = findElementValueInXmlDoc(name, 'elementRegexFormats', templateXmlDoc) || '';
61
-
62
- return {
63
- name,
64
- label,
65
- regex,
66
- regexFormat,
67
- };
68
- },
69
- );
70
-
71
- const addressValidationSchema = Yup.object(
72
- validationSchemaObjs.reduce((final, current) => {
73
- final[current.name] = Yup.string().matches(current.regex, current.regexFormat);
74
- return final;
75
- }, {}),
76
- );
77
-
78
- const addressFieldValues: Array<{ name: string; defaultValue: string }> = Array.prototype.map.call(
79
- nameMappings,
80
- (nameMapping: Element) => {
81
- const name = nameMapping.getAttribute('name');
82
- const defaultValue = findElementValueInXmlDoc(name, 'elementDefaults', templateXmlDoc) ?? '';
83
- return { name, defaultValue };
84
- },
85
- );
86
-
87
- return {
88
- addressFieldValues,
89
- addressValidationSchema,
90
- };
91
- }
92
-
93
- function findElementValueInXmlDoc(fieldName: string, elementSelector: string, doc: XMLDocument) {
94
- return doc.querySelector(elementSelector)?.querySelector(`[name=${fieldName}]`)?.getAttribute('value') ?? null;
95
- }
96
-
97
10
  export function scrollIntoView(viewId: string) {
98
- document.getElementById(viewId).scrollIntoView({
11
+ document.getElementById(viewId)?.scrollIntoView({
99
12
  behavior: 'smooth',
100
- block: 'center',
13
+ block: 'start',
101
14
  inline: 'center',
102
15
  });
103
16
  }
@@ -183,15 +96,6 @@ export function getPatientUuidMapFromFhirPatient(patient: fhir.Patient): Patient
183
96
  };
184
97
  }
185
98
 
186
- export function getPatientIdentifiersFromFhirPatient(patient: fhir.Patient): Array<PatientIdentifier> {
187
- return patient.identifier.map((identifier) => {
188
- return {
189
- uuid: identifier.id,
190
- identifier: identifier.value,
191
- };
192
- });
193
- }
194
-
195
99
  export function getPhonePersonAttributeValueFromFhirPatient(patient: fhir.Patient) {
196
100
  const result = {};
197
101
  if (patient.telecom) {
@@ -4,7 +4,7 @@ import { Button, InlineLoading, Link } from '@carbon/react';
4
4
  import { XAxis } from '@carbon/react/icons';
5
5
  import { useLocation, useParams } from 'react-router-dom';
6
6
  import { useTranslation } from 'react-i18next';
7
- import { Form, Formik, type FormikHelpers } from 'formik';
7
+ import { Form, Formik, type FormikHelpers, type FormikErrors } from 'formik';
8
8
  import {
9
9
  createErrorHandler,
10
10
  interpolateUrl,
@@ -60,7 +60,10 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
60
60
 
61
61
  const location = currentSession?.sessionLocation?.uuid;
62
62
  const inEditMode = isLoadingPatientToEdit ? undefined : !!(uuidOfPatientToEdit && patientToEdit);
63
- const showDummyData = useMemo(() => localStorage.getItem('openmrs:devtools') === 'true' && !inEditMode, [inEditMode]);
63
+ const showDummyDataInput = useMemo(
64
+ () => localStorage.getItem('openmrs:devtools') === 'true' && !inEditMode,
65
+ [inEditMode],
66
+ );
64
67
  const { data: photo } = usePatientPhoto(patientToEdit?.id);
65
68
  const savePatientTransactionManager = useRef(new SavePatientTransactionManager());
66
69
  const validationSchema = getValidationSchema(config, t);
@@ -73,8 +76,8 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
73
76
  return config.sections
74
77
  .map(
75
78
  (sectionName) =>
76
- config.sectionDefinitions.filter((s) => s.id == sectionName)[0] ??
77
- builtInSections.filter((s) => s.id == sectionName)[0],
79
+ config.sectionDefinitions.filter((s) => s.id === sectionName)[0] ??
80
+ builtInSections.filter((s) => s.id === sectionName)[0],
78
81
  )
79
82
  .filter((s) => s);
80
83
  }, [config.sections, config.sectionDefinitions]);
@@ -144,7 +147,7 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
144
147
  }
145
148
  };
146
149
 
147
- const getDescription = (errors) => {
150
+ const getDescription = (errors: FormikErrors<FormValues>): JSX.Element => {
148
151
  return (
149
152
  <ul style={{ listStyle: 'inside' }}>
150
153
  {Object.keys(errors).map((error, index) => {
@@ -154,7 +157,7 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
154
157
  );
155
158
  };
156
159
 
157
- const displayErrors = (errors) => {
160
+ const displayErrors = (errors: FormikErrors<FormValues>): void => {
158
161
  if (errors && typeof errors === 'object' && !!Object.keys(errors).length) {
159
162
  showSnackbar({
160
163
  isLowContrast: true,
@@ -200,47 +203,48 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
200
203
  <Form className={styles.form}>
201
204
  <BeforeSavePrompt when={Object.keys(props.touched).length > 0} redirect={target} />
202
205
  <div className={styles.formContainer}>
203
- <div>
204
- <div className={styles.stickyColumn}>
205
- <h4>
206
- {inEditMode
207
- ? t('editPatientDetails', 'Edit patient details')
208
- : t('createNewPatient', 'Create new patient')}
209
- </h4>
210
- {showDummyData && <DummyDataInput setValues={props.setValues} />}
211
- <p className={styles.label01}>{t('jumpTo', 'Jump to')}</p>
212
- {sections.map((section) => (
213
- <div className={classNames(styles.space05, styles.touchTarget)} key={section.name}>
214
- <Link className={styles.linkName} onClick={() => scrollIntoView(section.id)}>
215
- <XAxis size={16} /> {t(`${section.id}Section`, section.name)}
216
- </Link>
217
- </div>
218
- ))}
219
- <Button
220
- className={styles.submitButton}
221
- type="submit"
222
- onClick={() => props.validateForm().then((errors) => displayErrors(errors))}
223
- // Current session and identifiers are required for patient registration.
224
- // If currentSession or identifierTypes are not available, then the
225
- // user should be blocked to register the patient.
226
- disabled={!currentSession || !identifierTypes || props.isSubmitting}>
227
- {props.isSubmitting ? (
228
- <InlineLoading
229
- className={styles.spinner}
230
- description={`${t('submitting', 'Submitting')} ...`}
231
- iconDescription="submitting"
232
- />
233
- ) : inEditMode ? (
234
- t('updatePatient', 'Update patient')
235
- ) : (
236
- t('registerPatient', 'Register patient')
237
- )}
238
- </Button>
239
- <Button className={styles.cancelButton} kind="secondary" onClick={cancelRegistration}>
240
- {t('cancel', 'Cancel')}
241
- </Button>
242
- </div>
206
+ {/* Navigation Sidebar */}
207
+ <div className={styles.stickyColumn}>
208
+ <h4>
209
+ {inEditMode
210
+ ? t('editPatientDetails', 'Edit patient details')
211
+ : t('createNewPatient', 'Create new patient')}
212
+ </h4>
213
+ {showDummyDataInput && <DummyDataInput setValues={props.setValues} />}
214
+ <p className={styles.label01}>{t('jumpTo', 'Jump to')}</p>
215
+ {sections.map((section) => (
216
+ <div className={classNames(styles.space05, styles.touchTarget)} key={section.name}>
217
+ <Link className={styles.linkName} onClick={() => scrollIntoView(section.id)}>
218
+ <XAxis size={16} /> {t(`${section.id}Section`, section.name)}
219
+ </Link>
220
+ </div>
221
+ ))}
222
+ <hr className={styles.divider} />
223
+ <Button
224
+ className={styles.submitButton}
225
+ type="submit"
226
+ onClick={() => props.validateForm().then((errors) => displayErrors(errors))}
227
+ // Current session and identifiers are required for patient registration.
228
+ // If currentSession or identifierTypes are not available, then the
229
+ // user should be blocked to register the patient.
230
+ disabled={!currentSession || !identifierTypes || props.isSubmitting}>
231
+ {props.isSubmitting ? (
232
+ <InlineLoading
233
+ className={styles.spinner}
234
+ description={`${t('submitting', 'Submitting')} ...`}
235
+ iconDescription="submitting"
236
+ />
237
+ ) : inEditMode ? (
238
+ t('updatePatient', 'Update patient')
239
+ ) : (
240
+ t('registerPatient', 'Register patient')
241
+ )}
242
+ </Button>
243
+ <Button className={styles.cancelButton} kind="secondary" onClick={cancelRegistration}>
244
+ {t('cancel', 'Cancel')}
245
+ </Button>
243
246
  </div>
247
+ {/* Registration Form */}
244
248
  <div className={styles.infoGrid}>
245
249
  <PatientRegistrationContextProvider value={createContextValue(props)}>
246
250
  {sections.map((section, index) => (
@@ -1,21 +1,18 @@
1
1
  import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
+ import { vi, describe, it, expect } from 'vitest';
2
3
  import { savePatient } from './patient-registration.resource';
3
4
 
4
- const mockOpenmrsFetch = openmrsFetch as jest.Mock;
5
-
6
- jest.mock('@openmrs/esm-framework', () => ({
7
- openmrsFetch: jest.fn(),
8
- }));
5
+ const mockOpenmrsFetch = vi.mocked(openmrsFetch);
9
6
 
10
7
  describe('savePatient', () => {
11
8
  it('appends patient uuid in url if provided', () => {
12
- mockOpenmrsFetch.mockImplementationOnce((url) => url);
9
+ mockOpenmrsFetch.mockImplementationOnce(() => Promise.resolve({} as any));
13
10
  savePatient(null, '1234');
14
11
  expect(mockOpenmrsFetch.mock.calls[0][0]).toEqual(`${restBaseUrl}/patient/1234`);
15
12
  });
16
13
 
17
14
  it('does not append patient uuid in url', () => {
18
- mockOpenmrsFetch.mockImplementationOnce(() => {});
15
+ mockOpenmrsFetch.mockImplementationOnce(() => Promise.resolve({} as any));
19
16
  savePatient(null);
20
17
  expect(mockOpenmrsFetch.mock.calls[0][0]).toEqual(`${restBaseUrl}/patient/`);
21
18
  });
@@ -1,9 +1,6 @@
1
+ import dayjs from 'dayjs';
1
2
  import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
3
  import { type Patient, type Relationship, type PatientIdentifier, type Encounter } from './patient-registration.types';
3
- import dayjs from 'dayjs';
4
-
5
- export const uuidIdentifier = '05a29f94-c0ed-11e2-94be-8c13b969e334';
6
- export const uuidTelephoneNumber = '14d4f066-15f5-102d-96e4-000c29c2a5d7';
7
4
 
8
5
  function dataURItoFile(dataURI: string) {
9
6
  const byteString = window.atob(dataURI.split(',')[1]);
@@ -45,16 +45,29 @@
45
45
  margin: layout.$spacing-05 0;
46
46
  }
47
47
 
48
+ .divider {
49
+ width: 100%;
50
+ height: 1px;
51
+ margin: layout.$spacing-07 0;
52
+ border: none;
53
+ background-color: $ui-03;
54
+ }
55
+
48
56
  .formContainer {
49
57
  display: flex;
50
58
  width: 100%;
59
+ align-items: flex-start;
60
+ height: calc(100dvh - var(--omrs-topnav-height, 3rem)); // subtract nav height
61
+ overflow-y: auto;
62
+ scrollbar-width: none;
63
+ &::-webkit-scrollbar {
64
+ display: none;
65
+ }
51
66
  }
52
67
 
53
68
  .stickyColumn {
54
69
  position: sticky;
55
- margin-top: layout.$spacing-05;
56
- // layout.$spacing-09 for the nav height and layout.$spacing-05 for top margin
57
- top: layout.$spacing-10;
70
+ top: layout.$spacing-05;
58
71
  }
59
72
 
60
73
  .touchTarget a:active {