@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/2f7bdab923addd01/2f7bdab923addd01.gz +0 -0
- package/b993f7ebd85e96c5/b993f7ebd85e96c5.gz +0 -0
- package/package.json +1 -1
- package/src/components/inputs/number/number.component.tsx +2 -2
- package/src/components/inputs/ui-select-extended/ui-select-extended.component.tsx +39 -18
- package/src/hooks/useEncounter.ts +9 -1
- package/src/hooks/useFormJson.ts +10 -2
- package/src/hooks/usePatientPrograms.ts +13 -3
- package/src/hooks/useProcessorDependencies.ts +14 -4
- package/src/types/schema.ts +5 -0
- package/1f09c0a0fe19adb9/1f09c0a0fe19adb9.gz +0 -0
- package/d85de193c6bc805e/d85de193c6bc805e.gz +0 -0
- /package/{061e32979240d73f/061e32979240d73f.gz → acad44ae2a2118d5/acad44ae2a2118d5.gz} +0 -0
- /package/{3ccbe3e56feebb71/3ccbe3e56feebb71.gz → afed242d3ca98bd3/afed242d3ca98bd3.gz} +0 -0
Binary file
|
Binary file
|
package/package.json
CHANGED
@@ -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={
|
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 {
|
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 {
|
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
|
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
|
18
|
-
import
|
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
|
-
|
97
|
-
|
100
|
+
if (!ignore) {
|
101
|
+
setItems(dataItems.map(dataSource.toUuidAndDisplay));
|
102
|
+
setIsLoading(false);
|
103
|
+
}
|
98
104
|
})
|
99
105
|
.catch((err) => {
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
120
|
-
|
132
|
+
if (!ignore) {
|
133
|
+
setItems([dataSource.toUuidAndDisplay(item)]);
|
134
|
+
setIsLoading(false);
|
135
|
+
}
|
121
136
|
});
|
122
137
|
} catch (error) {
|
123
|
-
|
124
|
-
|
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 };
|
package/src/hooks/useFormJson.ts
CHANGED
@@ -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
|
-
|
28
|
-
|
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
|
-
|
20
|
-
|
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((
|
20
|
-
|
21
|
+
.then(() => {
|
22
|
+
if (!ignore) {
|
23
|
+
setIsLoading(false);
|
24
|
+
}
|
21
25
|
})
|
22
26
|
.catch((error) => {
|
23
|
-
|
24
|
-
|
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 };
|
package/src/types/schema.ts
CHANGED
@@ -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
|
*/
|
Binary file
|
Binary file
|
File without changes
|
File without changes
|