@openmrs/esm-form-engine-lib 2.1.0-pre.1540 → 2.1.0-pre.1542

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.
@@ -0,0 +1,93 @@
1
+ {
2
+ "encounterType": "e22e39fd-7db2-45e7-80f1-60fa0d5a4378",
3
+ "name": "Hide Pages and Sections",
4
+ "processor": "EncounterFormProcessor",
5
+ "referencedForms": [],
6
+ "uuid": "7c77485c-7a57-4646-ac21-11d92555a420",
7
+ "version": "1.0",
8
+ "pages": [
9
+ {
10
+ "label": "Page 1",
11
+ "sections": [
12
+ {
13
+ "label": "Section 1A",
14
+ "isExpanded": "true",
15
+ "questions": [
16
+ {
17
+ "id": "hideSection1B",
18
+ "label": "Hide Section 1B",
19
+ "type": "obs",
20
+ "questionOptions": {
21
+ "rendering": "text",
22
+ "concept": "7aef2620-76e0-4d88-b9cb-c47ba4f67bce"
23
+ }
24
+ }
25
+ ]
26
+ },
27
+ {
28
+ "label": "Section 1B",
29
+ "isExpanded": "true",
30
+ "hide": {
31
+ "hideWhenExpression": "isEmpty(hideSection1B)"
32
+ },
33
+ "questions": [
34
+ {
35
+ "id": "hidePage2",
36
+ "label": "Hide Page 2",
37
+ "type": "obs",
38
+ "questionOptions": {
39
+ "rendering": "radio",
40
+ "concept": "1255AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
41
+ "answers": [
42
+ {
43
+ "concept": "1256AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
44
+ "label": "Choice 1",
45
+ "conceptMappings": []
46
+ },
47
+ {
48
+ "concept": "1258AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
49
+ "label": "Choice 2",
50
+ "conceptMappings": []
51
+ },
52
+ {
53
+ "concept": "1259AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
54
+ "label": "Choice 3",
55
+ "conceptMappings": []
56
+ }
57
+ ]
58
+ }
59
+ }
60
+ ]
61
+ }
62
+ ]
63
+ },
64
+ {
65
+ "label": "Page 2",
66
+ "hide": {
67
+ "hideWhenExpression": "hidePage2 === '1258AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'"
68
+ },
69
+ "sections": [
70
+ {
71
+ "label": "Section 2A",
72
+ "isExpanded": "true",
73
+ "questions": [
74
+ {
75
+ "label": "Date",
76
+ "type": "obs",
77
+ "required": false,
78
+ "id": "date",
79
+ "datePickerFormat": "calendar",
80
+ "questionOptions": {
81
+ "rendering": "date",
82
+ "concept": "159599AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
83
+ "conceptMappings": []
84
+ },
85
+ "validators": []
86
+ }
87
+ ]
88
+ }
89
+ ]
90
+ }
91
+ ],
92
+ "description": "Hide Pages and Sections"
93
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-form-engine-lib",
3
- "version": "2.1.0-pre.1540",
3
+ "version": "2.1.0-pre.1542",
4
4
  "description": "React Form Engine for O3",
5
5
  "browser": "dist/openmrs-esm-form-engine-lib.js",
6
6
  "main": "src/index.ts",
@@ -206,9 +206,9 @@ function evaluateFieldDependents(field: FormField, values: any, context: FormCon
206
206
  }
207
207
  shouldUpdateForm = true;
208
208
  });
209
+ }
209
210
 
210
- if (shouldUpdateForm) {
211
- setForm({ ...formJson });
212
- }
211
+ if (shouldUpdateForm) {
212
+ setForm(formJson);
213
213
  }
214
214
  }
@@ -43,6 +43,7 @@ import nextVisitForm from '__mocks__/forms/rfe-forms/next-visit-test-form.json';
43
43
  import viralLoadStatusForm from '__mocks__/forms/rfe-forms/viral-load-status-form.json';
44
44
  import readOnlyValidationForm from '__mocks__/forms/rfe-forms/read-only-validation-form.json';
45
45
  import jsExpressionValidationForm from '__mocks__/forms/rfe-forms/js-expression-validation-form.json';
46
+ import hidePagesAndSectionsForm from '__mocks__/forms/rfe-forms/hide-pages-and-sections-form.json';
46
47
 
47
48
  import FormEngine from './form-engine.component';
48
49
  import { type SessionMode } from './types';
@@ -635,6 +636,49 @@ describe('Form engine component', () => {
635
636
  });
636
637
  });
637
638
 
639
+ describe('Hide pages and sections', () => {
640
+ it('should hide/show section based on field value', async () => {
641
+ await act(async () => renderForm(null, hidePagesAndSectionsForm));
642
+
643
+ // assert section "Section 1B" is hidden at initial render
644
+ try {
645
+ await screen.findByText('Section 1B');
646
+ fail('The section named "Section 1B" should be hidden');
647
+ } catch (err) {
648
+ expect(err.message.includes('Unable to find an element with the text: Section 1B')).toBeTruthy();
649
+ }
650
+
651
+ // user interactions to make section visible
652
+ const hideSection1bField = await findTextOrDateInput(screen, 'Hide Section 1B');
653
+ await user.type(hideSection1bField, 'Some value');
654
+
655
+ const section1b = await screen.findByText('Section 1B');
656
+ expect(section1b).toBeInTheDocument();
657
+ });
658
+
659
+ it('should hide/show page based on field value', async () => {
660
+ await act(async () => renderForm(null, hidePagesAndSectionsForm));
661
+
662
+ // assert page "Page 2" is visible at initial render
663
+ const page2 = await screen.findByText('Page 2');
664
+ expect(page2).toBeInTheDocument();
665
+
666
+ // user interactions to hide page
667
+ const hideSection1bField = await findTextOrDateInput(screen, 'Hide Section 1B');
668
+ await user.type(hideSection1bField, 'Some value');
669
+ const choice2RadioOption = screen.getByRole('radio', { name: /Choice 2/i });
670
+ await user.click(choice2RadioOption);
671
+
672
+ // assert page is hidden
673
+ try {
674
+ await screen.findByText('Page 2');
675
+ fail('The page named "Page 2" should be hidden');
676
+ } catch (err) {
677
+ expect(err.message.includes('Unable to find an element with the text: Page 2')).toBeTruthy();
678
+ }
679
+ });
680
+ });
681
+
638
682
  describe('Calculated values', () => {
639
683
  it('should evaluate BMI', async () => {
640
684
  await act(async () => renderForm(null, bmiForm));
@@ -5,6 +5,7 @@ import { evalConditionalRequired, evaluateConditionalAnswered, evaluateHide } fr
5
5
  import { isTrue } from '../utils/boolean-utils';
6
6
  import { isEmpty } from '../validators/form-validator';
7
7
  import { type QuestionAnswerOption } from '../types/schema';
8
+ import { updateFormSectionReferences } from '../utils/common-utils';
8
9
 
9
10
  export const useEvaluateFormFieldExpressions = (
10
11
  formValues: Record<string, any>,
@@ -125,7 +126,7 @@ export const useEvaluateFormFieldExpressions = (
125
126
  }
126
127
  });
127
128
  });
128
- setEvaluatedFormJson(factoryContext.formJson);
129
+ setEvaluatedFormJson(updateFormSectionReferences(factoryContext.formJson));
129
130
  }, [factoryContext.formJson, formFields]);
130
131
 
131
132
  return { evaluatedFormJson, evaluatedFields };
@@ -2,6 +2,7 @@ import { type Dispatch, useCallback } from 'react';
2
2
  import { type FormField, type FormSchema } from '../types';
3
3
  import { type Action } from '../components/renderer/form/state';
4
4
  import cloneDeep from 'lodash/cloneDeep';
5
+ import { updateFormSectionReferences } from '../utils/common-utils';
5
6
 
6
7
  export function useFormStateHelpers(dispatch: Dispatch<Action>, formFields: FormField[]) {
7
8
  const addFormField = useCallback((field: FormField) => {
@@ -35,7 +36,7 @@ export function useFormStateHelpers(dispatch: Dispatch<Action>, formFields: Form
35
36
  }, []);
36
37
 
37
38
  const setForm = useCallback((formJson: FormSchema) => {
38
- dispatch({ type: 'SET_FORM_JSON', value: formJson });
39
+ dispatch({ type: 'SET_FORM_JSON', value: updateFormSectionReferences(formJson) });
39
40
  }, []);
40
41
 
41
42
  return {
@@ -1,5 +1,5 @@
1
1
  import dayjs from 'dayjs';
2
- import { type FormField, type OpenmrsObs, type RenderType } from '../types';
2
+ import { type FormSchema, type FormField, type OpenmrsObs, type RenderType } from '../types';
3
3
  import { isEmpty } from '../validators/form-validator';
4
4
  import { formatDate, type FormatDateOptions } from '@openmrs/esm-framework';
5
5
 
@@ -77,3 +77,15 @@ export function formatDateAsDisplayString(field: FormField, date: Date) {
77
77
  }
78
78
  return formatDate(date, options);
79
79
  }
80
+
81
+ /**
82
+ * Creates a new copy of `formJson` with updated references at the page and section levels.
83
+ * This ensures React re-renders properly by providing new references for nested arrays.
84
+ */
85
+ export function updateFormSectionReferences(formJson: FormSchema) {
86
+ formJson.pages = formJson.pages.map((page) => {
87
+ page.sections = Array.from(page.sections);
88
+ return page;
89
+ });
90
+ return { ...formJson };
91
+ }