@kenyaemr/esm-patient-registration-app 4.5.4 → 4.5.5
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/117.js +2 -0
- package/dist/117.js.map +1 -0
- package/dist/130.js +1 -1
- package/dist/130.js.map +1 -1
- package/dist/208.js +1 -1
- package/dist/218.js +1 -0
- package/dist/218.js.map +1 -0
- package/dist/275.js +1 -0
- package/dist/275.js.map +1 -0
- package/dist/319.js +1 -1
- package/dist/{821.js → 348.js} +1 -1
- package/dist/{821.js.map → 348.js.map} +1 -1
- package/dist/574.js +1 -1
- package/dist/68.js +1 -1
- package/dist/68.js.map +1 -1
- package/dist/693.js +1 -0
- package/dist/693.js.map +1 -0
- package/dist/757.js +1 -1
- package/dist/788.js +1 -1
- package/dist/807.js +1 -1
- package/dist/833.js +1 -1
- package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
- package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +147 -122
- package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/jest.config.js +3 -0
- package/package.json +5 -2
- package/src/index.ts +9 -2
- package/src/offline.resources.ts +8 -4
- package/src/offline.ts +1 -1
- package/src/patient-registration/field/__mocks__/field.resource.ts +1 -1
- package/src/patient-registration/field/address/address-field.component.tsx +25 -70
- package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +11 -9
- package/src/patient-registration/field/address/address-hierarchy.resource.tsx +57 -3
- package/src/patient-registration/field/address/address-search.component.tsx +1 -1
- package/src/patient-registration/field/address/tests/address-hierarchy.test.tsx +137 -63
- package/src/patient-registration/field/address/tests/address-search-component.test.tsx +128 -0
- package/src/patient-registration/field/address/tests/mocks.ts +93 -99
- package/src/patient-registration/field/dob/dob.component.tsx +34 -32
- package/src/patient-registration/field/field.resource.ts +1 -1
- package/src/patient-registration/field/id/id-field.component.tsx +1 -1
- package/src/patient-registration/field/id/identifier-selection-overlay.tsx +1 -1
- package/src/patient-registration/field/name/name-field.component.tsx +0 -1
- package/src/patient-registration/field/obs/obs-field.component.tsx +14 -13
- package/src/patient-registration/field/person-attributes/coded-attributes.component.tsx +1 -1
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +1 -1
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +103 -0
- package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +187 -0
- package/src/patient-registration/field/person-attributes/person-attributes.resource.tsx +1 -1
- package/src/patient-registration/field/person-attributes/text-person-attribute-field.component.tsx +3 -3
- package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +88 -0
- package/src/patient-registration/form-manager.test.ts +1 -1
- package/src/patient-registration/form-manager.ts +1 -1
- package/src/patient-registration/input/basic-input/input/input.test.tsx +0 -135
- package/src/patient-registration/input/basic-input/select/select-input.test.tsx +8 -4
- package/src/patient-registration/input/combo-input/combo-input.component.tsx +8 -6
- package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +1 -1
- package/src/patient-registration/input/custom-input/identifier/utils.test.ts +81 -0
- package/src/patient-registration/input/custom-input/identifier/utils.ts +1 -1
- package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +1 -1
- package/src/patient-registration/patient-registration-context.ts +1 -1
- package/src/patient-registration/patient-registration-hooks.ts +1 -1
- package/src/patient-registration/patient-registration-utils.ts +1 -1
- package/src/patient-registration/patient-registration.component.tsx +1 -12
- package/src/patient-registration/patient-registration.resource.tsx +1 -72
- package/src/patient-registration/patient-registration.test.tsx +250 -247
- package/src/patient-registration/{patient-registration-types.tsx → patient-registration.types.tsx} +45 -0
- package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +83 -79
- package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +88 -0
- package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +1 -1
- package/src/patient-registration/validation/patient-registration-validation.tsx +1 -1
- package/src/patient-verification/patient-verification-hook.tsx +12 -1
- package/src/patient-verification/patient-verification-utils.ts +1 -1
- package/src/patient-verification/patient-verification.component.tsx +1 -1
- package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +11 -0
- package/src/root.component.tsx +1 -1
- package/src/routes.json +62 -51
- package/src/widgets/display-photo.test.tsx +37 -0
- package/src/widgets/edit-patient-details-button.test.tsx +36 -0
- package/translations/am.json +0 -7
- package/translations/en.json +2 -5
- package/translations/es.json +0 -7
- package/translations/fr.json +0 -7
- package/translations/he.json +0 -7
- package/translations/km.json +0 -7
- package/__mocks__/react-i18next.js +0 -49
- package/dist/196.js +0 -1
- package/dist/196.js.map +0 -1
- package/dist/59.js +0 -1
- package/dist/59.js.map +0 -1
- package/dist/9.js +0 -2
- package/dist/9.js.map +0 -1
- /package/dist/{9.js.LICENSE.txt → 117.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { usePersonAttributeType } from './person-attributes.resource';
|
|
4
|
+
import { PersonAttributeField } from './person-attribute-field.component';
|
|
5
|
+
import { useConceptAnswers } from '../field.resource';
|
|
6
|
+
import { Form, Formik } from 'formik';
|
|
7
|
+
import { FieldDefinition } from '../../../config-schema';
|
|
8
|
+
|
|
9
|
+
jest.mock('./person-attributes.resource'); // Mock the usePersonAttributeType hook
|
|
10
|
+
jest.mock('../field.resource'); // Mock the useConceptAnswers hook
|
|
11
|
+
|
|
12
|
+
jest.mock('formik', () => ({
|
|
13
|
+
...jest.requireActual('formik'),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
const mockedUsePersonAttributeType = usePersonAttributeType as jest.Mock;
|
|
17
|
+
const mockedUseConceptAnswers = useConceptAnswers as jest.Mock;
|
|
18
|
+
|
|
19
|
+
let fieldDefinition: FieldDefinition;
|
|
20
|
+
|
|
21
|
+
describe('PersonAttributeField', () => {
|
|
22
|
+
let mockPersonAttributeType = {
|
|
23
|
+
format: 'java.lang.String',
|
|
24
|
+
display: 'Referred by',
|
|
25
|
+
uuid: '4dd56a75-14ab-4148-8700-1f4f704dc5b0',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
fieldDefinition = {
|
|
30
|
+
id: 'referredby',
|
|
31
|
+
name: 'Referred by',
|
|
32
|
+
type: 'person attribute',
|
|
33
|
+
uuid: '4dd56a75-14ab-4148-8700-1f4f704dc5b0',
|
|
34
|
+
answerConceptSetUuid: '6682d17f-0777-45e4-a39b-93f77eb3531c',
|
|
35
|
+
validation: {
|
|
36
|
+
matches: '',
|
|
37
|
+
required: true,
|
|
38
|
+
},
|
|
39
|
+
showHeading: true,
|
|
40
|
+
};
|
|
41
|
+
mockedUsePersonAttributeType.mockReturnValue({
|
|
42
|
+
data: mockPersonAttributeType,
|
|
43
|
+
isLoading: false,
|
|
44
|
+
error: null,
|
|
45
|
+
uuid: '14d4f066-15f5-102d-96e4-000c29c2a5d7d',
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
jest.resetAllMocks();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('renders the text input field for String format', () => {
|
|
54
|
+
render(
|
|
55
|
+
<Formik initialValues={{}} onSubmit={() => {}}>
|
|
56
|
+
<Form>
|
|
57
|
+
<PersonAttributeField fieldDefinition={fieldDefinition} />
|
|
58
|
+
</Form>
|
|
59
|
+
</Formik>,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const input = screen.getByLabelText(/Referred by/i) as HTMLInputElement;
|
|
63
|
+
expect(screen.getByRole('heading')).toBeInTheDocument();
|
|
64
|
+
expect(input).toBeInTheDocument();
|
|
65
|
+
expect(input.type).toBe('text');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should not show heading if showHeading is false', () => {
|
|
69
|
+
fieldDefinition = {
|
|
70
|
+
...fieldDefinition,
|
|
71
|
+
showHeading: false,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
render(
|
|
75
|
+
<Formik initialValues={{}} onSubmit={() => {}}>
|
|
76
|
+
<Form>
|
|
77
|
+
<PersonAttributeField fieldDefinition={fieldDefinition} />
|
|
78
|
+
</Form>
|
|
79
|
+
</Formik>,
|
|
80
|
+
);
|
|
81
|
+
expect(screen.queryByRole('heading')).not.toBeInTheDocument();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('renders the coded attribute field for Concept format', () => {
|
|
85
|
+
mockedUsePersonAttributeType.mockReturnValue({
|
|
86
|
+
data: { ...mockPersonAttributeType, format: 'org.openmrs.Concept' },
|
|
87
|
+
isLoading: false,
|
|
88
|
+
error: null,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
fieldDefinition = {
|
|
92
|
+
id: 'referredby',
|
|
93
|
+
...fieldDefinition,
|
|
94
|
+
label: 'Referred by',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
mockedUseConceptAnswers.mockReturnValueOnce({
|
|
98
|
+
data: [
|
|
99
|
+
{ uuid: '1', display: 'Option 1' },
|
|
100
|
+
{ uuid: '2', display: 'Option 2' },
|
|
101
|
+
],
|
|
102
|
+
isLoading: false,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
render(
|
|
106
|
+
<Formik initialValues={{}} onSubmit={() => {}}>
|
|
107
|
+
<Form>
|
|
108
|
+
<PersonAttributeField fieldDefinition={fieldDefinition} />
|
|
109
|
+
</Form>
|
|
110
|
+
</Formik>,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const input = screen.getByLabelText(/Referred by/i) as HTMLInputElement;
|
|
114
|
+
expect(input).toBeInTheDocument();
|
|
115
|
+
expect(input.type).toBe('select-one');
|
|
116
|
+
expect(screen.getByText('Option 1')).toBeInTheDocument();
|
|
117
|
+
expect(screen.getByText('Option 2')).toBeInTheDocument();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('renders an error notification if attribute type has unknown format', () => {
|
|
121
|
+
mockedUsePersonAttributeType.mockReturnValue({
|
|
122
|
+
data: { ...mockPersonAttributeType, format: 'unknown' },
|
|
123
|
+
isLoading: false,
|
|
124
|
+
error: null,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
render(
|
|
128
|
+
<Formik initialValues={{}} onSubmit={() => {}}>
|
|
129
|
+
<Form>
|
|
130
|
+
<PersonAttributeField fieldDefinition={fieldDefinition} />
|
|
131
|
+
</Form>
|
|
132
|
+
</Formik>,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(screen.getByText('Error')).toBeInTheDocument();
|
|
136
|
+
expect(screen.getByText(/Patient attribute type has unknown format/i)).toBeInTheDocument();
|
|
137
|
+
});
|
|
138
|
+
it('renders an error notification if unable to fetch attribute type', () => {
|
|
139
|
+
mockedUsePersonAttributeType.mockReturnValue({
|
|
140
|
+
data: null,
|
|
141
|
+
isLoading: false,
|
|
142
|
+
error: new Error('Failed to fetch attribute type'),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
fieldDefinition = {
|
|
146
|
+
uuid: 'attribute-uuid',
|
|
147
|
+
label: 'Attribute',
|
|
148
|
+
showHeading: false,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
render(
|
|
152
|
+
<Formik initialValues={{}} onSubmit={() => {}}>
|
|
153
|
+
<Form>
|
|
154
|
+
<PersonAttributeField fieldDefinition={fieldDefinition} />
|
|
155
|
+
</Form>
|
|
156
|
+
</Formik>,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
expect(screen.getByText('Error')).toBeInTheDocument();
|
|
160
|
+
expect(screen.getByText(/Unable to fetch person attribute type/i)).toBeInTheDocument();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('renders a skeleton if attribute type is loading', () => {
|
|
164
|
+
mockedUsePersonAttributeType.mockReturnValue({
|
|
165
|
+
data: null,
|
|
166
|
+
isLoading: true,
|
|
167
|
+
error: null,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
fieldDefinition = {
|
|
171
|
+
uuid: 'attribute-uuid',
|
|
172
|
+
label: 'Attribute',
|
|
173
|
+
showHeading: true,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
render(
|
|
177
|
+
<Formik initialValues={{}} onSubmit={() => {}}>
|
|
178
|
+
<Form>
|
|
179
|
+
<PersonAttributeField fieldDefinition={fieldDefinition} />
|
|
180
|
+
</Form>
|
|
181
|
+
</Formik>,
|
|
182
|
+
);
|
|
183
|
+
const input = screen.findByLabelText(/Reffered by/i);
|
|
184
|
+
expect(screen.getByText(/Attribute/i)).toBeInTheDocument();
|
|
185
|
+
expect(input).not.toBeNull(); // checks that the input is not rendered when the attribute type is loading
|
|
186
|
+
});
|
|
187
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FetchResponse, openmrsFetch, showToast } from '@openmrs/esm-framework';
|
|
2
2
|
import useSWRImmutable from 'swr/immutable';
|
|
3
|
-
import { PersonAttributeTypeResponse } from '../../patient-registration
|
|
3
|
+
import { PersonAttributeTypeResponse } from '../../patient-registration.types';
|
|
4
4
|
|
|
5
5
|
export function usePersonAttributeType(personAttributeTypeUuid: string): {
|
|
6
6
|
data: PersonAttributeTypeResponse;
|
package/src/patient-registration/field/person-attributes/text-person-attribute-field.component.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import styles from './../field.scss';
|
|
3
|
-
import { Input } from '../../input/basic-input/input/input.component';
|
|
4
2
|
import { Field } from 'formik';
|
|
5
3
|
import { useTranslation } from 'react-i18next';
|
|
6
|
-
import {
|
|
4
|
+
import { Input } from '../../input/basic-input/input/input.component';
|
|
5
|
+
import styles from './../field.scss';
|
|
6
|
+
import { PersonAttributeTypeResponse } from '../../patient-registration.types';
|
|
7
7
|
|
|
8
8
|
export interface TextPersonAttributeFieldProps {
|
|
9
9
|
id: string;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { Form, Formik } from 'formik';
|
|
5
|
+
import { TextPersonAttributeField } from './text-person-attribute-field.component';
|
|
6
|
+
|
|
7
|
+
describe('TextPersonAttributeField', () => {
|
|
8
|
+
const mockPersonAttributeType = {
|
|
9
|
+
format: 'java.lang.String',
|
|
10
|
+
display: 'Referred by',
|
|
11
|
+
uuid: '4dd56a75-14ab-4148-8700-1f4f704dc5b0',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
it('renders the input field with a label', () => {
|
|
15
|
+
render(
|
|
16
|
+
<Formik initialValues={{}} onSubmit={() => {}}>
|
|
17
|
+
<Form>
|
|
18
|
+
<TextPersonAttributeField
|
|
19
|
+
id="attributeId"
|
|
20
|
+
personAttributeType={mockPersonAttributeType}
|
|
21
|
+
label="Custom Label"
|
|
22
|
+
/>
|
|
23
|
+
</Form>
|
|
24
|
+
</Formik>,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
expect(screen.getByRole('textbox', { name: /custom label \(optional\)/i })).toBeInTheDocument();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('renders the input field with the default label if label prop is not provided', () => {
|
|
31
|
+
render(
|
|
32
|
+
<Formik initialValues={{}} onSubmit={() => {}}>
|
|
33
|
+
<Form>
|
|
34
|
+
<TextPersonAttributeField id="attributeId" personAttributeType={mockPersonAttributeType} />
|
|
35
|
+
</Form>
|
|
36
|
+
</Formik>,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
expect(screen.getByRole('textbox', { name: /referred by \(optional\)/i })).toBeInTheDocument();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('validates the input with the provided validationRegex', async () => {
|
|
43
|
+
const user = userEvent.setup();
|
|
44
|
+
const validationRegex = '^[A-Z]+$'; // Accepts only uppercase letters
|
|
45
|
+
|
|
46
|
+
render(
|
|
47
|
+
<Formik initialValues={{}} onSubmit={() => {}}>
|
|
48
|
+
<Form>
|
|
49
|
+
<TextPersonAttributeField
|
|
50
|
+
id="attributeId"
|
|
51
|
+
personAttributeType={mockPersonAttributeType}
|
|
52
|
+
validationRegex={validationRegex}
|
|
53
|
+
/>
|
|
54
|
+
</Form>
|
|
55
|
+
</Formik>,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const textbox = screen.getByRole('textbox', { name: /referred by \(optional\)/i });
|
|
59
|
+
expect(textbox).toBeInTheDocument();
|
|
60
|
+
|
|
61
|
+
// Valid input: "ABC"
|
|
62
|
+
await user.type(textbox, 'ABC');
|
|
63
|
+
await user.tab();
|
|
64
|
+
|
|
65
|
+
expect(screen.queryByText(/invalid input/i)).not.toBeInTheDocument();
|
|
66
|
+
await user.clear(textbox);
|
|
67
|
+
|
|
68
|
+
// // Invalid input: "abc" (contains lowercase letters)
|
|
69
|
+
await user.type(textbox, 'abc');
|
|
70
|
+
await user.tab();
|
|
71
|
+
expect(screen.getByText(/invalid input/i)).toBeInTheDocument();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('renders the input field as required when required prop is true', () => {
|
|
75
|
+
render(
|
|
76
|
+
<Formik initialValues={{}} onSubmit={() => {}}>
|
|
77
|
+
<Form>
|
|
78
|
+
<TextPersonAttributeField id="attributeId" personAttributeType={mockPersonAttributeType} required />
|
|
79
|
+
</Form>
|
|
80
|
+
</Formik>,
|
|
81
|
+
);
|
|
82
|
+
const textbox = screen.getByRole('textbox', { name: /referred by/i });
|
|
83
|
+
|
|
84
|
+
// Required attribute should be truthy on the input element
|
|
85
|
+
expect(textbox).toBeInTheDocument();
|
|
86
|
+
expect(textbox).toBeRequired();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -4,36 +4,6 @@ import userEvent from '@testing-library/user-event';
|
|
|
4
4
|
import { Formik, Form } from 'formik';
|
|
5
5
|
import { Input } from './input.component';
|
|
6
6
|
|
|
7
|
-
describe.skip('number input', () => {
|
|
8
|
-
const setupInput = async () => {
|
|
9
|
-
render(
|
|
10
|
-
<Formik initialValues={{ number: 0 }} onSubmit={null}>
|
|
11
|
-
<Form>
|
|
12
|
-
<Input id="number" type="number" labelText="Number" name="number" />
|
|
13
|
-
</Form>
|
|
14
|
-
</Formik>,
|
|
15
|
-
);
|
|
16
|
-
return screen.getByLabelText('Number (optional)') as HTMLInputElement;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
it('exists', async () => {
|
|
20
|
-
const input = await setupInput();
|
|
21
|
-
expect(input.type).toEqual('number');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('can input data', async () => {
|
|
25
|
-
const user = userEvent.setup();
|
|
26
|
-
|
|
27
|
-
const input = await setupInput();
|
|
28
|
-
const expected = 1;
|
|
29
|
-
|
|
30
|
-
await user.type(input, expected.toString());
|
|
31
|
-
await user.tab();
|
|
32
|
-
|
|
33
|
-
expect(input.valueAsNumber).toEqual(expected);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
7
|
describe('text input', () => {
|
|
38
8
|
const setupInput = async () => {
|
|
39
9
|
render(
|
|
@@ -100,108 +70,3 @@ describe('text input', () => {
|
|
|
100
70
|
expect(screen.getByLabelText('Text (optional)')).toBeInTheDocument();
|
|
101
71
|
});
|
|
102
72
|
});
|
|
103
|
-
|
|
104
|
-
describe.skip('telephone number input', () => {
|
|
105
|
-
const setupInput = async () => {
|
|
106
|
-
render(
|
|
107
|
-
<Formik initialValues={{ telephoneNumber: '' }} onSubmit={null}>
|
|
108
|
-
<Form>
|
|
109
|
-
<Input id="tel" labelText="Telephone Number" name="telephoneNumber" placeholder="Enter telephone number" />
|
|
110
|
-
</Form>
|
|
111
|
-
</Formik>,
|
|
112
|
-
);
|
|
113
|
-
return screen.getByLabelText('telephoneNumber') as HTMLInputElement;
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
it('exists', async () => {
|
|
117
|
-
const input = await setupInput();
|
|
118
|
-
expect(input.type).toEqual('tel');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('can input data', async () => {
|
|
122
|
-
const user = userEvent.setup();
|
|
123
|
-
|
|
124
|
-
const input = await setupInput();
|
|
125
|
-
const expected = '0800001066';
|
|
126
|
-
|
|
127
|
-
await user.type(input, expected);
|
|
128
|
-
await user.tab();
|
|
129
|
-
|
|
130
|
-
expect(input.value).toEqual(expected);
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
describe.skip('date input', () => {
|
|
135
|
-
const setupInput = async () => {
|
|
136
|
-
render(
|
|
137
|
-
<Formik initialValues={{ date: '' }} onSubmit={null}>
|
|
138
|
-
<Form>
|
|
139
|
-
<Input id="date" labelText="date" name="date" />
|
|
140
|
-
</Form>
|
|
141
|
-
</Formik>,
|
|
142
|
-
);
|
|
143
|
-
return screen.getByLabelText('date') as HTMLInputElement;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
it('exists', async () => {
|
|
147
|
-
const input = await setupInput();
|
|
148
|
-
expect(input.type).toEqual('date');
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('can input data', async () => {
|
|
152
|
-
const user = userEvent.setup();
|
|
153
|
-
|
|
154
|
-
const input = await setupInput();
|
|
155
|
-
const expected = '1990-09-10';
|
|
156
|
-
|
|
157
|
-
await user.type(input, expected);
|
|
158
|
-
await user.tab();
|
|
159
|
-
|
|
160
|
-
expect(input.value).toEqual(expected);
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
describe.skip('checkbox input', () => {
|
|
165
|
-
const setupInput = async () => {
|
|
166
|
-
render(
|
|
167
|
-
<Formik initialValues={{ checkbox: false }} onSubmit={null}>
|
|
168
|
-
<Form>
|
|
169
|
-
<Input id="checkbox" labelText="checkbox" name="checkbox" />
|
|
170
|
-
</Form>
|
|
171
|
-
</Formik>,
|
|
172
|
-
);
|
|
173
|
-
return screen.getByLabelText('checkbox') as HTMLInputElement;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
it('exists', async () => {
|
|
177
|
-
const input = await setupInput();
|
|
178
|
-
expect(input.type).toEqual('checkbox');
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('can input data', async () => {
|
|
182
|
-
const user = userEvent.setup();
|
|
183
|
-
const input = await setupInput();
|
|
184
|
-
|
|
185
|
-
const expected = true;
|
|
186
|
-
|
|
187
|
-
await user.click(input);
|
|
188
|
-
await user.tab();
|
|
189
|
-
|
|
190
|
-
expect(input.checked).toEqual(expected);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('can update data', async () => {
|
|
194
|
-
const user = userEvent.setup();
|
|
195
|
-
const input = await setupInput();
|
|
196
|
-
|
|
197
|
-
const expected = false;
|
|
198
|
-
|
|
199
|
-
await user.click(input);
|
|
200
|
-
await user.tab();
|
|
201
|
-
|
|
202
|
-
await user.click(input);
|
|
203
|
-
await user.tab();
|
|
204
|
-
|
|
205
|
-
expect(input.checked).toEqual(expected);
|
|
206
|
-
});
|
|
207
|
-
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { render,
|
|
2
|
+
import { render, waitFor, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
3
4
|
import { Formik, Form } from 'formik';
|
|
4
5
|
import { SelectInput } from './select-input.component';
|
|
5
6
|
|
|
@@ -21,16 +22,16 @@ describe('the select input', () => {
|
|
|
21
22
|
});
|
|
22
23
|
|
|
23
24
|
it('can input data', async () => {
|
|
25
|
+
const user = userEvent.setup();
|
|
24
26
|
const input = await setupSelect();
|
|
25
27
|
const expected = 'A Option';
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
fireEvent.blur(input);
|
|
29
|
+
await user.selectOptions(input, expected);
|
|
29
30
|
|
|
30
31
|
await waitFor(() => expect(input.value).toEqual(expected));
|
|
31
32
|
});
|
|
32
33
|
|
|
33
|
-
it('should show optional label if the input is not required', () => {
|
|
34
|
+
it('should show optional label if the input is not required', async () => {
|
|
34
35
|
render(
|
|
35
36
|
<Formik initialValues={{ select: '' }} onSubmit={null}>
|
|
36
37
|
<Form>
|
|
@@ -38,6 +39,9 @@ describe('the select input', () => {
|
|
|
38
39
|
</Form>
|
|
39
40
|
</Formik>,
|
|
40
41
|
);
|
|
42
|
+
|
|
43
|
+
await waitFor(() => expect(screen.findByRole('combobox')));
|
|
44
|
+
|
|
41
45
|
const selectInput = screen.getByRole('combobox', { name: 'Select (optional)' }) as HTMLSelectElement;
|
|
42
46
|
expect(selectInput.labels).toHaveLength(1);
|
|
43
47
|
expect(selectInput.labels[0]).toHaveTextContent('Select (optional)');
|
|
@@ -94,20 +94,22 @@ const ComboInput: React.FC<ComboInputProps> = ({ entries, fieldProps, handleInpu
|
|
|
94
94
|
</Layer>
|
|
95
95
|
<div className={styles.comboInputEntries}>
|
|
96
96
|
{showEntries && (
|
|
97
|
-
<div id="downshift-1-menu"
|
|
97
|
+
<div id="downshift-1-menu" className="cds--list-box__menu" role="listbox">
|
|
98
98
|
{filteredEntries.map((entry, indx) => (
|
|
99
99
|
<div
|
|
100
100
|
key={indx}
|
|
101
101
|
id="downshift-1-item-0"
|
|
102
102
|
role="option"
|
|
103
|
-
className={`cds--list-box__menu-item ${
|
|
104
|
-
|
|
103
|
+
className={`cds--list-box__menu-item ${
|
|
104
|
+
indx === highlightedEntry && 'cds--list-box__menu-item--highlighted'
|
|
105
|
+
}`}
|
|
105
106
|
tabIndex={-1}
|
|
106
107
|
aria-selected="true"
|
|
107
108
|
onClick={() => handleOptionClick(entry)}>
|
|
108
109
|
<div
|
|
109
|
-
className={`cds--list-box__menu-item__option ${styles.comboInputItemOption} ${
|
|
110
|
-
|
|
110
|
+
className={`cds--list-box__menu-item__option ${styles.comboInputItemOption} ${
|
|
111
|
+
entry === value && 'cds--list-box__menu-item--active'
|
|
112
|
+
}`}>
|
|
111
113
|
{entry}
|
|
112
114
|
{entry === value && <SelectionTick />}
|
|
113
115
|
</div>
|
|
@@ -116,7 +118,7 @@ const ComboInput: React.FC<ComboInputProps> = ({ entries, fieldProps, handleInpu
|
|
|
116
118
|
</div>
|
|
117
119
|
)}
|
|
118
120
|
</div>
|
|
119
|
-
</div
|
|
121
|
+
</div>
|
|
120
122
|
);
|
|
121
123
|
};
|
|
122
124
|
|
package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import { ResourcesContext } from '../../../../offline.resources';
|
|
|
7
7
|
import { showModal, useConfig, UserHasAccess } from '@openmrs/esm-framework';
|
|
8
8
|
import { shouldBlockPatientIdentifierInOfflineMode } from './utils';
|
|
9
9
|
import { deleteIdentifierType, setIdentifierSource } from '../../../field/id/id-field.component';
|
|
10
|
-
import { PatientIdentifierValue } from '../../../patient-registration
|
|
10
|
+
import { PatientIdentifierValue } from '../../../patient-registration.types';
|
|
11
11
|
import { PatientRegistrationContext } from '../../../patient-registration-context';
|
|
12
12
|
import { Input } from '../../basic-input/input/input.component';
|
|
13
13
|
import styles from '../../input.scss';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { isUniqueIdentifierTypeForOffline, shouldBlockPatientIdentifierInOfflineMode } from './utils';
|
|
2
|
+
|
|
3
|
+
interface IdentifierTypeOptions {
|
|
4
|
+
uniquenessBehavior?: 'UNIQUE' | 'LOCATION' | 'NON_UNIQUE';
|
|
5
|
+
manualEntryEnabled?: boolean;
|
|
6
|
+
automaticGenerationEnabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function createIdentifierType(options: IdentifierTypeOptions) {
|
|
10
|
+
return {
|
|
11
|
+
uniquenessBehavior: options.uniquenessBehavior,
|
|
12
|
+
identifierSources: [
|
|
13
|
+
{
|
|
14
|
+
uuid: 'identifier-source-uuid',
|
|
15
|
+
name: 'Identifier Source Name',
|
|
16
|
+
autoGenerationOption: {
|
|
17
|
+
manualEntryEnabled: options.manualEntryEnabled,
|
|
18
|
+
automaticGenerationEnabled: options.automaticGenerationEnabled,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
name: 'Identifier Type Name',
|
|
23
|
+
required: true,
|
|
24
|
+
uuid: 'identifier-type-uuid',
|
|
25
|
+
fieldName: 'identifierFieldName',
|
|
26
|
+
format: 'identifierFormat',
|
|
27
|
+
isPrimary: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('shouldBlockPatientIdentifierInOfflineMode function', () => {
|
|
32
|
+
it('should return false if identifierType is not unique', () => {
|
|
33
|
+
const identifierType = createIdentifierType({ uniquenessBehavior: null });
|
|
34
|
+
|
|
35
|
+
const result = shouldBlockPatientIdentifierInOfflineMode(identifierType);
|
|
36
|
+
|
|
37
|
+
expect(result).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should return false if identifierType is unique and no manual entry is enabled', () => {
|
|
41
|
+
const identifierType = createIdentifierType({ uniquenessBehavior: null });
|
|
42
|
+
|
|
43
|
+
const result = shouldBlockPatientIdentifierInOfflineMode(identifierType);
|
|
44
|
+
|
|
45
|
+
expect(result).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should return true if identifierType is unique and manual entry is enabled', () => {
|
|
49
|
+
const identifierType = createIdentifierType({ manualEntryEnabled: true, uniquenessBehavior: 'UNIQUE' });
|
|
50
|
+
|
|
51
|
+
const result = shouldBlockPatientIdentifierInOfflineMode(identifierType);
|
|
52
|
+
|
|
53
|
+
expect(result).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('isUniqueIdentifierTypeForOffline function', () => {
|
|
58
|
+
it('should return true if uniquenessBehavior is UNIQUE', () => {
|
|
59
|
+
const identifierType = createIdentifierType({ uniquenessBehavior: 'UNIQUE' });
|
|
60
|
+
|
|
61
|
+
const result = isUniqueIdentifierTypeForOffline(identifierType);
|
|
62
|
+
|
|
63
|
+
expect(result).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should return true if uniquenessBehavior is LOCATION', () => {
|
|
67
|
+
const identifierType = createIdentifierType({ uniquenessBehavior: 'LOCATION' });
|
|
68
|
+
|
|
69
|
+
const result = isUniqueIdentifierTypeForOffline(identifierType);
|
|
70
|
+
|
|
71
|
+
expect(result).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should return false for other uniqueness behaviors', () => {
|
|
75
|
+
const identifierType = createIdentifierType({ uniquenessBehavior: null });
|
|
76
|
+
|
|
77
|
+
const result = isUniqueIdentifierTypeForOffline(identifierType);
|
|
78
|
+
|
|
79
|
+
expect(result).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FetchedPatientIdentifierType, PatientIdentifierType } from '../../../patient-registration
|
|
1
|
+
import { FetchedPatientIdentifierType, PatientIdentifierType } from '../../../patient-registration.types';
|
|
2
2
|
|
|
3
3
|
export function shouldBlockPatientIdentifierInOfflineMode(identifierType: PatientIdentifierType) {
|
|
4
4
|
// Patient Identifiers which are unique and can be manually entered are prohibited while offline because
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useConfig } from '@openmrs/esm-framework';
|
|
2
2
|
import { createContext, SetStateAction } from 'react';
|
|
3
3
|
import { RegistrationConfig } from '../config-schema';
|
|
4
|
-
import { FormValues, CapturePhotoProps } from './patient-registration
|
|
4
|
+
import { FormValues, CapturePhotoProps } from './patient-registration.types';
|
|
5
5
|
|
|
6
6
|
export interface PatientRegistrationContextProps {
|
|
7
7
|
identifierTypes: Array<any>;
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
PatientUuidMapType,
|
|
7
7
|
PatientIdentifierValue,
|
|
8
8
|
Encounter,
|
|
9
|
-
} from './patient-registration
|
|
9
|
+
} from './patient-registration.types';
|
|
10
10
|
import { parseDate } from '@openmrs/esm-framework';
|
|
11
11
|
import camelCase from 'lodash-es/camelCase';
|
|
12
12
|
import capitalize from 'lodash-es/capitalize';
|