@openmrs/esm-form-engine-lib 3.0.0-pre.1637 → 3.0.0-pre.1644

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": "3.0.0-pre.1637",
3
+ "version": "3.0.0-pre.1644",
4
4
  "description": "React Form Engine for O3",
5
5
  "browser": "dist/openmrs-esm-form-engine-lib.js",
6
6
  "main": "src/index.ts",
@@ -70,14 +70,14 @@ const NumberField: React.FC<FormFieldInputProps> = ({ field, value, errors, warn
70
70
  onBlur={onBlur}
71
71
  allowEmpty={true}
72
72
  size="lg"
73
- hideSteppers={true}
73
+ hideSteppers={field.hideSteppers ?? false}
74
74
  onWheel={(e) => e.target.blur()}
75
75
  disabled={field.isDisabled}
76
76
  readOnly={isTrue(field.readonly)}
77
77
  className={classNames(styles.controlWidthConstrained, styles.boldedLabel)}
78
78
  warn={warnings.length > 0}
79
79
  warnText={warnings[0]?.message}
80
- step={0.01}
80
+ step={field.questionOptions.step ?? 0.01}
81
81
  />
82
82
  </Layer>
83
83
  )
@@ -1,21 +1,21 @@
1
1
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { debounce } from 'lodash-es';
3
3
  import { ComboBox, DropdownSkeleton, Layer, InlineLoading } from '@carbon/react';
4
- import { isTrue } from '../../../utils/boolean-utils';
5
4
  import { useTranslation } from 'react-i18next';
6
- import { getRegisteredDataSource } from '../../../registry/registry';
5
+ import { useWatch } from 'react-hook-form';
6
+ import { type OpenmrsResource } from '@openmrs/esm-framework';
7
7
  import { getControlTemplate } from '../../../registry/inbuilt-components/control-templates';
8
- import { type DataSource, type FormFieldInputProps } from '../../../types';
8
+ import { getRegisteredDataSource } from '../../../registry/registry';
9
9
  import { isEmpty } from '../../../validators/form-validator';
10
+ import { isTrue } from '../../../utils/boolean-utils';
11
+ import { isViewMode } from '../../../utils/common-utils';
10
12
  import { shouldUseInlineLayout } from '../../../utils/form-helper';
11
- import FieldValueView from '../../value/view/field-value-view.component';
12
- import styles from './ui-select-extended.scss';
13
+ import { type DataSource, type FormFieldInputProps } from '../../../types';
13
14
  import { useFormProviderContext } from '../../../provider/form-provider';
14
- import FieldLabel from '../../field-label/field-label.component';
15
- import { useWatch } from 'react-hook-form';
16
15
  import useDataSourceDependentValue from '../../../hooks/useDataSourceDependentValue';
17
- import { isViewMode } from '../../../utils/common-utils';
18
- import { type OpenmrsResource } from '@openmrs/esm-framework';
16
+ import FieldLabel from '../../field-label/field-label.component';
17
+ import FieldValueView from '../../value/view/field-value-view.component';
18
+ import styles from './ui-select-extended.scss';
19
19
 
20
20
  const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnings, setFieldValue }) => {
21
21
  const { t } = useTranslation();
@@ -49,6 +49,7 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
49
49
 
50
50
  const debouncedSearch = debounce((searchTerm: string, dataSource: DataSource<OpenmrsResource>) => {
51
51
  setIsSearching(true);
52
+
52
53
  dataSource
53
54
  .fetchData(searchTerm, config)
54
55
  .then((dataItems) => {
@@ -86,22 +87,33 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
86
87
  }, [field.questionOptions?.datasource]);
87
88
 
88
89
  useEffect(() => {
90
+ let ignore = false;
91
+
89
92
  // If not searchable, preload the items
90
93
  if (dataSource && !isTrue(field.questionOptions.isSearchable)) {
91
94
  setItems([]);
92
95
  setIsLoading(true);
96
+
93
97
  dataSource
94
98
  .fetchData(null, { ...config, referencedValue: dataSourceDependentValue })
95
99
  .then((dataItems) => {
96
- setItems(dataItems.map(dataSource.toUuidAndDisplay));
97
- setIsLoading(false);
100
+ if (!ignore) {
101
+ setItems(dataItems.map(dataSource.toUuidAndDisplay));
102
+ setIsLoading(false);
103
+ }
98
104
  })
99
105
  .catch((err) => {
100
- console.error(err);
101
- setIsLoading(false);
102
- setItems([]);
106
+ if (!ignore) {
107
+ console.error(err);
108
+ setIsLoading(false);
109
+ setItems([]);
110
+ }
103
111
  });
104
112
  }
113
+
114
+ return () => {
115
+ ignore = true;
116
+ };
105
117
  }, [dataSource, config, dataSourceDependentValue]);
106
118
 
107
119
  useEffect(() => {
@@ -111,19 +123,28 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
111
123
  }, [dataSource, searchTerm, config]);
112
124
 
113
125
  useEffect(() => {
126
+ let ignore = false;
114
127
  if (value && !isDirty && dataSource && isSearchable && sessionMode !== 'enter' && !items.length) {
115
128
  // While in edit mode, search-based instances should fetch the initial item (previously selected value) to resolve its display property
116
129
  setIsLoading(true);
117
130
  try {
118
131
  dataSource.fetchSingleItem(value).then((item) => {
119
- setItems([dataSource.toUuidAndDisplay(item)]);
120
- setIsLoading(false);
132
+ if (!ignore) {
133
+ setItems([dataSource.toUuidAndDisplay(item)]);
134
+ setIsLoading(false);
135
+ }
121
136
  });
122
137
  } catch (error) {
123
- console.error(error);
124
- setIsLoading(false);
138
+ if (!ignore) {
139
+ console.error(error);
140
+ setIsLoading(false);
141
+ }
125
142
  }
126
143
  }
144
+
145
+ return () => {
146
+ ignore = true;
147
+ };
127
148
  }, [value, isDirty, sessionMode, dataSource, isSearchable, items]);
128
149
 
129
150
  if (isLoading) {
@@ -11,8 +11,12 @@ export function useEncounter(formJson: FormSchema) {
11
11
  const [error, setError] = useState(null);
12
12
 
13
13
  useEffect(() => {
14
+ const abortController = new AbortController();
15
+
14
16
  if (!isEmpty(formJson.encounter) && isString(formJson.encounter)) {
15
- openmrsFetch(`${restBaseUrl}/encounter/${formJson.encounter}?v=${encounterRepresentation}`)
17
+ openmrsFetch(`${restBaseUrl}/encounter/${formJson.encounter}?v=${encounterRepresentation}`, {
18
+ signal: abortController.signal,
19
+ })
16
20
  .then((response) => {
17
21
  setEncounter(response.data);
18
22
  setIsLoading(false);
@@ -26,6 +30,10 @@ export function useEncounter(formJson: FormSchema) {
26
30
  } else {
27
31
  setIsLoading(false);
28
32
  }
33
+
34
+ return () => {
35
+ abortController.abort();
36
+ };
29
37
  }, [formJson.encounter]);
30
38
 
31
39
  return { encounter: encounter, error, isLoading };
@@ -11,6 +11,8 @@ export function useFormJson(formUuid: string, rawFormJson: any, encounterUuid: s
11
11
  const [error, setError] = useState(validateFormsArgs(formUuid, rawFormJson));
12
12
 
13
13
  useEffect(() => {
14
+ const abortController = new AbortController();
15
+
14
16
  const setFormJsonWithTranslations = (formJson: FormSchema) => {
15
17
  if (formJson?.translations) {
16
18
  const language = window.i18next.language;
@@ -24,9 +26,15 @@ export function useFormJson(formUuid: string, rawFormJson: any, encounterUuid: s
24
26
  setFormJsonWithTranslations({ ...formJson, encounter: encounterUuid });
25
27
  })
26
28
  .catch((error) => {
27
- console.error(error);
28
- setError(new Error('Error loading form JSON: ' + error.message));
29
+ if (error.name !== 'AbortError') {
30
+ console.error(error);
31
+ setError(new Error('Error loading form JSON: ' + error.message));
32
+ }
29
33
  });
34
+
35
+ return () => {
36
+ abortController.abort();
37
+ };
30
38
  }, [formSessionIntent, formUuid, rawFormJson, encounterUuid]);
31
39
 
32
40
  return {
@@ -9,19 +9,29 @@ export const usePatientPrograms = (patientUuid: string, formJson: FormSchema) =>
9
9
  const [error, setError] = useState(null);
10
10
 
11
11
  useEffect(() => {
12
+ const abortController = new AbortController();
13
+
12
14
  if (formJson.meta?.programs?.hasProgramFields) {
13
- openmrsFetch(`${restBaseUrl}/programenrollment?patient=${patientUuid}&v=${customRepresentation}`)
15
+ openmrsFetch(`${restBaseUrl}/programenrollment?patient=${patientUuid}&v=${customRepresentation}`, {
16
+ signal: abortController.signal,
17
+ })
14
18
  .then((response) => {
15
19
  setPatientPrograms(response.data.results.filter((enrollment) => enrollment.dateCompleted === null));
16
20
  setIsLoading(false);
17
21
  })
18
22
  .catch((error) => {
19
- setError(error);
20
- setIsLoading(false);
23
+ if (error.name !== 'AbortError') {
24
+ setError(error);
25
+ setIsLoading(false);
26
+ }
21
27
  });
22
28
  } else {
23
29
  setIsLoading(false);
24
30
  }
31
+
32
+ return () => {
33
+ abortController.abort();
34
+ };
25
35
  }, [formJson]);
26
36
 
27
37
  return {
@@ -13,17 +13,27 @@ const useProcessorDependencies = (
13
13
  const { loadDependencies } = formProcessor;
14
14
 
15
15
  useEffect(() => {
16
+ let ignore = false;
17
+
16
18
  if (loadDependencies) {
17
19
  setIsLoading(true);
18
20
  loadDependencies(context, setContext)
19
- .then((results) => {
20
- setIsLoading(false);
21
+ .then(() => {
22
+ if (!ignore) {
23
+ setIsLoading(false);
24
+ }
21
25
  })
22
26
  .catch((error) => {
23
- setError(error);
24
- reportError(error, 'Encountered error while loading dependencies');
27
+ if (!ignore) {
28
+ setError(error);
29
+ reportError(error, 'Encountered error while loading dependencies');
30
+ }
25
31
  });
26
32
  }
33
+
34
+ return () => {
35
+ ignore = true;
36
+ };
27
37
  }, [loadDependencies]);
28
38
 
29
39
  return { isLoading, error };
@@ -84,6 +84,7 @@ export interface FormField {
84
84
  questionInfo?: string;
85
85
  historicalExpression?: string;
86
86
  constrainMaxWidth?: boolean;
87
+ hideSteppers?: boolean;
87
88
  /** @deprecated */
88
89
  inlineMultiCheckbox?: boolean;
89
90
  meta?: QuestionMetaProps;
@@ -143,6 +144,10 @@ export interface FormQuestionOptions {
143
144
  */
144
145
  max?: string;
145
146
  min?: string;
147
+ /**
148
+ * specifies the increment or decrement step for number field values
149
+ */
150
+ step?: number;
146
151
  /**
147
152
  * maxLength and maxLength are used to validate text field length
148
153
  */