@openmrs/esm-form-engine-lib 2.1.0-pre.1513 → 2.1.0-pre.1523
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/__mocks__/forms/rfe-forms/sample_unspecified-form.json +39 -0
- package/jest.config.js +1 -0
- package/package.json +1 -1
- package/src/components/inputs/unspecified/unspecified.component.tsx +9 -6
- package/src/components/inputs/unspecified/unspecified.test.tsx +142 -50
- package/src/components/renderer/field/form-field-renderer.component.tsx +4 -0
- package/src/utils/common-utils.ts +1 -0
- package/src/utils/form-helper.test.ts +0 -4
@@ -0,0 +1,39 @@
|
|
1
|
+
{
|
2
|
+
"name": "Sample Unspecified Form",
|
3
|
+
"pages": [
|
4
|
+
{
|
5
|
+
"label": "Page 1",
|
6
|
+
"sections": [
|
7
|
+
{
|
8
|
+
"label": "Section 1",
|
9
|
+
"isExpanded": "true",
|
10
|
+
"questions": [
|
11
|
+
{
|
12
|
+
"label": "Body Weight",
|
13
|
+
"type": "obs",
|
14
|
+
"questionOptions": {
|
15
|
+
"rendering": "number",
|
16
|
+
"concept": "560555AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
17
|
+
"conceptMappings": [
|
18
|
+
{
|
19
|
+
"type": "CIEL",
|
20
|
+
"value": "160555"
|
21
|
+
}
|
22
|
+
]
|
23
|
+
},
|
24
|
+
"id": "bodyWeight",
|
25
|
+
"validators": [],
|
26
|
+
"required": true,
|
27
|
+
"unspecified": true
|
28
|
+
}
|
29
|
+
]
|
30
|
+
}
|
31
|
+
]
|
32
|
+
}
|
33
|
+
],
|
34
|
+
"availableIntents": [],
|
35
|
+
"processor": "EncounterFormProcessor",
|
36
|
+
"uuid": "na24c540-cc83-43bc-978f-c1ef180a597f",
|
37
|
+
"referencedForms": [],
|
38
|
+
"encounterType": "b9c1f50f-f77d-42e2-ad2a-d29304dde2fv"
|
39
|
+
}
|
package/jest.config.js
CHANGED
package/package.json
CHANGED
@@ -7,7 +7,7 @@ import { isTrue } from '../../../utils/boolean-utils';
|
|
7
7
|
|
8
8
|
import styles from './unspecified.scss';
|
9
9
|
import { useFormProviderContext } from '../../../provider/form-provider';
|
10
|
-
import { isViewMode } from '../../../utils/common-utils';
|
10
|
+
import { clearSubmission, isViewMode } from '../../../utils/common-utils';
|
11
11
|
|
12
12
|
interface UnspecifiedFieldProps {
|
13
13
|
field: FormField;
|
@@ -29,25 +29,28 @@ const UnspecifiedField: React.FC<UnspecifiedFieldProps> = ({ field, fieldValue,
|
|
29
29
|
}, []);
|
30
30
|
|
31
31
|
useEffect(() => {
|
32
|
-
if (field.meta.submission?.unspecified && field.meta.submission.newValue) {
|
32
|
+
if (field.meta.submission?.unspecified && (field.meta.submission.newValue || !isEmpty(fieldValue))) {
|
33
33
|
setIsUnspecified(false);
|
34
34
|
field.meta.submission.unspecified = false;
|
35
35
|
updateFormField(field);
|
36
36
|
}
|
37
|
-
}, [field.meta?.submission]);
|
37
|
+
}, [field.meta?.submission, fieldValue]);
|
38
38
|
|
39
39
|
const handleOnChange = useCallback(
|
40
40
|
(value) => {
|
41
41
|
const rendering = field.questionOptions.rendering;
|
42
42
|
if (value.target.checked) {
|
43
|
-
const emptyValue = rendering === 'checkbox' ? [] : '';
|
44
|
-
field.meta.submission = { ...field.meta.submission, unspecified: true };
|
45
|
-
updateFormField({ ...field });
|
46
43
|
setIsUnspecified(true);
|
44
|
+
const emptyValue = rendering === 'checkbox' ? [] : '';
|
45
|
+
clearSubmission(field);
|
46
|
+
field.meta.submission.unspecified = true;
|
47
|
+
updateFormField(field);
|
47
48
|
setFieldValue(emptyValue);
|
48
49
|
onAfterChange(emptyValue);
|
49
50
|
} else {
|
50
51
|
setIsUnspecified(false);
|
52
|
+
field.meta.submission.unspecified = false;
|
53
|
+
updateFormField(field);
|
51
54
|
}
|
52
55
|
},
|
53
56
|
[field.questionOptions.rendering],
|
@@ -1,74 +1,166 @@
|
|
1
1
|
import React from 'react';
|
2
|
-
import
|
3
|
-
import {
|
4
|
-
import {
|
5
|
-
import {
|
6
|
-
import
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
2
|
+
import { act, render, screen } from '@testing-library/react';
|
3
|
+
import { usePatient, useSession } from '@openmrs/esm-framework';
|
4
|
+
import { type FormSchema, type SessionMode } from '../../../types';
|
5
|
+
import { findNumberInput } from '../../../utils/test-utils';
|
6
|
+
import unspecifiedForm from '../../../../__mocks__/forms/rfe-forms/sample_unspecified-form.json';
|
7
|
+
import { FormEngine } from '../../..';
|
8
|
+
import { mockPatient } from '../../../../__mocks__/patient.mock';
|
9
|
+
import { mockSessionDataResponse } from '../../../../__mocks__/session.mock';
|
10
|
+
import userEvent from '@testing-library/user-event';
|
11
|
+
import * as api from '../../../api';
|
12
|
+
|
13
|
+
const mockUsePatient = jest.mocked(usePatient);
|
14
|
+
const mockUseSession = jest.mocked(useSession);
|
15
|
+
|
16
|
+
global.ResizeObserver = require('resize-observer-polyfill');
|
17
|
+
|
18
|
+
jest.mock('../../../api', () => {
|
19
|
+
const originalModule = jest.requireActual('../../../api');
|
20
|
+
return {
|
21
|
+
...originalModule,
|
22
|
+
getPreviousEncounter: jest.fn().mockImplementation(() => Promise.resolve(null)),
|
23
|
+
getConcept: jest.fn().mockImplementation(() => Promise.resolve(null)),
|
24
|
+
saveEncounter: jest.fn(),
|
25
|
+
};
|
21
26
|
});
|
22
27
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
},
|
31
|
-
|
32
|
-
};
|
28
|
+
jest.mock('../../../hooks/useConcepts', () => ({
|
29
|
+
useConcepts: jest.fn().mockImplementation((references: Set<string>) => {
|
30
|
+
return {
|
31
|
+
isLoading: false,
|
32
|
+
concepts: [],
|
33
|
+
error: undefined,
|
34
|
+
};
|
35
|
+
}),
|
36
|
+
}));
|
33
37
|
|
34
|
-
|
35
|
-
|
38
|
+
jest.mock('../../../hooks/useEncounterRole', () => ({
|
39
|
+
useEncounterRole: jest.fn().mockReturnValue({
|
40
|
+
isLoading: false,
|
41
|
+
encounterRole: { name: 'Clinician', uuid: 'clinician-uuid' },
|
42
|
+
error: undefined,
|
43
|
+
}),
|
44
|
+
}));
|
45
|
+
|
46
|
+
jest.mock('../../../hooks/useEncounter', () => ({
|
47
|
+
useEncounter: jest.fn().mockImplementation((formJson: FormSchema) => {
|
48
|
+
return {
|
49
|
+
encounter: formJson.encounter
|
50
|
+
? {
|
51
|
+
uuid: 'encounter-uuid',
|
52
|
+
obs: [],
|
53
|
+
}
|
54
|
+
: null,
|
55
|
+
isLoading: false,
|
56
|
+
error: undefined,
|
57
|
+
};
|
58
|
+
}),
|
59
|
+
}));
|
60
|
+
|
61
|
+
const renderForm = async (mode: SessionMode = 'enter') => {
|
62
|
+
await act(async () => {
|
63
|
+
render(
|
64
|
+
<FormEngine
|
65
|
+
formJson={unspecifiedForm as FormSchema}
|
66
|
+
patientUUID="8673ee4f-e2ab-4077-ba55-4980f408773e"
|
67
|
+
mode={mode}
|
68
|
+
encounterUUID={mode === 'edit' ? 'encounter-uuid' : null}
|
69
|
+
/>,
|
70
|
+
);
|
71
|
+
});
|
36
72
|
};
|
37
73
|
|
38
|
-
describe
|
39
|
-
|
40
|
-
|
41
|
-
|
74
|
+
describe('Unspecified', () => {
|
75
|
+
const user = userEvent.setup();
|
76
|
+
|
77
|
+
beforeEach(() => {
|
78
|
+
Object.defineProperty(window, 'i18next', {
|
79
|
+
writable: true,
|
80
|
+
configurable: true,
|
81
|
+
value: {
|
82
|
+
language: 'en',
|
83
|
+
t: jest.fn(),
|
84
|
+
},
|
85
|
+
});
|
86
|
+
|
87
|
+
mockUsePatient.mockImplementation(() => ({
|
88
|
+
patient: mockPatient,
|
89
|
+
isLoading: false,
|
90
|
+
error: undefined,
|
91
|
+
patientUuid: mockPatient.id,
|
92
|
+
}));
|
93
|
+
|
94
|
+
mockUseSession.mockImplementation(() => mockSessionDataResponse.data);
|
95
|
+
});
|
96
|
+
|
97
|
+
it('Should clear field value when the "Unspecified" checkbox is clicked', async () => {
|
98
|
+
//setup
|
99
|
+
await renderForm();
|
42
100
|
const unspecifiedCheckbox = screen.getByRole('checkbox', { name: /Unspecified/ });
|
101
|
+
const bodyWeightField = await findNumberInput(screen, 'Body Weight *');
|
43
102
|
|
44
103
|
// assert initial state
|
45
104
|
expect(unspecifiedCheckbox).not.toBeChecked();
|
105
|
+
expect(bodyWeightField.value).toBe('');
|
46
106
|
|
47
|
-
|
48
|
-
fireEvent.click(unspecifiedCheckbox);
|
49
|
-
expect(unspecifiedCheckbox).toBeChecked();
|
107
|
+
await user.type(bodyWeightField, '55');
|
50
108
|
|
51
|
-
// assert
|
52
|
-
|
53
|
-
|
109
|
+
// assert new value
|
110
|
+
expect(bodyWeightField.value).toBe('55');
|
111
|
+
|
112
|
+
// mark as unspecified
|
113
|
+
await user.click(unspecifiedCheckbox);
|
114
|
+
expect(unspecifiedCheckbox).toBeChecked();
|
115
|
+
expect(bodyWeightField.value).toBe('');
|
54
116
|
});
|
55
117
|
|
56
|
-
it('Should
|
118
|
+
it('Should bypass form validation when the "Unspecified" checkbox is clicked', async () => {
|
57
119
|
//setup
|
58
|
-
|
120
|
+
const mockSaveEncounter = jest.spyOn(api, 'saveEncounter');
|
121
|
+
await renderForm();
|
59
122
|
const unspecifiedCheckbox = screen.getByRole('checkbox', { name: /Unspecified/ });
|
60
|
-
const
|
123
|
+
const bodyWeightField = await findNumberInput(screen, 'Body Weight *');
|
61
124
|
|
62
125
|
// assert initial state
|
63
126
|
expect(unspecifiedCheckbox).not.toBeChecked();
|
64
|
-
expect(
|
127
|
+
expect(bodyWeightField.value).toBe('');
|
128
|
+
|
129
|
+
// attempt to submit the form
|
130
|
+
await user.click(screen.getByRole('button', { name: /Save/ }));
|
131
|
+
expect(screen.getByText(/Field is mandatory/)).toBeInTheDocument();
|
132
|
+
expect(mockSaveEncounter).not.toHaveBeenCalled();
|
133
|
+
|
134
|
+
// mark as unspecified
|
135
|
+
await user.click(unspecifiedCheckbox);
|
136
|
+
expect(unspecifiedCheckbox).toBeChecked();
|
137
|
+
expect(bodyWeightField.value).toBe('');
|
138
|
+
|
139
|
+
// submit the form again
|
140
|
+
await user.click(screen.getByRole('button', { name: /Save/ }));
|
141
|
+
expect(mockSaveEncounter).toHaveBeenCalled();
|
142
|
+
});
|
65
143
|
|
66
|
-
|
144
|
+
it('Should mark fields with null values as unspecified when in edit mode', async () => {
|
145
|
+
// setup
|
146
|
+
await renderForm('edit');
|
147
|
+
const unspecifiedCheckbox = screen.getByRole('checkbox', { name: /Unspecified/ });
|
148
|
+
const bodyWeightField = await findNumberInput(screen, 'Body Weight *');
|
67
149
|
|
68
|
-
// assert
|
69
|
-
fireEvent.click(unspecifiedCheckbox);
|
150
|
+
// assert initial state
|
70
151
|
expect(unspecifiedCheckbox).toBeChecked();
|
71
|
-
|
72
|
-
|
152
|
+
expect(bodyWeightField.value).toBe('');
|
153
|
+
});
|
154
|
+
|
155
|
+
it('Should not display the unspecified checkbox in view mode', async () => {
|
156
|
+
// setup
|
157
|
+
await renderForm('view');
|
158
|
+
|
159
|
+
try {
|
160
|
+
screen.getByRole('checkbox', { name: /Unspecified/ });
|
161
|
+
fail('Unspecified checkbox should not be displayed');
|
162
|
+
} catch (error) {
|
163
|
+
expect(error).toBeDefined();
|
164
|
+
}
|
73
165
|
});
|
74
166
|
});
|
@@ -105,6 +105,10 @@ export const FormFieldRenderer = ({ fieldId, valueAdapter, repeatOptions }: Form
|
|
105
105
|
if (field.meta.submission?.warnings) {
|
106
106
|
setWarnings(field.meta.submission.warnings);
|
107
107
|
}
|
108
|
+
if (field.meta.submission?.unspecified) {
|
109
|
+
setErrors([]);
|
110
|
+
removeInvalidField(field.id);
|
111
|
+
}
|
108
112
|
}, [field.meta.submission]);
|
109
113
|
|
110
114
|
const onAfterChange = (value: any) => {
|
@@ -129,10 +129,6 @@ describe('Form Engine Helper', () => {
|
|
129
129
|
// setEncounterRole: jest.fn(),
|
130
130
|
// };
|
131
131
|
|
132
|
-
// beforeEach(() => {
|
133
|
-
// jest.clearAllMocks();
|
134
|
-
// });
|
135
|
-
|
136
132
|
// it('should return true if rendering is toggle and default value is ConceptTrue', () => {
|
137
133
|
// const sampleField: FormField = {
|
138
134
|
// label: 'Sample Toggle Field',
|