@openmrs/esm-form-engine-lib 3.0.0 → 3.0.1-pre.1652

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.
Files changed (44) hide show
  1. package/00718f89a2ec9729/00718f89a2ec9729.gz +0 -0
  2. package/121aae94b7b7602c/121aae94b7b7602c.gz +0 -0
  3. package/1f1205a43e369f8a/1f1205a43e369f8a.gz +0 -0
  4. package/56d74d49545e0e3e/56d74d49545e0e3e.gz +0 -0
  5. package/README.md +65 -30
  6. package/__mocks__/forms/rfe-forms/obs-group-test_form.json +188 -124
  7. package/dist/openmrs-esm-form-engine-lib.js +1 -1
  8. package/package.json +5 -4
  9. package/src/components/group/obs-group.component.tsx +5 -5
  10. package/src/components/inputs/content-switcher/content-switcher.component.tsx +6 -1
  11. package/src/components/inputs/date/date.component.tsx +1 -1
  12. package/src/components/inputs/multi-select/multi-select.component.tsx +4 -4
  13. package/src/components/inputs/number/number.component.tsx +2 -2
  14. package/src/components/inputs/radio/radio.component.tsx +1 -1
  15. package/src/components/inputs/select/dropdown.component.tsx +2 -2
  16. package/src/components/inputs/ui-select-extended/ui-select-extended.component.tsx +39 -18
  17. package/src/components/inputs/ui-select-extended/ui-select-extended.test.tsx +0 -1
  18. package/src/components/inputs/workspace-launcher/workspace-launcher.component.tsx +3 -1
  19. package/src/components/sidebar/sidebar.component.tsx +5 -6
  20. package/src/components/sidebar/sidebar.scss +5 -0
  21. package/src/components/value/value.component.tsx +4 -1
  22. package/src/form-engine.component.tsx +17 -13
  23. package/src/form-engine.scss +8 -0
  24. package/src/form-engine.test.tsx +44 -1
  25. package/src/hooks/{useConcepts.tsx → useConcepts.ts} +1 -2
  26. package/src/hooks/useDataSourceDependentValue.ts +1 -1
  27. package/src/hooks/{useEncounter.tsx → useEncounter.ts} +9 -1
  28. package/src/hooks/{useFormJson.tsx → useFormJson.ts} +12 -2
  29. package/src/hooks/{usePatientData.tsx → usePatientData.ts} +1 -1
  30. package/src/hooks/usePatientPrograms.ts +29 -23
  31. package/src/hooks/useProcessorDependencies.ts +14 -4
  32. package/src/processors/encounter/encounter-form-processor.ts +19 -22
  33. package/src/types/domain.ts +4 -0
  34. package/src/types/schema.ts +5 -0
  35. package/src/utils/common-expression-helpers.test.ts +1 -1
  36. package/src/utils/common-expression-helpers.ts +10 -7
  37. package/translations/en.json +3 -3
  38. package/src/hooks/useClobData.tsx +0 -21
  39. package/src/hooks/useFieldValidationResults.ts +0 -18
  40. package/src/hooks/useFormsConfig.tsx +0 -27
  41. package/src/hooks/useRestMaxResultsCount.ts +0 -5
  42. package/src/hooks/useSystemSetting.ts +0 -36
  43. package/src/hooks/{useEncounterRole.tsx → useEncounterRole.ts} +1 -1
  44. package/src/hooks/{useFormCollapse.tsx → useFormCollapse.ts} +1 -1
@@ -1,17 +1,5 @@
1
- import {
2
- type FormField,
3
- type FormPage,
4
- type FormProcessorContextProps,
5
- type FormSchema,
6
- type FormSection,
7
- type ValueAndDisplay,
8
- } from '../../types';
9
- import { usePatientPrograms } from '../../hooks/usePatientPrograms';
10
1
  import { useEffect, useState } from 'react';
11
- import { useEncounter } from '../../hooks/useEncounter';
12
- import { isEmpty } from '../../validators/form-validator';
13
- import { type FormContextProps } from '../../provider/form-provider';
14
- import { FormProcessor } from '../form-processor';
2
+ import { type OpenmrsResource, showSnackbar, translateFrom } from '@openmrs/esm-framework';
15
3
  import {
16
4
  getMutableSessionProps,
17
5
  hydrateRepeatField,
@@ -23,23 +11,32 @@ import {
23
11
  savePatientIdentifiers,
24
12
  savePatientPrograms,
25
13
  } from './encounter-processor-helper';
26
- import { type OpenmrsResource, showSnackbar, translateFrom } from '@openmrs/esm-framework';
27
- import { moduleName } from '../../globals';
14
+ import {
15
+ type FormField,
16
+ type FormPage,
17
+ type FormProcessorContextProps,
18
+ type FormSchema,
19
+ type FormSection,
20
+ type ValueAndDisplay,
21
+ } from '../../types';
22
+ import { evaluateAsyncExpression, type FormNode } from '../../utils/expression-runner';
28
23
  import { extractErrorMessagesFromResponse } from '../../utils/error-utils';
24
+ import { extractObsValueAndDisplay } from '../../utils/form-helper';
25
+ import { FormProcessor } from '../form-processor';
29
26
  import { getPreviousEncounter, saveEncounter } from '../../api';
30
- import { useEncounterRole } from '../../hooks/useEncounterRole';
31
- import { evaluateAsyncExpression, type FormNode } from '../../utils/expression-runner';
32
27
  import { hasRendering } from '../../utils/common-utils';
33
- import { extractObsValueAndDisplay } from '../../utils/form-helper';
28
+ import { isEmpty } from '../../validators/form-validator';
29
+ import { moduleName } from '../../globals';
30
+ import { type FormContextProps } from '../../provider/form-provider';
31
+ import { useEncounter } from '../../hooks/useEncounter';
32
+ import { useEncounterRole } from '../../hooks/useEncounterRole';
33
+ import { usePatientPrograms } from '../../hooks/usePatientPrograms';
34
34
 
35
35
  function useCustomHooks(context: Partial<FormProcessorContextProps>) {
36
36
  const [isLoading, setIsLoading] = useState(true);
37
37
  const { encounter, isLoading: isLoadingEncounter } = useEncounter(context.formJson);
38
38
  const { encounterRole, isLoading: isLoadingEncounterRole } = useEncounterRole();
39
- const { isLoading: isLoadingPatientPrograms, patientPrograms } = usePatientPrograms(
40
- context.patient?.id,
41
- context.formJson,
42
- );
39
+ const { isLoadingPatientPrograms, patientPrograms } = usePatientPrograms(context.patient?.id, context.formJson);
43
40
 
44
41
  useEffect(() => {
45
42
  setIsLoading(isLoadingPatientPrograms || isLoadingEncounter || isLoadingEncounterRole);
@@ -165,6 +165,10 @@ export interface PatientProgram extends OpenmrsResource {
165
165
  states?: Array<ProgramWorkflowState>;
166
166
  }
167
167
 
168
+ export interface ProgramsFetchResponse {
169
+ results: Array<PatientProgram>;
170
+ }
171
+
168
172
  export interface PatientProgramPayload {
169
173
  program?: string;
170
174
  uuid?: string;
@@ -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
  */
@@ -171,7 +171,7 @@ describe('CommonExpressionHelpers', () => {
171
171
  it('should return the correct number of months on ART', () => {
172
172
  const artStartDate = new Date('2020-01-01');
173
173
  const today = new Date();
174
- const monthsOnART = Math.floor((today.getTime() - artStartDate.getTime()) / (1000 * 60 * 60 * 24 * 30));
174
+ const monthsOnART = dayjs(today).diff(dayjs(artStartDate), 'months');
175
175
  expect(helpers.calcMonthsOnART(artStartDate)).toBe(monthsOnART);
176
176
  });
177
177
 
@@ -118,19 +118,22 @@ export class CommonExpressionHelpers {
118
118
  };
119
119
 
120
120
  calcMonthsOnART = (artStartDate: Date) => {
121
- if (artStartDate == null) return null;
121
+ if (artStartDate == null) {
122
+ return null;
123
+ }
122
124
 
123
125
  if (!(artStartDate instanceof Date)) {
124
126
  throw new Error('DateFormatException: value passed is not a valid date');
125
127
  }
126
128
 
127
- let today = new Date();
128
- let resultMonthsOnART: number;
129
- let artInDays = Math.round((today.getTime() - artStartDate.getTime?.()) / 86400000);
130
- if (artStartDate && artInDays >= 30) {
131
- resultMonthsOnART = Math.floor(artInDays / 30);
129
+ const today = new Date();
130
+ const artInDays = Math.round((today.getTime() - artStartDate.getTime()) / 86400000);
131
+
132
+ if (artInDays < 30) {
133
+ return 0;
132
134
  }
133
- return artStartDate ? resultMonthsOnART : null;
135
+
136
+ return dayjs(today).diff(artStartDate, 'month');
134
137
  };
135
138
 
136
139
  calcViralLoadStatus = (viralLoadCount: number) => {
@@ -2,6 +2,7 @@
2
2
  "add": "Add",
3
3
  "addCameraImage": "Add camera image",
4
4
  "addFile": "Add files",
5
+ "blank": "Blank",
5
6
  "cameraCapture": "Camera capture",
6
7
  "cancel": "Cancel",
7
8
  "chooseAnOption": "Choose an option",
@@ -17,7 +18,7 @@
17
18
  "fileUploadDescriptionAny": "Upload any file type",
18
19
  "invalidWorkspaceName": "Invalid workspace name.",
19
20
  "invalidWorkspaceNameSubtitle": "Please provide a valid workspace name.",
20
- "launchWorkspace": "",
21
+ "launchWorkspace": "Launch Workspace",
21
22
  "loading": "Loading",
22
23
  "notification": "Notification",
23
24
  "nullMandatoryField": "Please fill the required fields",
@@ -26,13 +27,12 @@
26
27
  "remove": "Remove",
27
28
  "required": "Required",
28
29
  "reuseValue": "Reuse value",
29
- "revert": "Revert",
30
30
  "save": "Save",
31
31
  "search": "Search",
32
+ "searching": "Searching",
32
33
  "submitting": "Submitting",
33
34
  "time": "Time",
34
35
  "unspecified": "Unspecified",
35
- "unspecifyAll": "Unspecify All",
36
36
  "upload": "Upload",
37
37
  "uploadedPhoto": "Uploaded photo",
38
38
  "uploadImage": "Upload image",
@@ -1,21 +0,0 @@
1
- import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
- import { useMemo } from 'react';
3
- import { type FormSchema, type OpenmrsForm } from '../types';
4
- import useSWRImmutable from 'swr/immutable';
5
-
6
- export function useClobData(form: OpenmrsForm) {
7
- const valueReferenceUuid = useMemo(
8
- () => form?.resources?.find(({ name }) => name === 'JSON schema').valueReference,
9
- [form],
10
- );
11
- const { data, error } = useSWRImmutable<{ data: FormSchema }, Error>(
12
- valueReferenceUuid ? `${restBaseUrl}/clobdata/${valueReferenceUuid}` : null,
13
- openmrsFetch,
14
- );
15
-
16
- return {
17
- clobdata: data?.data,
18
- clobdataError: error || null,
19
- isLoadingClobData: (!data && !error) || false,
20
- };
21
- }
@@ -1,18 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import { type FormField } from '../types';
3
-
4
- export function useFieldValidationResults(field: FormField) {
5
- const [errors, setErrors] = useState([]);
6
- const [warnings, setWarnings] = useState([]);
7
-
8
- useEffect(() => {
9
- if (field.meta?.submission?.errors) {
10
- setErrors(field.meta.submission.errors);
11
- }
12
- if (field.meta?.submission?.warnings) {
13
- setWarnings(field.meta.submission.warnings);
14
- }
15
- }, [field.meta?.submission]);
16
-
17
- return { errors, warnings, setErrors, setWarnings };
18
- }
@@ -1,27 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import get from 'lodash-es/get';
3
- import { getConfig } from '@openmrs/esm-framework';
4
- import { ConceptTrue, ConceptFalse } from '../constants';
5
-
6
- export interface FormsConfig {
7
- conceptTrue: string;
8
- conceptFalse: string;
9
- }
10
- const defaultOptions: FormsConfig = {
11
- conceptTrue: ConceptTrue,
12
- conceptFalse: ConceptFalse,
13
- };
14
-
15
- export function useFormsConfig(moduleName: string, configPath: string) {
16
- const [config, setConfig] = useState<FormsConfig>(defaultOptions);
17
-
18
- useEffect(() => {
19
- if (moduleName && configPath) {
20
- getConfig(moduleName).then((c) => {
21
- setConfig({ config, ...get(c, configPath, config) });
22
- });
23
- }
24
- }, [moduleName, configPath, config]);
25
-
26
- return config;
27
- }
@@ -1,5 +0,0 @@
1
- import useSystemSetting from './useSystemSetting';
2
-
3
- export default function useRestMaxResultsCount() {
4
- return useSystemSetting('webservices.rest.maxResultsDefault');
5
- }
@@ -1,36 +0,0 @@
1
- import { openmrsFetch, restBaseUrl, showSnackbar } from '@openmrs/esm-framework';
2
- import { useEffect } from 'react';
3
- import { useTranslation } from 'react-i18next';
4
- import useSWRImmutable from 'swr/immutable';
5
-
6
- export interface SystemSetting {
7
- uuid: string;
8
- property: string;
9
- value: string;
10
- }
11
-
12
- export default function useSystemSetting(setting: string) {
13
- const { t } = useTranslation();
14
- const apiUrl = `${restBaseUrl}/systemsetting/${setting}?v=custom:(value)`;
15
- const { data, error, isLoading } = useSWRImmutable<{ data: SystemSetting }, Error>(apiUrl, openmrsFetch);
16
-
17
- useEffect(() => {
18
- if (error) {
19
- showSnackbar({
20
- title: t('error', 'Error'),
21
- subtitle: error?.message,
22
- kind: 'error',
23
- isLowContrast: false,
24
- });
25
- }
26
- }, [error]);
27
-
28
- return {
29
- systemSetting: data?.data,
30
- error: error,
31
- isLoading: isLoading,
32
- isValueUuid:
33
- /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(data?.data?.value) ||
34
- /^[0-9a-f]{36}$/i.test(data?.data?.value),
35
- };
36
- }
@@ -1,5 +1,5 @@
1
- import { type OpenmrsResource, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
1
  import useSWRImmutable from 'swr/immutable';
2
+ import { type OpenmrsResource, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
3
3
 
4
4
  export function useEncounterRole() {
5
5
  const { data, error, isLoading } = useSWRImmutable<{ data: { results: Array<OpenmrsResource> } }, Error>(
@@ -1,5 +1,5 @@
1
- import type { FormExpanded, SessionMode } from '../types';
2
1
  import { useCallback, useEffect, useState } from 'react';
2
+ import type { FormExpanded, SessionMode } from '../types';
3
3
 
4
4
  export function useFormCollapse(sessionMode: SessionMode) {
5
5
  const [isFormExpanded, setIsFormExpanded] = useState<FormExpanded>(undefined);