diff --git a/src/components/renderer/field/form-field-renderer.component.tsx b/src/components/renderer/field/form-field-renderer.component.tsx index 43d7478bd..b9bec071c 100644 --- a/src/components/renderer/field/form-field-renderer.component.tsx +++ b/src/components/renderer/field/form-field-renderer.component.tsx @@ -1,17 +1,18 @@ import React, { useEffect, useMemo, useState } from 'react'; import { type FormField, - type RenderType, - type ValidationResult, + type FormFieldInputProps, type FormFieldValidator, + type FormFieldValueAdapter, + type RenderType, type SessionMode, + type ValidationResult, type ValueAndDisplay, } from '../../../types'; import { Controller, useWatch } from 'react-hook-form'; import { ToastNotification } from '@carbon/react'; import { useTranslation } from 'react-i18next'; import { ErrorBoundary } from 'react-error-boundary'; -import { type FormFieldValueAdapter, type FormFieldInputProps } from '../../../types'; import { hasRendering } from '../../../utils/common-utils'; import { useFormProviderContext } from '../../../provider/form-provider'; import { isEmpty } from '../../../validators/form-validator'; diff --git a/src/form-engine.test.tsx b/src/form-engine.test.tsx index b836a2bd1..47434337c 100644 --- a/src/form-engine.test.tsx +++ b/src/form-engine.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import dayjs from 'dayjs'; import userEvent from '@testing-library/user-event'; -import { act, cleanup, render, screen, within } from '@testing-library/react'; +import { act, cleanup, render, screen, waitFor, within } from '@testing-library/react'; import { createErrorHandler, createGlobalStore, @@ -26,20 +26,30 @@ import { evaluatePostSubmissionExpression } from './utils/post-submission-action import { mockPatient } from '__mocks__/patient.mock'; import { mockSessionDataResponse } from '__mocks__/session.mock'; import { mockVisit } from '__mocks__/visit.mock'; +// import ageValidationForm from '__mocks__/forms/rfe-forms/age-validation-form.json'; +// import bmiForm from '__mocks__/forms/rfe-forms/bmi-test-form.json'; +// import bsaForm from '__mocks__/forms/rfe-forms/bsa-test-form.json'; import demoHtsForm from '__mocks__/forms/rfe-forms/demo_hts-form.json'; import demoHtsOpenmrsForm from '__mocks__/forms/afe-forms/demo_hts-form.json'; +// import eddForm from '__mocks__/forms/rfe-forms/edd-test-form.json'; +// import externalDataSourceForm from '__mocks__/forms/rfe-forms/external_data_source_form.json'; import filterAnswerOptionsTestForm from '__mocks__/forms/rfe-forms/filter-answer-options-test-form.json'; import htsPocForm from '__mocks__/packages/hiv/forms/hts_poc/1.1.json'; import labourAndDeliveryTestForm from '__mocks__/forms/rfe-forms/labour_and_delivery_test_form.json'; import mockConceptsForm from '__mocks__/concepts.mock.json'; +// import monthsOnArtForm from '__mocks__/forms/rfe-forms/months-on-art-form.json'; +// import nextVisitForm from '__mocks__/forms/rfe-forms/next-visit-test-form.json'; import obsGroupTestForm from '__mocks__/forms/rfe-forms/obs-group-test_form.json'; import postSubmissionTestForm from '__mocks__/forms/rfe-forms/post-submission-test-form.json'; import referenceByMappingForm from '__mocks__/forms/rfe-forms/reference-by-mapping-form.json'; import sampleFieldsForm from '__mocks__/forms/rfe-forms/sample_fields.json'; import testEnrolmentForm from '__mocks__/forms/rfe-forms/test-enrolment-form.json'; +// import viralLoadStatusForm from '__mocks__/forms/rfe-forms/viral-load-status-form.json'; +import historicalExpressionsForm from '__mocks__/forms/rfe-forms/historical-expressions-form.json'; import mockHxpEncounter from '__mocks__/forms/rfe-forms/mockHistoricalvisitsEncounter.json'; import requiredTestForm from '__mocks__/forms/rfe-forms/required-form.json'; import conditionalRequiredTestForm from '__mocks__/forms/rfe-forms/conditional-required-form.json'; +// import conditionalAnsweredForm from '__mocks__/forms/rfe-forms/conditional-answered-form.json'; import FormEngine from './form-engine.component'; import { type FormsRegistryStoreState } from './registry/registry'; @@ -269,26 +279,23 @@ describe('Form engine component', () => { // }); // }); - // describe('historical expressions', () => { - // it('should ascertain getPreviousEncounter() returns an encounter and the historical expression displays on the UI', async () => { - // const user = userEvent.setup(); - // - // renderForm(null, historicalExpressionsForm, 'COVID Assessment'); - // - // //ascertain form has rendered - // await screen.findByRole('combobox', { name: /Reasons for assessment/i }); - // - // //ascertain function fetching the encounter has been called - // expect(api.getPreviousEncounter).toHaveBeenCalled(); - // expect(api.getPreviousEncounter).toHaveReturnedWith(Promise.resolve(mockHxpEncounter)); - // - // const reuseValueButton = screen.getByRole('button', { name: /reuse value/i }); - // const evaluatedHistoricalValue = screen.getByText(/Entry into a country/i); - // - // expect(reuseValueButton).toBeInTheDocument; - // expect(evaluatedHistoricalValue).toBeInTheDocument; - // }); - // }); + describe('historical expressions', () => { + it('should ascertain getPreviousEncounter() returns an encounter and the historical expression displays on the UI', async () => { + const user = userEvent.setup(); + + renderForm(null, historicalExpressionsForm, 'COVID Assessment'); + + //ascertain form has rendered + await screen.findByRole('combobox', { name: /Reasons for assessment/i }); + + //ascertain function fetching the encounter has been called + expect(api.getPreviousEncounter).toHaveBeenCalled(); + expect(api.getPreviousEncounter).toHaveReturnedWith(Promise.resolve(mockHxpEncounter)); + + expect(screen.getByRole('button', { name: /reuse value/i })).toBeInTheDocument; + await waitFor(() => expect(screen.getByText(/Entry into a country/i))); + }); + }); describe('Form submission', () => { it('should validate required field on form submission', async () => { @@ -354,8 +361,8 @@ describe('Form engine component', () => { // // TODO: Temporarily disabling this until the core date picker mock gets fixed // // Issue - https://openmrs.atlassian.net/browse/O3-3479 // // Validate date field - // // const dateInputField = await screen.getByLabelText(/If Unscheduled, actual scheduled date/i); - // // expect(dateInputField).toHaveClass('cds--date-picker__input--invalid'); + // const dateInputField = screen.getByLabelText(/If Unscheduled, actual scheduled date/i); + // expect(dateInputField).toHaveClass('cds--date-picker__input--invalid'); // const errorMessage = await screen.findByText( // 'Patient visit marked as unscheduled. Please provide the scheduled date.', // ); diff --git a/src/processors/encounter/encounter-form-processor.ts b/src/processors/encounter/encounter-form-processor.ts index 851ee59dc..1b8faf7be 100644 --- a/src/processors/encounter/encounter-form-processor.ts +++ b/src/processors/encounter/encounter-form-processor.ts @@ -30,6 +30,7 @@ import { getPreviousEncounter, saveEncounter } from '../../api'; import { useEncounterRole } from '../../hooks/useEncounterRole'; import { evaluateAsyncExpression, evaluateExpression, type FormNode } from '../../utils/expression-runner'; import { hasRendering } from '../../utils/common-utils'; +import { extractObsValueAndDisplay } from '../../utils/form-helper'; function useCustomHooks(context: Partial) { const [isLoading, setIsLoading] = useState(true); @@ -311,7 +312,7 @@ export class EncounterFormProcessor extends FormProcessor { patient: patient, previousEncounter: previousDomainObjectValue, }); - return value; + return extractObsValueAndDisplay(field, value); } if (previousDomainObjectValue && field.questionOptions.enablePreviousValue) { return await adapter.getPreviousValue(field, previousDomainObjectValue, context); diff --git a/src/utils/form-helper.ts b/src/utils/form-helper.ts index d2f778800..bc09fc76d 100644 --- a/src/utils/form-helper.ts +++ b/src/utils/form-helper.ts @@ -1,7 +1,8 @@ import { type LayoutType } from '@openmrs/esm-framework'; import { type FormField, type FormPage, type FormSection, type SessionMode } from '../types'; import { isEmpty } from '../validators/form-validator'; -import { getRegisteredControl } from '../registry/registry'; +import { parseToLocalDateTime } from './common-utils'; +import dayjs from 'dayjs'; export function shouldUseInlineLayout( renderingType: 'single-line' | 'multiline' | 'automatic', @@ -170,3 +171,25 @@ export function scrollIntoView(viewId: string, shouldFocus: boolean = false) { currentElement?.focus(); } } + +export const extractObsValueAndDisplay = (field: FormField, obs: OpenmrsObs) => { + const rendering = field.questionOptions.rendering; + + if (typeof obs.value === 'string' || typeof obs.value === 'number') { + if (rendering === 'date' || rendering === 'datetime') { + const dateObj = parseToLocalDateTime(`${obs.value}`); + return { value: dateObj, display: dayjs(dateObj).format('YYYY-MM-DD HH:mm') }; + } + return { value: obs.value, display: obs.value }; + } else if (['toggle', 'checkbox'].includes(rendering)) { + return { + value: obs.value?.uuid, + display: obs.value?.name?.name, + }; + } else { + return { + value: obs.value?.uuid, + display: field.questionOptions.answers?.find((option) => option.concept === obs.value?.uuid)?.label, + }; + } +};