@olaboot/esm-patient-registration-app 9.2.1 → 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.
- package/dist/1339.js +1 -0
- package/dist/1339.js.map +1 -0
- package/dist/1480.js +1 -0
- package/dist/1480.js.map +1 -0
- package/dist/1646.js +1 -0
- package/dist/1646.js.map +1 -0
- package/dist/1789.js +1 -0
- package/dist/1789.js.map +1 -0
- package/dist/1869.js +1 -0
- package/dist/1869.js.map +1 -0
- package/dist/1877.js +1 -0
- package/dist/1877.js.map +1 -0
- package/dist/2317.js +1 -0
- package/dist/2317.js.map +1 -0
- package/dist/2416.js +1 -0
- package/dist/2416.js.map +1 -0
- package/dist/2747.js +1 -0
- package/dist/2747.js.map +1 -0
- package/dist/282.js +1 -0
- package/dist/282.js.map +1 -0
- package/dist/2881.js +1 -0
- package/dist/2881.js.map +1 -0
- package/dist/3378.js +1 -0
- package/dist/3378.js.map +1 -0
- package/dist/3720.js +1 -0
- package/dist/3720.js.map +1 -0
- package/dist/3906.js +1 -0
- package/dist/3906.js.map +1 -0
- package/dist/3963.js +1 -0
- package/dist/3963.js.map +1 -0
- package/dist/3989.js +1 -0
- package/dist/3989.js.map +1 -0
- package/dist/4106.js +1 -0
- package/dist/4106.js.map +1 -0
- package/dist/4111.js +1 -0
- package/dist/4111.js.map +1 -0
- package/dist/434.js +1 -0
- package/dist/434.js.map +1 -0
- package/dist/4348.js +1 -0
- package/dist/4348.js.map +1 -0
- package/dist/4383.js +1 -0
- package/dist/4383.js.map +1 -0
- package/dist/4658.js +1 -0
- package/dist/4658.js.map +1 -0
- package/dist/466.js +1 -0
- package/dist/466.js.map +1 -0
- package/dist/4928.js +1 -0
- package/dist/4928.js.map +1 -0
- package/dist/5117.js +1 -0
- package/dist/5117.js.map +1 -0
- package/dist/5132.js +1 -0
- package/dist/5132.js.map +1 -0
- package/dist/5145.js +1 -0
- package/dist/5145.js.map +1 -0
- package/dist/5208.js +43 -0
- package/dist/5208.js.map +1 -0
- package/dist/527.js +1 -0
- package/dist/527.js.map +1 -0
- package/dist/5280.js +1 -0
- package/dist/5280.js.map +1 -0
- package/dist/5338.js +6 -0
- package/dist/5338.js.map +1 -0
- package/dist/5503.js +1 -0
- package/dist/5503.js.map +1 -0
- package/dist/555.js +1 -0
- package/dist/555.js.map +1 -0
- package/dist/556.js +1 -0
- package/dist/556.js.map +1 -0
- package/dist/5644.js +1 -0
- package/dist/5644.js.map +1 -0
- package/dist/5697.js +1 -0
- package/dist/{4024.js.map → 5697.js.map} +1 -1
- package/dist/5940.js +1 -0
- package/dist/5940.js.map +1 -0
- package/dist/6047.js +1 -0
- package/dist/6047.js.map +1 -0
- package/dist/6371.js +1 -0
- package/dist/6371.js.map +1 -0
- package/dist/6377.js +1 -0
- package/dist/6377.js.map +1 -0
- package/dist/6388.js +1 -0
- package/dist/6388.js.map +1 -0
- package/dist/6444.js +1 -0
- package/dist/6444.js.map +1 -0
- package/dist/6508.js +1 -0
- package/dist/6508.js.map +1 -0
- package/dist/6724.js +1 -0
- package/dist/6724.js.map +1 -0
- package/dist/689.js +1 -0
- package/dist/689.js.map +1 -0
- package/dist/6904.js +1 -0
- package/dist/6904.js.map +1 -0
- package/dist/7045.js +1 -0
- package/dist/7045.js.map +1 -0
- package/dist/7175.js +1 -0
- package/dist/7175.js.map +1 -0
- package/dist/7182.js +1 -0
- package/dist/7182.js.map +1 -0
- package/dist/7649.js +1 -0
- package/dist/7649.js.map +1 -0
- package/dist/7742.js +1 -0
- package/dist/7742.js.map +1 -0
- package/dist/7912.js +1 -0
- package/dist/7912.js.map +1 -0
- package/dist/8358.js +1 -0
- package/dist/8358.js.map +1 -0
- package/dist/8359.js +1 -0
- package/dist/8359.js.map +1 -0
- package/dist/8695.js +1 -0
- package/dist/8695.js.map +1 -0
- package/dist/903.js +1 -0
- package/dist/903.js.map +1 -0
- package/dist/9061.js +1 -0
- package/dist/9061.js.map +1 -0
- package/dist/9072.js +1 -0
- package/dist/9072.js.map +1 -0
- package/dist/9397.js +1 -0
- package/dist/9397.js.map +1 -0
- package/dist/9712.js +1 -0
- package/dist/9712.js.map +1 -0
- package/dist/9771.js +1 -0
- package/dist/9771.js.map +1 -0
- package/dist/9806.js +1 -0
- package/dist/9806.js.map +1 -0
- package/dist/9816.js +1 -0
- package/dist/9816.js.map +1 -0
- package/dist/main.js +7 -6
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-registration-app.js +6 -0
- package/dist/{olaboot-esm-patient-registration-app.js.buildmanifest.json → openmrs-esm-patient-registration-app.js.buildmanifest.json} +540 -455
- package/dist/openmrs-esm-patient-registration-app.js.map +1 -0
- package/dist/routes.json +1 -1
- package/package.json +8 -9
- package/src/add-patient-link.extension.tsx +3 -2
- package/src/add-patient-link.test.tsx +2 -1
- package/src/config-schema.ts +507 -507
- package/src/constants.ts +1 -1
- package/src/index.ts +2 -24
- package/src/nav-link.test.tsx +1 -0
- package/src/offline.resources.ts +97 -31
- package/src/patient-registration/before-save-prompt.test.tsx +199 -0
- package/src/patient-registration/field/__mocks__/field.resource.ts +8 -7
- package/src/patient-registration/field/address/address-field.component.tsx +183 -186
- package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +6 -1
- package/src/patient-registration/field/address/address-hierarchy.test.tsx +289 -296
- package/src/patient-registration/field/address/address-search.component.tsx +20 -8
- package/src/patient-registration/field/address/address-search.scss +19 -2
- package/src/patient-registration/field/address/address-search.test.tsx +333 -141
- package/src/patient-registration/field/address/custom-address-field.component.tsx +1 -1
- package/src/patient-registration/field/cause-of-death/cause-of-death.component.tsx +1 -1
- package/src/patient-registration/field/cause-of-death/cause-of-death.test.tsx +251 -0
- package/src/patient-registration/field/custom-field.component.tsx +1 -1
- package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +1 -1
- package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.test.tsx +144 -0
- package/src/patient-registration/field/dob/dob.component.tsx +2 -2
- package/src/patient-registration/field/dob/dob.test.tsx +370 -54
- package/src/patient-registration/field/field.component.tsx +1 -1
- package/src/patient-registration/field/field.resource.ts +2 -2
- package/src/patient-registration/field/field.test.tsx +333 -330
- package/src/patient-registration/field/gender/gender-field.test.tsx +240 -54
- package/src/patient-registration/field/id/id-field.component.tsx +146 -144
- package/src/patient-registration/field/id/id-field.test.tsx +177 -121
- package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +200 -200
- package/src/patient-registration/field/id/identifier-selection-overlay.test.tsx +346 -0
- package/src/patient-registration/field/name/name-field.component.tsx +2 -2
- package/src/patient-registration/field/name/name-field.test.tsx +282 -0
- package/src/patient-registration/field/obs/obs-field.test.tsx +294 -118
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +172 -108
- package/src/patient-registration/field/person-attributes/location-person-attribute-field.component.tsx +3 -3
- package/src/patient-registration/field/person-attributes/location-person-attribute-field.resource.tsx +2 -5
- package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +2 -2
- package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +249 -131
- package/src/patient-registration/field/person-attributes/person-attributes.resource.ts +1 -1
- package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +98 -70
- package/src/patient-registration/field/phone/phone-field.test.tsx +100 -0
- package/src/patient-registration/form-manager.test.ts +6 -5
- package/src/patient-registration/form-manager.ts +5 -2
- package/src/patient-registration/input/basic-input/input/input.component.tsx +3 -121
- package/src/patient-registration/input/basic-input/input/input.test.tsx +151 -51
- package/src/patient-registration/input/basic-input/select/select-input.test.tsx +113 -33
- package/src/patient-registration/input/combo-input/combo-input.component.tsx +60 -24
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +10 -101
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +144 -108
- package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +241 -177
- package/src/patient-registration/input/custom-input/identifier/utils.test.ts +47 -8
- package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +12 -12
- package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +52 -20
- package/src/patient-registration/input/input.scss +1 -2
- package/src/patient-registration/patient-registration-context.ts +5 -3
- package/src/patient-registration/patient-registration-hooks.ts +4 -12
- package/src/patient-registration/patient-registration-utils.test.ts +2 -1
- package/src/patient-registration/patient-registration-utils.ts +2 -98
- package/src/patient-registration/patient-registration.component.tsx +50 -46
- package/src/patient-registration/patient-registration.resource.test.tsx +4 -7
- package/src/patient-registration/patient-registration.resource.ts +1 -4
- package/src/patient-registration/patient-registration.scss +16 -3
- package/src/patient-registration/patient-registration.test.tsx +614 -580
- package/src/patient-registration/patient-registration.types.ts +17 -28
- package/src/patient-registration/section/death-info/death-info-section.test.tsx +130 -34
- package/src/patient-registration/section/demographics/demographics-section.test.tsx +122 -68
- package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +15 -15
- package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +278 -84
- package/src/patient-registration/section/section-wrapper.component.tsx +1 -1
- package/src/patient-registration/ui-components/overlay/overlay.test.tsx +104 -0
- package/src/patient-registration/validation/patient-registration-validation.test.ts +2 -1
- package/src/patient-registration/validation/patient-registration-validation.ts +9 -3
- package/src/root.component.tsx +2 -5
- package/src/widgets/cancel-patient-edit.test.tsx +48 -11
- package/src/widgets/delete-identifier-confirmation.test.tsx +77 -24
- package/src/widgets/edit-patient-details-button.component.tsx +14 -18
- package/src/widgets/edit-patient-details-button.scss +2 -2
- package/src/widgets/edit-patient-details-button.test.tsx +11 -13
- package/translations/am.json +9 -4
- package/translations/ar.json +9 -4
- package/translations/ar_SY.json +9 -4
- package/translations/bn.json +9 -4
- package/translations/cs.json +9 -4
- package/translations/de.json +120 -115
- package/translations/en.json +10 -4
- package/translations/en_US.json +9 -4
- package/translations/es.json +9 -4
- package/translations/es_MX.json +9 -4
- package/translations/fr.json +9 -4
- package/translations/he.json +9 -4
- package/translations/hi.json +9 -4
- package/translations/hi_IN.json +9 -4
- package/translations/id.json +9 -4
- package/translations/it.json +9 -4
- package/translations/ka.json +9 -4
- package/translations/km.json +9 -4
- package/translations/ku.json +9 -4
- package/translations/ky.json +9 -4
- package/translations/lg.json +9 -4
- package/translations/ne.json +9 -4
- package/translations/pl.json +9 -4
- package/translations/pt.json +9 -4
- package/translations/pt_BR.json +10 -5
- package/translations/qu.json +9 -4
- package/translations/ro_RO.json +9 -4
- package/translations/ru_RU.json +9 -4
- package/translations/si.json +9 -4
- package/translations/sq.json +9 -4
- package/translations/sw.json +9 -4
- package/translations/sw_KE.json +9 -4
- package/translations/tr.json +9 -4
- package/translations/tr_TR.json +9 -4
- package/translations/uk.json +9 -4
- package/translations/uz.json +9 -4
- package/translations/uz@Latn.json +9 -4
- package/translations/uz_UZ.json +9 -4
- package/translations/vi.json +9 -4
- package/translations/zh.json +50 -45
- package/translations/zh_CN.json +9 -4
- package/translations/zh_TW.json +9 -4
- package/vitest.config.ts +4 -0
- package/dist/126.js +0 -1
- package/dist/15.js +0 -1
- package/dist/1564.js +0 -1
- package/dist/1567.js +0 -1
- package/dist/1845.js +0 -1
- package/dist/1953.js +0 -1
- package/dist/200.js +0 -1
- package/dist/200.js.map +0 -1
- package/dist/215.js +0 -1
- package/dist/2178.js +0 -1
- package/dist/250.js +0 -1
- package/dist/250.js.map +0 -1
- package/dist/2523.js +0 -1
- package/dist/2523.js.map +0 -1
- package/dist/2566.js +0 -1
- package/dist/2586.js +0 -1
- package/dist/2586.js.map +0 -1
- package/dist/2716.js +0 -1
- package/dist/2716.js.map +0 -1
- package/dist/2759.js +0 -1
- package/dist/2821.js +0 -6
- package/dist/2821.js.map +0 -1
- package/dist/3089.js +0 -1
- package/dist/3089.js.map +0 -1
- package/dist/3230.js +0 -1
- package/dist/3441.js +0 -1
- package/dist/3565.js +0 -1
- package/dist/3571.js +0 -1
- package/dist/3571.js.map +0 -1
- package/dist/3746.js +0 -1
- package/dist/3925.js +0 -1
- package/dist/3946.js +0 -1
- package/dist/4024.js +0 -1
- package/dist/4744.js +0 -1
- package/dist/4744.js.map +0 -1
- package/dist/4809.js +0 -1
- package/dist/4894.js +0 -1
- package/dist/4970.js +0 -1
- package/dist/4970.js.map +0 -1
- package/dist/5130.js +0 -1
- package/dist/5187.js +0 -1
- package/dist/5491.js +0 -1
- package/dist/5491.js.map +0 -1
- package/dist/5595.js +0 -1
- package/dist/5961.js +0 -1
- package/dist/6133.js +0 -1
- package/dist/634.js +0 -1
- package/dist/634.js.map +0 -1
- package/dist/6456.js +0 -1
- package/dist/6466.js +0 -1
- package/dist/6613.js +0 -1
- package/dist/6783.js +0 -1
- package/dist/7073.js +0 -38
- package/dist/7073.js.map +0 -1
- package/dist/7154.js +0 -1
- package/dist/7154.js.map +0 -1
- package/dist/7348.js +0 -1
- package/dist/7439.js +0 -1
- package/dist/7439.js.map +0 -1
- package/dist/7543.js +0 -1
- package/dist/7607.js +0 -1
- package/dist/772.js +0 -1
- package/dist/7984.js +0 -1
- package/dist/7984.js.map +0 -1
- package/dist/8538.js +0 -1
- package/dist/8538.js.map +0 -1
- package/dist/8599.js +0 -1
- package/dist/8727.js +0 -1
- package/dist/8847.js +0 -1
- package/dist/9015.js +0 -1
- package/dist/906.js +0 -1
- package/dist/9065.js +0 -1
- package/dist/9182.js +0 -1
- package/dist/9339.js +0 -1
- package/dist/9453.js +0 -1
- package/dist/9833.js +0 -1
- package/dist/9833.js.map +0 -1
- package/dist/9856.js +0 -1
- package/dist/9856.js.map +0 -1
- package/dist/9920.js +0 -1
- package/dist/9938.js +0 -1
- package/dist/9943.js +0 -1
- package/dist/9943.js.map +0 -1
- package/dist/olaboot-esm-patient-registration-app.js +0 -5
- package/dist/olaboot-esm-patient-registration-app.js.map +0 -1
- package/jest.config.js +0 -3
- package/src/resource.ts +0 -12
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { vi, describe, it, expect, beforeEach, type Mock } from 'vitest';
|
|
3
|
+
import { Formik, Form } from 'formik';
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
|
|
6
|
+
import { type RegistrationConfig, esmPatientRegistrationSchema } from '../../../config-schema';
|
|
7
|
+
import { PhoneField } from './phone-field.component';
|
|
8
|
+
import { PatientRegistrationContextProvider } from '../../patient-registration-context';
|
|
9
|
+
import { initialFormValues } from '../../patient-registration.component';
|
|
10
|
+
import { type FormValues } from '../../patient-registration.types';
|
|
11
|
+
|
|
12
|
+
const mockUseConfig = vi.mocked(useConfig<RegistrationConfig>);
|
|
13
|
+
|
|
14
|
+
// Mock PersonAttributeField since PhoneField is a wrapper around it
|
|
15
|
+
vi.mock('../person-attributes/person-attribute-field.component', () => ({
|
|
16
|
+
PersonAttributeField: ({ fieldDefinition }) => (
|
|
17
|
+
<div data-testid="phone-field">
|
|
18
|
+
<label htmlFor="phone-input">Phone</label>
|
|
19
|
+
<input id="phone-input" name="phone" data-field-uuid={fieldDefinition.uuid} />
|
|
20
|
+
</div>
|
|
21
|
+
),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Helper to render PhoneField.
|
|
26
|
+
*/
|
|
27
|
+
const renderPhoneField = () => {
|
|
28
|
+
return render(
|
|
29
|
+
<Formik initialValues={{}} onSubmit={() => {}}>
|
|
30
|
+
<Form>
|
|
31
|
+
<PatientRegistrationContextProvider
|
|
32
|
+
value={{
|
|
33
|
+
identifierTypes: [],
|
|
34
|
+
values: initialFormValues,
|
|
35
|
+
validationSchema: null,
|
|
36
|
+
inEditMode: false,
|
|
37
|
+
setFieldValue: vi.fn().mockResolvedValue(undefined),
|
|
38
|
+
setCapturePhotoProps: vi.fn(),
|
|
39
|
+
setFieldTouched: vi.fn().mockResolvedValue(undefined),
|
|
40
|
+
currentPhoto: '',
|
|
41
|
+
isOffline: false,
|
|
42
|
+
initialFormValues: initialFormValues,
|
|
43
|
+
}}>
|
|
44
|
+
<PhoneField />
|
|
45
|
+
</PatientRegistrationContextProvider>
|
|
46
|
+
</Form>
|
|
47
|
+
</Formik>,
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
describe('PhoneField', () => {
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
mockUseConfig.mockReturnValue({
|
|
54
|
+
...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
|
|
55
|
+
fieldConfigurations: {
|
|
56
|
+
phone: {
|
|
57
|
+
personAttributeUuid: 'phone-uuid-123',
|
|
58
|
+
validation: {
|
|
59
|
+
required: false,
|
|
60
|
+
matches: null,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
} as RegistrationConfig['fieldConfigurations'],
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('renders the phone field', () => {
|
|
68
|
+
renderPhoneField();
|
|
69
|
+
|
|
70
|
+
expect(screen.getByTestId('phone-field')).toBeInTheDocument();
|
|
71
|
+
expect(screen.getByLabelText(/phone/i)).toBeInTheDocument();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('passes correct field definition to PersonAttributeField', () => {
|
|
75
|
+
renderPhoneField();
|
|
76
|
+
|
|
77
|
+
const phoneInput = screen.getByLabelText(/phone/i);
|
|
78
|
+
expect(phoneInput).toHaveAttribute('data-field-uuid', 'phone-uuid-123');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('uses configured person attribute UUID', () => {
|
|
82
|
+
mockUseConfig.mockReturnValue({
|
|
83
|
+
...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
|
|
84
|
+
fieldConfigurations: {
|
|
85
|
+
phone: {
|
|
86
|
+
personAttributeUuid: 'custom-phone-uuid',
|
|
87
|
+
validation: {
|
|
88
|
+
required: false,
|
|
89
|
+
matches: null,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
} as RegistrationConfig['fieldConfigurations'],
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
renderPhoneField();
|
|
96
|
+
|
|
97
|
+
const phoneInput = screen.getByLabelText(/phone/i);
|
|
98
|
+
expect(phoneInput).toHaveAttribute('data-field-uuid', 'custom-phone-uuid');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { FormManager } from './form-manager';
|
|
2
|
+
import { vi, describe, it, expect } from 'vitest';
|
|
2
3
|
import { type FormValues } from './patient-registration.types';
|
|
3
4
|
import { generateIdentifier } from './patient-registration.resource';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
...
|
|
7
|
-
generateIdentifier:
|
|
6
|
+
vi.mock('./patient-registration.resource', async () => ({
|
|
7
|
+
...((await vi.importActual('./patient-registration.resource')) as object),
|
|
8
|
+
generateIdentifier: vi.fn(),
|
|
8
9
|
}));
|
|
9
10
|
|
|
10
|
-
const mockGenerateIdentifier = generateIdentifier
|
|
11
|
+
const mockGenerateIdentifier = vi.mocked(generateIdentifier);
|
|
11
12
|
|
|
12
13
|
const formValues: FormValues = {
|
|
13
14
|
patientUuid: '',
|
|
@@ -76,7 +77,7 @@ describe('FormManager', () => {
|
|
|
76
77
|
it('should generate identifier if it has autoGeneration and manual entry disabled', async () => {
|
|
77
78
|
formValues.identifiers.foo.autoGeneration = true;
|
|
78
79
|
formValues.identifiers.foo.selectedSource.autoGenerationOption.manualEntryEnabled = false;
|
|
79
|
-
mockGenerateIdentifier.mockResolvedValue({ data: { identifier: '10001V' } });
|
|
80
|
+
mockGenerateIdentifier.mockResolvedValue({ data: { identifier: '10001V' } } as any);
|
|
80
81
|
await FormManager.savePatientIdentifiers(true, undefined, formValues.identifiers, {}, 'Nyc');
|
|
81
82
|
expect(mockGenerateIdentifier.mock.calls).toHaveLength(1);
|
|
82
83
|
});
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '@openmrs/esm-framework';
|
|
11
11
|
import { patientRegistration } from '../constants';
|
|
12
12
|
import {
|
|
13
|
+
type AddressProperties,
|
|
13
14
|
type AttributeValue,
|
|
14
15
|
type CapturePhotoProps,
|
|
15
16
|
type Encounter,
|
|
@@ -36,11 +37,13 @@ import {
|
|
|
36
37
|
} from './patient-registration.resource';
|
|
37
38
|
import { type RegistrationConfig } from '../config-schema';
|
|
38
39
|
|
|
40
|
+
type AddressFieldValues = Partial<Record<AddressProperties, string>>;
|
|
41
|
+
|
|
39
42
|
export type SavePatientForm = (
|
|
40
43
|
isNewPatient: boolean,
|
|
41
44
|
values: FormValues,
|
|
42
45
|
patientUuidMap: PatientUuidMapType,
|
|
43
|
-
initialAddressFieldValues:
|
|
46
|
+
initialAddressFieldValues: AddressFieldValues,
|
|
44
47
|
capturePhotoProps: CapturePhotoProps,
|
|
45
48
|
currentLocation: string,
|
|
46
49
|
initialIdentifierValues: FormValues['identifiers'],
|
|
@@ -303,7 +306,7 @@ export class FormManager {
|
|
|
303
306
|
isNewPatient: boolean,
|
|
304
307
|
values: FormValues,
|
|
305
308
|
patientUuidMap: PatientUuidMapType,
|
|
306
|
-
initialAddressFieldValues:
|
|
309
|
+
initialAddressFieldValues: AddressFieldValues,
|
|
307
310
|
identifiers: Array<PatientIdentifier>,
|
|
308
311
|
config?: RegistrationConfig,
|
|
309
312
|
): Patient {
|
|
@@ -3,125 +3,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
3
3
|
import { Layer, TextInput } from '@carbon/react';
|
|
4
4
|
import { useField } from 'formik';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
export interface TextInputProps
|
|
8
|
-
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'defaultValue' | 'id' | 'size' | 'value'> {
|
|
9
|
-
/**
|
|
10
|
-
* Specify an optional className to be applied to the `<input>` node
|
|
11
|
-
*/
|
|
12
|
-
className?: string;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Optionally provide the default value of the `<input>`
|
|
16
|
-
*/
|
|
17
|
-
defaultValue?: string | number;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Specify whether the `<input>` should be disabled
|
|
21
|
-
*/
|
|
22
|
-
disabled?: boolean;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Specify whether to display the character counter
|
|
26
|
-
*/
|
|
27
|
-
enableCounter?: boolean;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Provide text that is used alongside the control label for additional help
|
|
31
|
-
*/
|
|
32
|
-
helperText?: React.ReactNode;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Specify whether you want the underlying label to be visually hidden
|
|
36
|
-
*/
|
|
37
|
-
hideLabel?: boolean;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Specify a custom `id` for the `<input>`
|
|
41
|
-
*/
|
|
42
|
-
id: string;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* `true` to use the inline version.
|
|
46
|
-
*/
|
|
47
|
-
inline?: boolean;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Specify whether the control is currently invalid
|
|
51
|
-
*/
|
|
52
|
-
invalid?: boolean;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Provide the text that is displayed when the control is in an invalid state
|
|
56
|
-
*/
|
|
57
|
-
invalidText?: React.ReactNode;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Provide the text that will be read by a screen reader when visiting this
|
|
61
|
-
* control
|
|
62
|
-
*/
|
|
63
|
-
labelText: React.ReactNode;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* `true` to use the light version. For use on $ui-01 backgrounds only.
|
|
67
|
-
* Don't use this to make tile background color same as container background color.
|
|
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.'
|
|
70
|
-
*/
|
|
71
|
-
light?: boolean;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Max character count allowed for the input. This is needed in order for enableCounter to display
|
|
75
|
-
*/
|
|
76
|
-
maxCount?: number;
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Optionally provide an `onChange` handler that is called whenever `<input>`
|
|
80
|
-
* is updated
|
|
81
|
-
*/
|
|
82
|
-
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Optionally provide an `onClick` handler that is called whenever the
|
|
86
|
-
* `<input>` is clicked
|
|
87
|
-
*/
|
|
88
|
-
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Specify the placeholder attribute for the `<input>`
|
|
92
|
-
*/
|
|
93
|
-
placeholder?: string;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Whether the input should be read-only
|
|
97
|
-
*/
|
|
98
|
-
readOnly?: boolean;
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Specify the size of the Text Input. Currently supports the following:
|
|
102
|
-
*/
|
|
103
|
-
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Specify the type of the `<input>`
|
|
107
|
-
*/
|
|
108
|
-
type?: string;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Specify the value of the `<input>`
|
|
112
|
-
*/
|
|
113
|
-
value?: string | number | undefined;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Specify whether the control is currently in warning state
|
|
117
|
-
*/
|
|
118
|
-
warn?: boolean;
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Provide the text that is displayed when the control is in warning state
|
|
122
|
-
*/
|
|
123
|
-
warnText?: React.ReactNode;
|
|
124
|
-
}
|
|
6
|
+
export type TextInputProps = React.ComponentProps<typeof TextInput>;
|
|
125
7
|
|
|
126
8
|
interface InputProps extends TextInputProps {
|
|
127
9
|
checkWarning?(value: string): string;
|
|
@@ -146,9 +28,9 @@ export const Input: React.FC<InputProps> = ({ checkWarning, ...props }) => {
|
|
|
146
28
|
t('numberInNameDubious')
|
|
147
29
|
t('yearsEstimateRequired')
|
|
148
30
|
t('deathdayIsRequired', 'Death date is required when the patient is marked as deceased.')
|
|
149
|
-
t('
|
|
31
|
+
t('deathdayBeforeBirthday', 'Death date and time cannot be before the birthday')
|
|
150
32
|
t('deathCauseRequired', 'Cause of death is required')
|
|
151
|
-
t('
|
|
33
|
+
t('nonCodedCauseOfDeathRequiredWhenSelected', 'Non-coded cause of death is required')
|
|
152
34
|
*/
|
|
153
35
|
|
|
154
36
|
const value = field.value || '';
|
|
@@ -1,72 +1,172 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { vi, describe, it, expect, test } from 'vitest';
|
|
3
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
3
4
|
import userEvent from '@testing-library/user-event';
|
|
4
5
|
import { Formik, Form } from 'formik';
|
|
5
6
|
import { Input } from './input.component';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return screen.getByLabelText('Text') as HTMLInputElement;
|
|
8
|
+
/**
|
|
9
|
+
* Helper to render Input component with Formik.
|
|
10
|
+
*/
|
|
11
|
+
function renderInput(
|
|
12
|
+
props: {
|
|
13
|
+
name?: string;
|
|
14
|
+
labelText?: string;
|
|
15
|
+
required?: boolean;
|
|
16
|
+
checkWarning?: (value: string) => string | undefined;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
} = {},
|
|
19
|
+
initialValues: Record<string, string> = {},
|
|
20
|
+
) {
|
|
21
|
+
const defaultProps = {
|
|
22
|
+
id: 'text',
|
|
23
|
+
name: 'text',
|
|
24
|
+
labelText: 'Text',
|
|
25
|
+
placeholder: 'Enter text',
|
|
26
|
+
required: false,
|
|
27
|
+
...props,
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
return render(
|
|
31
|
+
<Formik initialValues={{ text: '', ...initialValues }} onSubmit={() => {}}>
|
|
32
|
+
<Form>
|
|
33
|
+
<Input {...defaultProps} />
|
|
34
|
+
</Form>
|
|
35
|
+
</Formik>,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('Input component', () => {
|
|
40
|
+
describe('Rendering', () => {
|
|
41
|
+
it('renders the input field', () => {
|
|
42
|
+
renderInput();
|
|
34
43
|
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
const input = screen.getByLabelText('Text (optional)') as HTMLInputElement;
|
|
45
|
+
expect(input).toBeInTheDocument();
|
|
46
|
+
expect(input.type).toBe('text');
|
|
47
|
+
});
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
it('shows required label when field is required', () => {
|
|
50
|
+
renderInput({ required: true });
|
|
40
51
|
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
expect(screen.getByLabelText('Text')).toBeInTheDocument();
|
|
53
|
+
expect(screen.queryByLabelText('Text (optional)')).not.toBeInTheDocument();
|
|
54
|
+
});
|
|
43
55
|
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
it('shows optional label when field is not required', () => {
|
|
57
|
+
renderInput({ required: false });
|
|
58
|
+
|
|
59
|
+
expect(screen.getByLabelText('Text (optional)')).toBeInTheDocument();
|
|
60
|
+
});
|
|
46
61
|
});
|
|
47
62
|
|
|
48
|
-
|
|
49
|
-
|
|
63
|
+
describe('User interaction', () => {
|
|
64
|
+
it('allows user to type and updates the input value', async () => {
|
|
65
|
+
const user = userEvent.setup();
|
|
66
|
+
renderInput({ required: true });
|
|
67
|
+
|
|
68
|
+
const input = screen.getByLabelText('Text') as HTMLInputElement;
|
|
69
|
+
await user.type(input, 'test value');
|
|
50
70
|
|
|
51
|
-
|
|
52
|
-
|
|
71
|
+
expect(input.value).toBe('test value');
|
|
72
|
+
});
|
|
53
73
|
|
|
54
|
-
|
|
74
|
+
it('updates form values when user types', async () => {
|
|
75
|
+
const user = userEvent.setup();
|
|
76
|
+
let formValues: Record<string, string> = {};
|
|
55
77
|
|
|
56
|
-
|
|
57
|
-
|
|
78
|
+
render(
|
|
79
|
+
<Formik initialValues={{ text: '' }} onSubmit={() => {}}>
|
|
80
|
+
{({ values }) => {
|
|
81
|
+
formValues = values as Record<string, string>;
|
|
82
|
+
return (
|
|
83
|
+
<Form>
|
|
84
|
+
<Input id="text" name="text" labelText="Text" required />
|
|
85
|
+
</Form>
|
|
86
|
+
);
|
|
87
|
+
}}
|
|
88
|
+
</Formik>,
|
|
89
|
+
);
|
|
58
90
|
|
|
59
|
-
|
|
91
|
+
const input = screen.getByLabelText('Text') as HTMLInputElement;
|
|
92
|
+
await user.type(input, 'typed value');
|
|
93
|
+
|
|
94
|
+
await waitFor(() => {
|
|
95
|
+
expect(formValues.text).toBe('typed value');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
60
98
|
});
|
|
61
99
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
100
|
+
describe('Warning validation', () => {
|
|
101
|
+
it('shows warning when checkWarning returns a warning message', async () => {
|
|
102
|
+
const user = userEvent.setup();
|
|
103
|
+
renderInput({
|
|
104
|
+
required: true,
|
|
105
|
+
checkWarning: (value) => {
|
|
106
|
+
if (value.length > 5) {
|
|
107
|
+
return 'name should be of 5 char';
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const input = screen.getByLabelText('Text') as HTMLInputElement;
|
|
113
|
+
await user.type(input, 'Hello World');
|
|
114
|
+
await user.tab();
|
|
115
|
+
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
expect(screen.getByText('name should be of 5 char')).toBeInTheDocument();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('does not show warning when input is valid', async () => {
|
|
122
|
+
const user = userEvent.setup();
|
|
123
|
+
renderInput({
|
|
124
|
+
required: true,
|
|
125
|
+
checkWarning: (value) => {
|
|
126
|
+
if (value.length > 5) {
|
|
127
|
+
return 'name should be of 5 char';
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const input = screen.getByLabelText('Text') as HTMLInputElement;
|
|
133
|
+
await user.type(input, 'text');
|
|
134
|
+
await user.tab();
|
|
135
|
+
|
|
136
|
+
await waitFor(() => {
|
|
137
|
+
expect(screen.queryByText('name should be of 5 char')).not.toBeInTheDocument();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('clears warning when user corrects input', async () => {
|
|
142
|
+
const user = userEvent.setup();
|
|
143
|
+
renderInput({
|
|
144
|
+
required: true,
|
|
145
|
+
checkWarning: (value) => {
|
|
146
|
+
if (value.length > 5) {
|
|
147
|
+
return 'name should be of 5 char';
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const input = screen.getByLabelText('Text') as HTMLInputElement;
|
|
153
|
+
|
|
154
|
+
// Type invalid value
|
|
155
|
+
await user.type(input, 'Hello World');
|
|
156
|
+
await user.tab();
|
|
157
|
+
|
|
158
|
+
await waitFor(() => {
|
|
159
|
+
expect(screen.getByText('name should be of 5 char')).toBeInTheDocument();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Clear and type valid value
|
|
163
|
+
await user.clear(input);
|
|
164
|
+
await user.type(input, 'text');
|
|
165
|
+
await user.tab();
|
|
166
|
+
|
|
167
|
+
await waitFor(() => {
|
|
168
|
+
expect(screen.queryByText('name should be of 5 char')).not.toBeInTheDocument();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
71
171
|
});
|
|
72
172
|
});
|