@openmrs/esm-form-engine-lib 2.1.0-pre.1564 → 2.1.0-pre.1565

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-form-engine-lib",
3
- "version": "2.1.0-pre.1564",
3
+ "version": "2.1.0-pre.1565",
4
4
  "description": "React Form Engine for O3",
5
5
  "browser": "dist/openmrs-esm-form-engine-lib.js",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  import { codedTypes } from '../../../constants';
2
2
  import { type FormContextProps } from '../../../provider/form-provider';
3
- import { type FormField } from '../../../types';
3
+ import { type FormFieldValidator, type SessionMode, type ValidationResult, type FormField } from '../../../types';
4
4
  import { isTrue } from '../../../utils/boolean-utils';
5
5
  import { hasRendering } from '../../../utils/common-utils';
6
6
  import { evaluateAsyncExpression, evaluateExpression } from '../../../utils/expression-runner';
@@ -65,6 +65,21 @@ function evaluateFieldDependents(field: FormField, values: any, context: FormCon
65
65
  },
66
66
  ).then((result) => {
67
67
  setValue(dependent.id, result);
68
+ // validate calculated value
69
+ const { errors, warnings } = validateFieldValue(dependent, result, context.formFieldValidators, {
70
+ formFields,
71
+ values,
72
+ expressionContext: { patient, mode: sessionMode },
73
+ });
74
+ if (!dependent.meta.submission) {
75
+ dependent.meta.submission = {};
76
+ }
77
+ dependent.meta.submission.errors = errors;
78
+ dependent.meta.submission.warnings = warnings;
79
+ if (!errors.length) {
80
+ context.formFieldAdapters[dependent.type].transformFieldValue(dependent, result, context);
81
+ }
82
+ updateFormField(dependent);
68
83
  });
69
84
  }
70
85
  // evaluate hide
@@ -212,3 +227,48 @@ function evaluateFieldDependents(field: FormField, values: any, context: FormCon
212
227
  setForm(formJson);
213
228
  }
214
229
  }
230
+
231
+ export interface ValidatorConfig {
232
+ formFields: FormField[];
233
+ values: Record<string, any>;
234
+ expressionContext: {
235
+ patient: fhir.Patient;
236
+ mode: SessionMode;
237
+ };
238
+ }
239
+
240
+ export function validateFieldValue(
241
+ field: FormField,
242
+ value: any,
243
+ validators: Record<string, FormFieldValidator>,
244
+ context: ValidatorConfig,
245
+ ): { errors: ValidationResult[]; warnings: ValidationResult[] } {
246
+ const errors: ValidationResult[] = [];
247
+ const warnings: ValidationResult[] = [];
248
+
249
+ if (field.meta.submission?.unspecified) {
250
+ return { errors: [], warnings: [] };
251
+ }
252
+
253
+ try {
254
+ field.validators.forEach((validatorConfig) => {
255
+ const results = validators[validatorConfig.type]?.validate?.(field, value, {
256
+ ...validatorConfig,
257
+ ...context,
258
+ });
259
+ if (results) {
260
+ results.forEach((result) => {
261
+ if (result.resultType === 'error') {
262
+ errors.push(result);
263
+ } else if (result.resultType === 'warning') {
264
+ warnings.push(result);
265
+ }
266
+ });
267
+ }
268
+ });
269
+ } catch (error) {
270
+ console.error(error);
271
+ }
272
+
273
+ return { errors, warnings };
274
+ }
@@ -21,7 +21,7 @@ import { getFieldControlWithFallback, getRegisteredControl } from '../../../regi
21
21
  import styles from './form-field-renderer.scss';
22
22
  import { isTrue } from '../../../utils/boolean-utils';
23
23
  import UnspecifiedField from '../../inputs/unspecified/unspecified.component';
24
- import { handleFieldLogic } from './fieldLogic';
24
+ import { handleFieldLogic, validateFieldValue } from './fieldLogic';
25
25
 
26
26
  export interface FormFieldRendererProps {
27
27
  fieldId: string;
@@ -221,51 +221,6 @@ function ErrorFallback({ error }) {
221
221
  );
222
222
  }
223
223
 
224
- export interface ValidatorConfig {
225
- formFields: FormField[];
226
- values: Record<string, any>;
227
- expressionContext: {
228
- patient: fhir.Patient;
229
- mode: SessionMode;
230
- };
231
- }
232
-
233
- function validateFieldValue(
234
- field: FormField,
235
- value: any,
236
- validators: Record<string, FormFieldValidator>,
237
- context: ValidatorConfig,
238
- ): { errors: ValidationResult[]; warnings: ValidationResult[] } {
239
- const errors: ValidationResult[] = [];
240
- const warnings: ValidationResult[] = [];
241
-
242
- if (field.meta.submission?.unspecified) {
243
- return { errors: [], warnings: [] };
244
- }
245
-
246
- try {
247
- field.validators.forEach((validatorConfig) => {
248
- const results = validators[validatorConfig.type]?.validate?.(field, value, {
249
- ...validatorConfig,
250
- ...context,
251
- });
252
- if (results) {
253
- results.forEach((result) => {
254
- if (result.resultType === 'error') {
255
- errors.push(result);
256
- } else if (result.resultType === 'warning') {
257
- warnings.push(result);
258
- }
259
- });
260
- }
261
- });
262
- } catch (error) {
263
- console.error(error);
264
- }
265
-
266
- return { errors, warnings };
267
- }
268
-
269
224
  /**
270
225
  * Determines whether a field can be unspecified
271
226
  */
@@ -681,6 +681,8 @@ describe('Form engine component', () => {
681
681
 
682
682
  describe('Calculated values', () => {
683
683
  it('should evaluate BMI', async () => {
684
+ const saveEncounterMock = jest.spyOn(api, 'saveEncounter');
685
+
684
686
  await act(async () => renderForm(null, bmiForm));
685
687
 
686
688
  const bmiField = screen.getByRole('textbox', { name: /bmi/i });
@@ -694,9 +696,17 @@ describe('Form engine component', () => {
694
696
  expect(heightField).toHaveValue(150);
695
697
  expect(weightField).toHaveValue(50);
696
698
  expect(bmiField).toHaveValue('22.2');
699
+
700
+ await user.click(screen.getByRole('button', { name: /save/i }));
701
+
702
+ const encounter = saveEncounterMock.mock.calls[0][1];
703
+ expect(encounter.obs.length).toEqual(3);
704
+ expect(encounter.obs.find((obs) => obs.formFieldPath === 'rfe-forms-bmi').value).toBe(22.2);
697
705
  });
698
706
 
699
707
  it('should evaluate BSA', async () => {
708
+ const saveEncounterMock = jest.spyOn(api, 'saveEncounter');
709
+
700
710
  await act(async () => renderForm(null, bsaForm));
701
711
 
702
712
  const bsaField = screen.getByRole('textbox', { name: /bsa/i });
@@ -710,6 +720,12 @@ describe('Form engine component', () => {
710
720
  expect(heightField).toHaveValue(190.5);
711
721
  expect(weightField).toHaveValue(95);
712
722
  expect(bsaField).toHaveValue('2.24');
723
+
724
+ await user.click(screen.getByRole('button', { name: /save/i }));
725
+
726
+ const encounter = saveEncounterMock.mock.calls[0][1];
727
+ expect(encounter.obs.length).toEqual(3);
728
+ expect(encounter.obs.find((obs) => obs.formFieldPath === 'rfe-forms-bsa').value).toBe(2.24);
713
729
  });
714
730
 
715
731
  it('should evaluate EDD', async () => {