@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
@@ -0,0 +1,199 @@
1
+ import React from 'react';
2
+ import { vi, describe, it, expect, beforeEach, afterEach, type Mock, type MockInstance } from 'vitest';
3
+ import { render, waitFor } from '@testing-library/react';
4
+ import { showModal, navigate } from '@openmrs/esm-framework';
5
+ import BeforeSavePrompt from './before-save-prompt.component';
6
+
7
+ const mockShowModal = vi.mocked(showModal);
8
+ const mockNavigate = vi.mocked(navigate);
9
+
10
+ const mockGetOpenmrsSpaBase = vi.fn(() => '/openmrs/spa');
11
+ window['getOpenmrsSpaBase'] = mockGetOpenmrsSpaBase;
12
+
13
+ /**
14
+ * Helper to render BeforeSavePrompt component.
15
+ */
16
+ function renderBeforeSavePrompt(when: boolean, redirect?: string) {
17
+ return render(<BeforeSavePrompt when={when} redirect={redirect} />);
18
+ }
19
+
20
+ describe('BeforeSavePrompt component', () => {
21
+ let addEventListenerSpy: MockInstance;
22
+ let removeEventListenerSpy: MockInstance;
23
+ let disposeMock: Mock;
24
+
25
+ beforeEach(() => {
26
+ disposeMock = vi.fn();
27
+ mockShowModal.mockReturnValue(disposeMock);
28
+ mockNavigate.mockClear();
29
+
30
+ // Spy on window.addEventListener and removeEventListener
31
+ addEventListenerSpy = vi.spyOn(window, 'addEventListener');
32
+ removeEventListenerSpy = vi.spyOn(window, 'removeEventListener');
33
+ });
34
+
35
+ afterEach(() => {
36
+ addEventListenerSpy.mockRestore();
37
+ removeEventListenerSpy.mockRestore();
38
+ });
39
+
40
+ describe('Event listener setup', () => {
41
+ it('does not set up event listeners when when is false', () => {
42
+ renderBeforeSavePrompt(false);
43
+
44
+ expect(addEventListenerSpy).not.toHaveBeenCalled();
45
+ });
46
+
47
+ it('sets up beforeunload and navigation event listeners when when is true', () => {
48
+ renderBeforeSavePrompt(true);
49
+
50
+ expect(addEventListenerSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function));
51
+ expect(addEventListenerSpy).toHaveBeenCalledWith('single-spa:before-routing-event', expect.any(Function));
52
+ });
53
+
54
+ it('removes event listeners on unmount', () => {
55
+ const { unmount } = renderBeforeSavePrompt(true);
56
+
57
+ unmount();
58
+
59
+ expect(removeEventListenerSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function));
60
+ expect(removeEventListenerSpy).toHaveBeenCalledWith('single-spa:before-routing-event', expect.any(Function));
61
+ });
62
+
63
+ it('does not set up listeners when redirect is provided', () => {
64
+ renderBeforeSavePrompt(true, '/some/path');
65
+
66
+ expect(addEventListenerSpy).not.toHaveBeenCalled();
67
+ });
68
+ });
69
+
70
+ describe('Beforeunload event handling', () => {
71
+ it('prevents default and sets returnValue on beforeunload event', () => {
72
+ renderBeforeSavePrompt(true);
73
+
74
+ const beforeunloadHandler = addEventListenerSpy.mock.calls.find((call) => call[0] === 'beforeunload')?.[1] as (
75
+ e: BeforeUnloadEvent,
76
+ ) => string;
77
+
78
+ const mockEvent = {
79
+ preventDefault: vi.fn(),
80
+ returnValue: '',
81
+ } as unknown as BeforeUnloadEvent;
82
+
83
+ const result = beforeunloadHandler(mockEvent);
84
+
85
+ expect(mockEvent.preventDefault).toHaveBeenCalled();
86
+ expect(mockEvent.returnValue).toBeTruthy();
87
+ expect(result).toBeTruthy();
88
+ });
89
+ });
90
+
91
+ describe('Navigation event handling', () => {
92
+ it('shows modal and cancels navigation on single-spa routing event', () => {
93
+ renderBeforeSavePrompt(true);
94
+
95
+ const navigationHandler = addEventListenerSpy.mock.calls.find(
96
+ (call) => call[0] === 'single-spa:before-routing-event',
97
+ )?.[1] as (e: CustomEvent) => void;
98
+
99
+ const mockCancelNavigation = vi.fn();
100
+ const mockEvent = {
101
+ detail: {
102
+ newUrl: '/openmrs/spa/new-page',
103
+ navigationIsCanceled: false,
104
+ cancelNavigation: mockCancelNavigation,
105
+ },
106
+ } as unknown as CustomEvent;
107
+
108
+ navigationHandler(mockEvent);
109
+
110
+ expect(mockCancelNavigation).toHaveBeenCalled();
111
+ expect(mockShowModal).toHaveBeenCalledWith(
112
+ 'cancel-patient-edit-modal',
113
+ expect.objectContaining({
114
+ onConfirm: expect.any(Function),
115
+ }),
116
+ expect.any(Function),
117
+ );
118
+ });
119
+
120
+ it('does not show modal if navigation is already canceled', () => {
121
+ renderBeforeSavePrompt(true);
122
+
123
+ const navigationHandler = addEventListenerSpy.mock.calls.find(
124
+ (call) => call[0] === 'single-spa:before-routing-event',
125
+ )?.[1] as (e: CustomEvent) => void;
126
+
127
+ const mockEvent = {
128
+ detail: {
129
+ newUrl: '/openmrs/spa/new-page',
130
+ navigationIsCanceled: true,
131
+ cancelNavigation: vi.fn(),
132
+ },
133
+ } as unknown as CustomEvent;
134
+
135
+ navigationHandler(mockEvent);
136
+
137
+ expect(mockShowModal).not.toHaveBeenCalled();
138
+ });
139
+
140
+ it('navigates to target URL when modal is confirmed', async () => {
141
+ renderBeforeSavePrompt(true);
142
+
143
+ const navigationHandler = addEventListenerSpy.mock.calls.find(
144
+ (call) => call[0] === 'single-spa:before-routing-event',
145
+ )?.[1] as (e: CustomEvent) => void;
146
+
147
+ const mockEvent = {
148
+ detail: {
149
+ newUrl: '/openmrs/spa/new-page',
150
+ navigationIsCanceled: false,
151
+ cancelNavigation: vi.fn(),
152
+ },
153
+ } as unknown as CustomEvent;
154
+
155
+ // Call navigationHandler directly - it triggers showModal synchronously
156
+ navigationHandler(mockEvent);
157
+
158
+ // Get the onConfirm callback from showModal call
159
+ // showModal is called with: (modalName, props, onClose)
160
+ const modalProps = mockShowModal.mock.calls[0]?.[1] as { onConfirm?: () => void } | undefined;
161
+ const onConfirm = modalProps?.onConfirm;
162
+
163
+ expect(onConfirm).toBeDefined();
164
+
165
+ // Call onConfirm directly - waitFor will handle async updates
166
+ onConfirm?.();
167
+
168
+ await waitFor(() => {
169
+ expect(mockNavigate).toHaveBeenCalledWith({
170
+ to: expect.stringContaining('/new-page'),
171
+ });
172
+ });
173
+ expect(disposeMock).toHaveBeenCalled();
174
+ });
175
+ });
176
+
177
+ describe('Redirect handling', () => {
178
+ it('navigates to redirect URL when redirect prop is provided', async () => {
179
+ renderBeforeSavePrompt(true, '/openmrs/spa/target-page');
180
+
181
+ await waitFor(() => {
182
+ expect(mockNavigate).toHaveBeenCalledWith({
183
+ to: expect.stringContaining('/target-page'),
184
+ });
185
+ });
186
+ });
187
+
188
+ it('handles redirect with full URL', async () => {
189
+ mockGetOpenmrsSpaBase.mockReturnValue('/openmrs/spa');
190
+ renderBeforeSavePrompt(true, '/openmrs/spa/target-page');
191
+
192
+ await waitFor(() => {
193
+ expect(mockNavigate).toHaveBeenCalledWith({
194
+ to: expect.stringContaining('/target-page'),
195
+ });
196
+ });
197
+ });
198
+ });
199
+ });
@@ -1,11 +1,12 @@
1
+ import { vi } from 'vitest';
1
2
  import { type ConceptResponse } from '../../patient-registration.types';
2
3
 
3
- export const useConcept = jest.fn(function mockUseConceptImplementation(uuid: string): {
4
+ export const useConcept = vi.fn(function mockUseConceptImplementation(uuid: string): {
4
5
  data: ConceptResponse;
5
6
  isLoading: boolean;
6
7
  } {
7
8
  let data;
8
- if (uuid == 'weight-uuid') {
9
+ if (uuid === 'weight-uuid') {
9
10
  data = {
10
11
  uuid: 'weight-uuid',
11
12
  display: 'Weight (kg)',
@@ -13,7 +14,7 @@ export const useConcept = jest.fn(function mockUseConceptImplementation(uuid: st
13
14
  answers: [],
14
15
  setMembers: [],
15
16
  };
16
- } else if (uuid == 'chief-complaint-uuid') {
17
+ } else if (uuid === 'chief-complaint-uuid') {
17
18
  data = {
18
19
  uuid: 'chief-complaint-uuid',
19
20
  display: 'Chief Complaint',
@@ -21,7 +22,7 @@ export const useConcept = jest.fn(function mockUseConceptImplementation(uuid: st
21
22
  answers: [],
22
23
  setMembers: [],
23
24
  };
24
- } else if (uuid == 'nationality-uuid') {
25
+ } else if (uuid === 'nationality-uuid') {
25
26
  data = {
26
27
  uuid: 'nationality-uuid',
27
28
  display: 'Nationality',
@@ -39,8 +40,8 @@ export const useConcept = jest.fn(function mockUseConceptImplementation(uuid: st
39
40
  };
40
41
  });
41
42
 
42
- export const useConceptAnswers = jest.fn((uuid: string) => {
43
- if (uuid == 'nationality-uuid') {
43
+ export const useConceptAnswers = vi.fn((uuid: string) => {
44
+ if (uuid === 'nationality-uuid') {
44
45
  return {
45
46
  data: [
46
47
  { display: 'USA', uuid: 'usa' },
@@ -48,7 +49,7 @@ export const useConceptAnswers = jest.fn((uuid: string) => {
48
49
  ],
49
50
  isLoading: false,
50
51
  };
51
- } else if (uuid == 'other-countries-uuid') {
52
+ } else if (uuid === 'other-countries-uuid') {
52
53
  return {
53
54
  data: [
54
55
  { display: 'Kenya', uuid: 'ke' },
@@ -9,11 +9,12 @@ import {
9
9
  usePatientRegistrationContext,
10
10
  } from '../../patient-registration-context';
11
11
  import { ResourcesContextProvider, useResourcesContext } from '../../../resources-context';
12
+ import { type AddressTemplate } from '../../patient-registration.types';
12
13
  import { Input } from '../../input/basic-input/input/input.component';
13
14
  import AddressHierarchyLevels from './address-hierarchy-levels.component';
14
15
  import AddressSearchComponent from './address-search.component';
15
16
  import styles from '../field.scss';
16
- import { type AddressTemplate } from '../../patient-registration.types';
17
+
17
18
 
18
19
  export const AddressComponent: React.FC = () => {
19
20
  const config = useConfig();
@@ -39,8 +40,6 @@ export const AddressComponent: React.FC = () => {
39
40
 
40
41
  const allFields = addressTemplate?.lines?.flat();
41
42
  const fields = allFields?.filter(({ isToken }) => isToken === 'IS_ADDR_TOKEN');
42
-
43
- // Use config-based required fields if provided, otherwise fall back to backend template
44
43
  const allRequiredFields = requiredAddressFields?.length
45
44
  ? Object.fromEntries(requiredAddressFields.map((field) => [field, field]))
46
45
  : Object.fromEntries(addressTemplate?.requiredElements?.map((curr) => [curr, curr]) || []);
@@ -80,10 +79,9 @@ export const AddressComponent: React.FC = () => {
80
79
  if (addressTemplate && !Object.keys(addressTemplate)?.length) {
81
80
  return (
82
81
  <AddressComponentContainer>
83
- <SkeletonText
84
- // @ts-expect-error
85
- role="progressbar"
86
- />
82
+ <div role="progressbar" aria-label={t('loading', 'Loading')}>
83
+ <SkeletonText />
84
+ </div>
87
85
  </AddressComponentContainer>
88
86
  );
89
87
  }
@@ -108,10 +106,9 @@ export const AddressComponent: React.FC = () => {
108
106
  if (isLoadingFieldOrder) {
109
107
  return (
110
108
  <AddressComponentContainer>
111
- <SkeletonText
112
- // @ts-expect-error
113
- role="progressbar"
114
- />
109
+ <div role="progressbar" aria-label={t('loading', 'Loading')}>
110
+ <SkeletonText />
111
+ </div>
115
112
  </AddressComponentContainer>
116
113
  );
117
114
  }
@@ -156,7 +153,7 @@ const AddressComponentContainer = ({ children }) => {
156
153
  () =>
157
154
  ({
158
155
  fieldConfigurations: {},
159
- setFieldValue: () => {},
156
+ setFieldValue: async () => {},
160
157
  values: {},
161
158
  }) as unknown as PatientRegistrationContextProps,
162
159
  [],
@@ -168,7 +165,7 @@ const AddressComponentContainer = ({ children }) => {
168
165
  addressTemplate: {} as AddressTemplate,
169
166
  currentSession: {} as Session,
170
167
  identifierTypes: [],
171
- relationshipTypes: [],
168
+ relationshipTypes: { results: [] },
172
169
  }}>
173
170
  <PatientRegistrationContextProvider value={contextValue}>
174
171
  <div>
@@ -22,7 +22,10 @@ const AddressComboBox: React.FC<AddressComboBoxProps> = ({ attribute }) => {
22
22
  const { t } = useTranslation();
23
23
  const [field, meta, { setValue }] = useField(`address.${attribute.name}`);
24
24
  const { fetchEntriesForField, searchString, updateChildElements } = useAddressEntryFetchConfig(attribute.name);
25
- const { entries } = useAddressEntries(fetchEntriesForField, searchString);
25
+ const { entries, isLoadingAddressEntries, errorFetchingAddressEntries } = useAddressEntries(
26
+ fetchEntriesForField,
27
+ searchString,
28
+ );
26
29
  const label = t(attribute.label) + (attribute?.required ? '' : ` (${t('optional', 'optional')})`);
27
30
 
28
31
  const handleInputChange = useCallback(
@@ -45,6 +48,8 @@ const AddressComboBox: React.FC<AddressComboBoxProps> = ({ attribute }) => {
45
48
  return (
46
49
  <ComboInput
47
50
  entries={entries}
51
+ error={errorFetchingAddressEntries}
52
+ isLoading={isLoadingAddressEntries}
48
53
  handleSelection={handleSelection}
49
54
  name={`address.${attribute.name}`}
50
55
  fieldProps={{