@openmrs/esm-form-engine-lib 3.3.1-pre.2179 → 3.3.1-pre.2185
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/expression-visit-object-test.json +29 -0
- package/__mocks__/forms/rfe-forms/index.ts +1 -0
- package/package.json +1 -1
- package/src/components/renderer/field/fieldLogic.ts +8 -1
- package/src/components/repeat/repeat.component.tsx +3 -0
- package/src/form-engine.test.tsx +15 -0
- package/src/hooks/useEvaluateFormFieldExpressions.ts +2 -1
- package/src/processors/encounter/encounter-form-processor.ts +4 -1
- package/src/provider/form-factory-provider.tsx +3 -2
- package/src/types/index.ts +2 -2
- package/src/utils/expression-runner.ts +5 -2
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Visit Form",
|
|
3
|
+
"pages": [
|
|
4
|
+
{
|
|
5
|
+
"label": "Page 1",
|
|
6
|
+
"sections": [
|
|
7
|
+
{
|
|
8
|
+
"label": "Section 1",
|
|
9
|
+
"isExpanded": "true",
|
|
10
|
+
"questions": [
|
|
11
|
+
{
|
|
12
|
+
"label": "Encounter Date",
|
|
13
|
+
"type": "encounterDatetime",
|
|
14
|
+
"id": "encounterDate",
|
|
15
|
+
"questionOptions": {
|
|
16
|
+
"rendering": "date",
|
|
17
|
+
"calculate": {
|
|
18
|
+
"calculateExpression": "visit && visit?.startDatetime ? new Date(visit?.startDatetime) : null"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"uuid": "form-visit-test",
|
|
28
|
+
"encounterType": "b9c1f50f-f77d-42e2-ad2a-d29304dde2fv"
|
|
29
|
+
}
|
|
@@ -40,6 +40,7 @@ export { default as uiSelectExtForm } from './sample-ui-select-ext.json';
|
|
|
40
40
|
export { default as unspecifiedForm } from './sample-unspecified-form.json';
|
|
41
41
|
export { default as viralLoadStatusForm } from './viral-load-status-form.json';
|
|
42
42
|
export { default as weightForHeightZscoreTestSchema } from './zscore-weight-height-form.json';
|
|
43
|
+
export { default as expressionVisitObjectTestSchema } from './expression-visit-object-test.json';
|
|
43
44
|
|
|
44
45
|
export { obsList } from './obs-list-data';
|
|
45
46
|
export {
|
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@ export function handleFieldLogic(field: FormField, context: FormContextProps) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function evaluateFieldAnswerDisabled(field: FormField, values: Record<string, any>, context: FormContextProps) {
|
|
22
|
-
const { sessionMode, formFields, patient } = context;
|
|
22
|
+
const { sessionMode, formFields, patient, visit } = context;
|
|
23
23
|
field.questionOptions.answers.forEach((answer) => {
|
|
24
24
|
const disableExpression = answer.disable?.disableWhenExpression;
|
|
25
25
|
if (disableExpression && disableExpression.includes('myValue')) {
|
|
@@ -31,6 +31,7 @@ function evaluateFieldAnswerDisabled(field: FormField, values: Record<string, an
|
|
|
31
31
|
{
|
|
32
32
|
mode: sessionMode,
|
|
33
33
|
patient,
|
|
34
|
+
visit,
|
|
34
35
|
},
|
|
35
36
|
);
|
|
36
37
|
}
|
|
@@ -47,6 +48,7 @@ function evaluateFieldDependents(field: FormField, values: any, context: FormCon
|
|
|
47
48
|
methods: { setValue },
|
|
48
49
|
updateFormField,
|
|
49
50
|
setForm,
|
|
51
|
+
visit,
|
|
50
52
|
} = context;
|
|
51
53
|
|
|
52
54
|
let shouldUpdateForm = false;
|
|
@@ -64,6 +66,7 @@ function evaluateFieldDependents(field: FormField, values: any, context: FormCon
|
|
|
64
66
|
{
|
|
65
67
|
mode: sessionMode,
|
|
66
68
|
patient,
|
|
69
|
+
visit,
|
|
67
70
|
},
|
|
68
71
|
)
|
|
69
72
|
.then((result) => {
|
|
@@ -169,6 +172,7 @@ function evaluateFieldDependents(field: FormField, values: any, context: FormCon
|
|
|
169
172
|
{
|
|
170
173
|
mode: sessionMode,
|
|
171
174
|
patient,
|
|
175
|
+
visit,
|
|
172
176
|
},
|
|
173
177
|
);
|
|
174
178
|
});
|
|
@@ -184,6 +188,7 @@ function evaluateFieldDependents(field: FormField, values: any, context: FormCon
|
|
|
184
188
|
{
|
|
185
189
|
mode: sessionMode,
|
|
186
190
|
patient,
|
|
191
|
+
visit,
|
|
187
192
|
},
|
|
188
193
|
);
|
|
189
194
|
});
|
|
@@ -197,6 +202,7 @@ function evaluateFieldDependents(field: FormField, values: any, context: FormCon
|
|
|
197
202
|
{
|
|
198
203
|
mode: sessionMode,
|
|
199
204
|
patient,
|
|
205
|
+
visit,
|
|
200
206
|
},
|
|
201
207
|
);
|
|
202
208
|
}
|
|
@@ -210,6 +216,7 @@ function evaluateFieldDependents(field: FormField, values: any, context: FormCon
|
|
|
210
216
|
{
|
|
211
217
|
mode: sessionMode,
|
|
212
218
|
patient,
|
|
219
|
+
visit,
|
|
213
220
|
},
|
|
214
221
|
);
|
|
215
222
|
}
|
|
@@ -35,6 +35,7 @@ const Repeat: React.FC<FormFieldInputProps> = ({ field }) => {
|
|
|
35
35
|
removeFormField,
|
|
36
36
|
deletedFields,
|
|
37
37
|
setDeletedFields,
|
|
38
|
+
visit,
|
|
38
39
|
} = context;
|
|
39
40
|
|
|
40
41
|
useEffect(() => {
|
|
@@ -61,6 +62,7 @@ const Repeat: React.FC<FormFieldInputProps> = ({ field }) => {
|
|
|
61
62
|
{
|
|
62
63
|
mode: sessionMode,
|
|
63
64
|
patient: patient,
|
|
65
|
+
visit,
|
|
64
66
|
},
|
|
65
67
|
);
|
|
66
68
|
}
|
|
@@ -73,6 +75,7 @@ const Repeat: React.FC<FormFieldInputProps> = ({ field }) => {
|
|
|
73
75
|
{
|
|
74
76
|
mode: sessionMode,
|
|
75
77
|
patient: patient,
|
|
78
|
+
visit,
|
|
76
79
|
},
|
|
77
80
|
).then((result) => {
|
|
78
81
|
if (!isEmpty(result)) {
|
package/src/form-engine.test.tsx
CHANGED
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
sampleFieldsForm,
|
|
46
46
|
testEnrolmentForm,
|
|
47
47
|
viralLoadStatusForm,
|
|
48
|
+
expressionVisitObjectTestSchema,
|
|
48
49
|
} from '__mocks__/forms';
|
|
49
50
|
import { type FormSchema, type OpenmrsEncounter, type SessionMode } from './types';
|
|
50
51
|
import { useEncounter } from './hooks/useEncounter';
|
|
@@ -1260,6 +1261,20 @@ describe('Form engine component', () => {
|
|
|
1260
1261
|
});
|
|
1261
1262
|
});
|
|
1262
1263
|
});
|
|
1264
|
+
describe('Form calculate expression integration', () => {
|
|
1265
|
+
it('should calculate encounter date from visit startDatetime', async () => {
|
|
1266
|
+
await act(async () => {
|
|
1267
|
+
return renderForm(null, expressionVisitObjectTestSchema, 'enter', 'enter', null);
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
await waitFor(() => {
|
|
1271
|
+
const dateInput = screen.getByLabelText('Encounter Date') as HTMLInputElement;
|
|
1272
|
+
expect(dateInput).toBeInTheDocument();
|
|
1273
|
+
|
|
1274
|
+
expect(dateInput.value).toContain('28/07/2020');
|
|
1275
|
+
});
|
|
1276
|
+
});
|
|
1277
|
+
});
|
|
1263
1278
|
|
|
1264
1279
|
function renderForm(formUUID, formJson, intent?: string, mode?: SessionMode, encounterUUID?: string) {
|
|
1265
1280
|
render(
|
|
@@ -11,7 +11,7 @@ export const useEvaluateFormFieldExpressions = (
|
|
|
11
11
|
formValues: Record<string, any>,
|
|
12
12
|
factoryContext: FormProcessorContextProps,
|
|
13
13
|
) => {
|
|
14
|
-
const { formFields, patient, sessionMode } = factoryContext;
|
|
14
|
+
const { formFields, patient, sessionMode, visit } = factoryContext;
|
|
15
15
|
const [evaluatedFormJson, setEvaluatedFormJson] = useState(factoryContext.formJson);
|
|
16
16
|
const [evaluatedPagesVisibility, setEvaluatedPagesVisibility] = useState(false);
|
|
17
17
|
|
|
@@ -21,6 +21,7 @@ export const useEvaluateFormFieldExpressions = (
|
|
|
21
21
|
const runnerContext = {
|
|
22
22
|
patient,
|
|
23
23
|
mode: sessionMode,
|
|
24
|
+
visit,
|
|
24
25
|
};
|
|
25
26
|
// evaluate hide
|
|
26
27
|
if (field.hide?.hideWhenExpression) {
|
|
@@ -330,6 +330,7 @@ export class EncounterFormProcessor extends FormProcessor {
|
|
|
330
330
|
methods: { getValues },
|
|
331
331
|
formFieldAdapters,
|
|
332
332
|
previousDomainObjectValue,
|
|
333
|
+
visit,
|
|
333
334
|
} = context;
|
|
334
335
|
const node: FormNode = { value: field, type: 'field' };
|
|
335
336
|
const adapter = formFieldAdapters[field.type];
|
|
@@ -338,6 +339,7 @@ export class EncounterFormProcessor extends FormProcessor {
|
|
|
338
339
|
mode: sessionMode,
|
|
339
340
|
patient: patient,
|
|
340
341
|
previousEncounter: previousDomainObjectValue,
|
|
342
|
+
visit,
|
|
341
343
|
});
|
|
342
344
|
return value ? extractObsValueAndDisplay(field, value) : null;
|
|
343
345
|
}
|
|
@@ -353,12 +355,13 @@ async function evaluateCalculateExpression(
|
|
|
353
355
|
values: Record<string, any>,
|
|
354
356
|
formContext: FormProcessorContextProps,
|
|
355
357
|
) {
|
|
356
|
-
const { formFields, sessionMode, patient } = formContext;
|
|
358
|
+
const { formFields, sessionMode, patient, visit } = formContext;
|
|
357
359
|
const expression = field.questionOptions.calculate.calculateExpression;
|
|
358
360
|
const node: FormNode = { value: field, type: 'field' };
|
|
359
361
|
const context = {
|
|
360
362
|
mode: sessionMode,
|
|
361
363
|
patient: patient,
|
|
364
|
+
visit,
|
|
362
365
|
};
|
|
363
366
|
const value = await evaluateAsyncExpression(expression, node, formFields, values, context);
|
|
364
367
|
if (!isEmpty(value)) {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
showSnackbar,
|
|
9
9
|
showToast,
|
|
10
10
|
type ToastDescriptor,
|
|
11
|
+
Visit,
|
|
11
12
|
} from '@openmrs/esm-framework';
|
|
12
13
|
import { type FormProcessorConstructor } from '../processors/form-processor';
|
|
13
14
|
import { type FormContextProps } from './form-provider';
|
|
@@ -24,7 +25,7 @@ interface FormFactoryProviderContextProps {
|
|
|
24
25
|
formProcessors: Record<string, FormProcessorConstructor>;
|
|
25
26
|
layoutType: LayoutType;
|
|
26
27
|
workspaceLayout: 'minimized' | 'maximized';
|
|
27
|
-
visit:
|
|
28
|
+
visit: Visit;
|
|
28
29
|
location: OpenmrsResource;
|
|
29
30
|
provider: OpenmrsResource;
|
|
30
31
|
isFormExpanded: boolean;
|
|
@@ -42,7 +43,7 @@ interface FormFactoryProviderProps {
|
|
|
42
43
|
workspaceLayout: 'minimized' | 'maximized';
|
|
43
44
|
location: OpenmrsResource;
|
|
44
45
|
provider: OpenmrsResource;
|
|
45
|
-
visit:
|
|
46
|
+
visit: Visit;
|
|
46
47
|
isFormExpanded: boolean;
|
|
47
48
|
children: React.ReactNode;
|
|
48
49
|
formSubmissionProps: {
|
package/src/types/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type LayoutType, type OpenmrsResource } from '@openmrs/esm-framework';
|
|
1
|
+
import { type Visit, type LayoutType, type OpenmrsResource } from '@openmrs/esm-framework';
|
|
2
2
|
import { type FormProcessor } from '../processors/form-processor';
|
|
3
3
|
import { type FormContextProps } from '../provider/form-provider';
|
|
4
4
|
import { type FormField, type FormSchema } from './schema';
|
|
@@ -9,7 +9,7 @@ export type SessionMode = 'edit' | 'enter' | 'view' | 'embedded-view';
|
|
|
9
9
|
export interface FormProcessorContextProps {
|
|
10
10
|
patient: fhir.Patient;
|
|
11
11
|
formJson: FormSchema;
|
|
12
|
-
visit:
|
|
12
|
+
visit: Visit;
|
|
13
13
|
sessionMode: SessionMode;
|
|
14
14
|
sessionDate: Date;
|
|
15
15
|
location: OpenmrsResource;
|
|
@@ -55,7 +55,6 @@ export function evaluateExpression(
|
|
|
55
55
|
const compiledExpression = getExpressionAst(expression);
|
|
56
56
|
// track dependencies
|
|
57
57
|
trackFieldDependencies(compiledExpression, node, fields);
|
|
58
|
-
|
|
59
58
|
try {
|
|
60
59
|
return evaluateAsType(compiledExpression, getEvaluationContext(node, fields, fieldValues, context), typePredicate);
|
|
61
60
|
} catch (error) {
|
|
@@ -74,6 +73,7 @@ export async function evaluateAsyncExpression(
|
|
|
74
73
|
if (!expression?.trim()) {
|
|
75
74
|
return null;
|
|
76
75
|
}
|
|
76
|
+
|
|
77
77
|
const compiledExpression = getExpressionAst(expression);
|
|
78
78
|
// track dependencies
|
|
79
79
|
trackFieldDependencies(compiledExpression, node, fields);
|
|
@@ -110,7 +110,8 @@ function getEvaluationContext(
|
|
|
110
110
|
},
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
-
const
|
|
113
|
+
const visit: Visit = context?.visit ?? ({} as Visit);
|
|
114
|
+
const visitType = visit?.visitType || { uuid: '' };
|
|
114
115
|
const visitTypeUuid = visitType.uuid ?? '';
|
|
115
116
|
|
|
116
117
|
const _ = {
|
|
@@ -127,6 +128,7 @@ function getEvaluationContext(
|
|
|
127
128
|
sex,
|
|
128
129
|
age,
|
|
129
130
|
HD,
|
|
131
|
+
visit,
|
|
130
132
|
visitType,
|
|
131
133
|
visitTypeUuid,
|
|
132
134
|
_,
|
|
@@ -160,6 +162,7 @@ export function trackFieldDependencies(
|
|
|
160
162
|
allFields: FormField[],
|
|
161
163
|
) {
|
|
162
164
|
const variables = extractVariableNames(expression);
|
|
165
|
+
|
|
163
166
|
for (const variable of variables) {
|
|
164
167
|
const field = allFields.find((field) => field.id === variable);
|
|
165
168
|
if (field) {
|