Skip to content

Commit

Permalink
[new-visits-widget] - updating branch to be in sync with [main] FormE…
Browse files Browse the repository at this point in the history
…ngine changes (#1821)

* (fix) O3-3046: Updated the interpretation keys for vital signs matching table headers (#1791)

* (fix) O3-2975: Encounter datetime should not be sent when saving vitals and biometrics form (#1790)

* (chore) Bump Angular form engine (#1799)

* (feat) O3-3084: Sort fetched tests alphabetically in the lab order search inside order basket (#1797)

* Sort fetched tests alphabetically in the lab order search

* Updated tests

* (feat) Update Angular form engine translations (#1801)

* (test) Refactor lab orders E2E test (#1803)

* (test) Split lab test step for saving the form (#1805)

* (fix) O3-3093: React form engine workspace should close after submitting a form (#1804)

* Patient forms not passing the closeWorkspaceWithSavedChanges function

* Passing closeWorkspaceWithSavedChanges to the forms

* Passing 'markFormAsDirty' in the form engine

* Bumped @openmrs/esm-form-engine-lib

* Fixed the import

* Reverted changes in form entry app

* (chore) Bump `@openmrs/esm-form-engine-lib` (#1807)

* Bumped @openmrs/esm-form-engine-lib

* Updated yarn.lock

* (chore) Bump @openmrs/openmrs-form-engine-lib (#1808)

* (chore) O3-3114: Bump react form engine (#1809)

(chore) Bump react form engine

* (chore) clean up unused webpack config and bumps engine (#1810)

* (chore) clean up unused webpack config and bumps engine

* Bumping form-engine again

---------

Co-authored-by: Vineet Sharma <sharmava05@gmail.com>

* (chore) Bump form-engine with added translations (#1811)

* Bump form-engine with added translations

* Bump form-engine to latest pre-release

* (fix) Use order reasons should correctly chunk concept references and  load all of them (#1813)

(fix) Use order reasons should correctly chunk concept references and load all of them

* (feat) O3-3077: Add validation to restrict users from initiating future visit (#1793)

* (fix) O3-3133: Clicking the cancel button should not close the workspace (#1815)

* (feat) Conditionally repress browser-specific date; also updates the form engine

* (feat) O3-3112: Add form collapse toggle to the `esm-form-engine-app` (#1814)

* feat: form collapse

* refactor: rename formCollapseToggle

* refactor: remove redundant add lister

* refactor: use usecallback for handleFormEmbedded

---------

Co-authored-by: Dennis Kigen <kigen.work@gmail.com>

* (feat) O3-3133: Adapt cancel logic in the react form engine workspace (#1816)

* (chore) Bump react form engine (#1817)

* (chore) Bump ejs from 3.1.9 to 3.1.10 (#1819)

Bumps [ejs](https://github.com/mde/ejs) from 3.1.9 to 3.1.10.
- [Release notes](https://github.com/mde/ejs/releases)
- [Commits](mde/ejs@v3.1.9...v3.1.10)

---
updated-dependencies:
- dependency-name: ejs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Vineet Sharma <sharmava05@gmail.com>
Co-authored-by: Dennis Kigen <kigen.work@gmail.com>
Co-authored-by: Daud Kakumirizi <dkakumirizii@gmail.com>
Co-authored-by: CynthiaKamau <cynthiakamau54@gmail.com>
Co-authored-by: Pius Rubangakene <piruville@gmail.com>
Co-authored-by: Ian <52504170+ibacher@users.noreply.github.com>
Co-authored-by: Donald Kibet <chelashawdonald@yahoo.com>
Co-authored-by: Ian <ian.c.bacher@gmail.com>
Co-authored-by: Usama Idriss Kakumba <53287480+usamaidrsk@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Loading branch information
11 people committed May 6, 2024
1 parent 17c7182 commit da12dea
Show file tree
Hide file tree
Showing 32 changed files with 252 additions and 244 deletions.
8 changes: 4 additions & 4 deletions __mocks__/vitals-and-biometrics.mock.ts
Expand Up @@ -6020,7 +6020,7 @@ export const formattedVitals = [
diastolic: 89,
systolic: 121,
bmi: null,
bloodPressureInterpretation: 'normal',
bloodPressureRenderInterpretation: 'normal',
},
{
id: '1',
Expand All @@ -6034,15 +6034,15 @@ export const formattedVitals = [
temperature: 37,
spo2: 90,
bmi: 23,
bloodPressureInterpretation: 'normal',
bloodPressureRenderInterpretation: 'normal',
},
{
id: '2',
date: '2021-05-07T09:04:51.000Z',
diastolic: 80,
systolic: 120,
bmi: null,
bloodPressureInterpretation: 'normal',
bloodPressureRenderInterpretation: 'normal',
},
{
id: '3',
Expand All @@ -6053,7 +6053,7 @@ export const formattedVitals = [
pulse: 78,
respiratoryRate: 65,
bmi: 22.6,
bloodPressureInterpretation: 'normal',
bloodPressureRenderInterpretation: 'normal',
},
];

Expand Down
1 change: 1 addition & 0 deletions e2e/pages/index.ts
Expand Up @@ -4,6 +4,7 @@ export * from './chart-page';
export * from './conditions-page';
export * from './immunizations-page';
export * from './medications-page';
export * from './orders-page';
export * from './program-page';
export * from './results-viewer-page';
export * from './visits-page';
Expand Down
11 changes: 11 additions & 0 deletions e2e/pages/orders-page.ts
@@ -0,0 +1,11 @@
import { type Page } from '@playwright/test';

export class OrdersPage {
constructor(readonly page: Page) {}

readonly ordersTable = () => this.page.getByRole('table', { name: /orders/i });

async goTo(patientUuid: string) {
await this.page.goto(`/openmrs/spa/patient/${patientUuid}/chart/Orders`);
}
}
1 change: 0 additions & 1 deletion e2e/specs/clinical-forms.spec.ts
Expand Up @@ -37,7 +37,6 @@ test('Fill a clinical form', async ({ page }) => {
await expect(headerRow).toContainText(/last completed/i);

await expect(chartPage.page.getByRole('cell', { name: /covid 19/i })).toBeVisible();
await expect(chartPage.page.getByRole('cell', { name: /laboratory test orders/i })).toBeVisible();
await expect(chartPage.page.getByRole('cell', { name: /laboratory test results/i })).toBeVisible();
await expect(chartPage.page.getByRole('cell', { name: /soap note template/i })).toBeVisible();
await expect(chartPage.page.getByRole('cell', { name: /surgical operation/i })).toBeVisible();
Expand Down
112 changes: 28 additions & 84 deletions e2e/specs/lab-orders.spec.ts
Expand Up @@ -2,7 +2,7 @@ import { expect } from '@playwright/test';
import { type Visit } from '@openmrs/esm-framework';
import { generateRandomPatient, type Patient, startVisit, endVisit, deletePatient } from '../commands';
import { test } from '../core';
import { ChartPage, VisitsPage } from '../pages';
import { OrdersPage } from '../pages';

let patient: Patient;
let visit: Visit;
Expand All @@ -12,113 +12,57 @@ test.beforeEach(async ({ api }) => {
visit = await startVisit(api, patient.uuid);
});

test('Record, edit and discontinue a lab order', async ({ page }) => {
const chartPage = new ChartPage(page);
const visitsPage = new VisitsPage(page);
test('Record a lab order', async ({ page }) => {
const ordersPage = new OrdersPage(page);

await test.step('When I visit the chart summary page', async () => {
await chartPage.goTo(patient.uuid);
await test.step('When I visit the orders page', async () => {
await ordersPage.goTo(patient.uuid);
});

await test.step('And I click on the `Clinical forms` button on the siderail', async () => {
await chartPage.page.getByLabel(/clinical forms/i).click();
await test.step('And I click on the `Record orders` link', async () => {
await page.getByText(/record orders/i).click();
});

await test.step('Then I should see the clinical forms workspace', async () => {
const headerRow = chartPage.formsTable().locator('thead > tr');

await expect(chartPage.page.getByPlaceholder(/search this list/i)).toBeVisible();
await expect(headerRow).toContainText(/form name \(a-z\)/i);
await expect(headerRow).toContainText(/last completed/i);
await expect(chartPage.page.getByRole('cell', { name: /laboratory test orders/i })).toBeVisible();
});

await test.step('When I launch the `Laboratory Test Orders` form', async () => {
await page.getByText(/laboratory test orders/i).click();
});

await test.step('And I click on the `Add` button', async () => {
await page.getByRole('button', { name: 'Add', exact: true }).click();
});

await test.step('And I set the lab test to `Blood urea nitrogen`', async () => {
await page.locator('#tab select').selectOption('857AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
});

await test.step('And I click on the `Save and close` button', async () => {
await page.getByRole('button', { name: /save and close/i }).click();
});

await test.step('Then I should see a success notification', async () => {
await expect(page.getByText('Lab order(s) generated')).toBeVisible();
await expect(page.getByText(/blood urea nitrogen/i)).toBeVisible();
});

await test.step('When I navigate to the visits dashboard', async () => {
await visitsPage.goTo(patient.uuid);
await test.step('And I click the `Add +` button on the Lab orders tile', async () => {
await page.getByRole('button', { name: /add/i }).nth(1).click();
});

await test.step('And I go to the `All encounters` tab', async () => {
await page.getByRole('tab', { name: /all encounters/i }).click();
await test.step('Then I type `Blood urea nitrogen` into the search bar', async () => {
await page.getByRole('searchbox', { name: /search for a test type/i }).fill('blood urea nitrogen');
});

await test.step('Then I should see the newly added lab order in the list', async () => {
await expect(
page.getByRole('cell', { name: /laboratory test orders/i }).getByText('Laboratory Test Orders'),
).toBeVisible();
});

await test.step('When I click the overflow menu in the table row with the newly created lab order', async () => {
await test.step('And I click the `Add to basket` button on the `Blood urea nitrogen` entry in the list', async () => {
await page
.getByRole('button', { name: /options/i })
.nth(0)
.getByRole('listitem')
.filter({ hasText: /blood urea nitrogen/i })
.getByRole('button', { name: /order form/i })
.click();
});

await test.step('And I click on the `Edit` button', async () => {
await page.getByRole('menuitem', { name: /edit this encounter/i }).click();
await test.step('Then I should see the lab order form launch in the workspace', async () => {
await expect(page.getByText(/add lab order/i)).toBeVisible();
});

await test.step('And I change the lab test to `Hepatitis c test - qualitative`', async () => {
await page.locator('#tab select').selectOption('1325AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
await test.step('When I fill in the fields in the form for the Blood urea nitrogen test and submit the form', async () => {
await page.getByLabel(/lab reference number/i).fill(' 20240419-1234');
await page.getByLabel(/additional instructions/i).fill(' N/A');
});

await test.step('And I save the form', async () => {
await page.getByRole('button', { name: /save and close/i }).click();
await test.step('Add I save the lab order form', async () => {
await page.getByRole('button', { name: /save order/i }).click();
await page.getByRole('button', { name: /sign and close/i }).click();
});

await test.step('Then I should see a success notification', async () => {
await expect(page.getByText('Lab order(s) generated')).toBeVisible();
await expect(page.getByText(/blood urea nitrogen/i)).not.toBeVisible();
await expect(page.getByText(/hepatitis c test - qualitative/i)).toBeVisible();
await expect(page.getByText(/placed order for blood urea nitrogen/i)).toBeVisible();
});

await test.step('And I should see the updated Laboratory Test Order in the list', async () => {
await expect(
page.getByRole('cell', { name: /laboratory test orders/i }).getByText('Laboratory Test Orders'),
).toBeVisible();
});

await test.step('When I click the overflow menu in the table row with the updated lab order', async () => {
await page
.getByRole('button', { name: /options/i })
.nth(0)
.click();
await test.step('When I navigate to the orders dashboard', async () => {
await ordersPage.goTo(patient.uuid);
});

await test.step('And I click on the `Delete` button', async () => {
await page.getByRole('menuitem', { name: /delete this encounter/i }).click();
await page.getByRole('button', { name: /delete/i }).click();
});

await test.step('Then I should see a success notification', async () => {
await expect(page.getByText(/encounter successfully deleted/i)).toBeVisible();
});

await test.step('And the encounters table should be empty', async () => {
await expect(
page.getByLabel(/all encounters/i).getByText(/there are no encounters to display for this patient/i),
).toBeVisible();
await test.step('Then I should see the newly added lab order in the list', async () => {
await expect(page.getByLabel('testorders').getByRole('cell', { name: /blood urea nitrogen/i })).toBeVisible();
});
});

Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions jest.config.js
Expand Up @@ -14,6 +14,7 @@ module.exports = {
'^@carbon/charts-react$': path.resolve(__dirname, '__mocks__', '@carbon__charts-react.ts'),
'^dexie$': require.resolve('dexie'),
'^lodash-es/(.*)$': 'lodash/$1',
'^lodash-es$': 'lodash',
'^react-i18next$': path.resolve(__dirname, '__mocks__', 'react-i18next.js'),
'^uuid$': path.resolve(__dirname, 'node_modules', 'uuid', 'dist', 'index.js'),
},
Expand Down
2 changes: 1 addition & 1 deletion packages/esm-form-engine-app/README.md
@@ -1,4 +1,4 @@
# esm-form-engine

This is a wrapper around ohri form engine
This is a wrapper around react form engine

@@ -0,0 +1,46 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Toggle } from '@carbon/react';
import styles from './styles.scss';

const FormCollapseToggle = () => {
const { t } = useTranslation();
const [isFormEmbedded, setIsFormEmbedded] = useState<boolean>(false);

const handleFormEmbedded = useCallback((event) => {
setIsFormEmbedded(event?.detail?.value || false);
}, []);

useEffect(() => {
window.addEventListener('openmrs:form-view-embedded', handleFormEmbedded);

return () => {
window.removeEventListener('openmrs:form-view-embedded', handleFormEmbedded);
};
}, [handleFormEmbedded]);

const handleOnToggle = (value: boolean) => {
const FormCollapseToggleEvent = new CustomEvent('openmrs:form-collapse-toggle', { detail: { value } });
window.dispatchEvent(FormCollapseToggleEvent);
};

if (!isFormEmbedded) {
return null;
}

return (
<div className={styles.toggleContainer}>
<Toggle
size="sm"
aria-label={t('toggleCollapseOrExpand', 'Toggle collapse or expand')}
defaultToggled
id="collapsable-toggle"
labelA={t('expandAll', 'Expand all')}
labelB={t('collapseAll', 'Collapse all')}
onToggle={handleOnToggle}
/>
</div>
);
};

export default FormCollapseToggle;
@@ -0,0 +1,6 @@
.toggleContainer {
display: flex;
align-items: center;
height: var(--workspace-header-height);
margin-right: 0.5rem;
}
@@ -1,33 +1,47 @@
import React from 'react';
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { InlineLoading } from '@carbon/react';
import { OHRIForm } from '@openmrs/openmrs-form-engine-lib';
import { FormEngine } from '@openmrs/openmrs-form-engine-lib';
import { type Visit } from '@openmrs/esm-framework';
import useFormSchema from '../hooks/useFormSchema';
import { launchPatientWorkspace, type DefaultWorkspaceProps } from '@openmrs/esm-patient-common-lib';
import FormError from './form-error.component';
import useFormSchema from '../hooks/useFormSchema';
import styles from './form-renderer.scss';
import { type DefaultWorkspaceProps } from '@openmrs/esm-patient-common-lib';

interface FormRendererProps extends DefaultWorkspaceProps {
interface FormRendererProps {
additionalProps?: Record<string, any>;
closeWorkspace: DefaultWorkspaceProps['closeWorkspace'];
closeWorkspaceWithSavedChanges: DefaultWorkspaceProps['closeWorkspaceWithSavedChanges'];
encounterUuid?: string;
formUuid: string;
patientUuid: string;
promptBeforeClosing: DefaultWorkspaceProps['promptBeforeClosing'];
visit?: Visit;
encounterUuid?: string;
additionalProps?: Record<string, any>;
}

const FormRenderer: React.FC<FormRendererProps> = ({
formUuid,
patientUuid,
visit,
additionalProps,
closeWorkspace,
closeWorkspaceWithSavedChanges,
encounterUuid,
additionalProps,
formUuid,
patientUuid,
promptBeforeClosing,
visit,
}) => {
const { t } = useTranslation();
const { schema, error, isLoading } = useFormSchema(formUuid);

const handleCloseForm = useCallback(() => {
closeWorkspace();
!encounterUuid && launchPatientWorkspace('clinical-forms-workspace');
}, [closeWorkspace, encounterUuid]);

const handleMarkFormAsDirty = useCallback(
(isDirty: boolean) => promptBeforeClosing(() => isDirty),
[promptBeforeClosing],
);

if (isLoading) {
return (
<div className={styles.loaderContainer}>
Expand All @@ -39,22 +53,23 @@ const FormRenderer: React.FC<FormRendererProps> = ({
if (error) {
return (
<div className={styles.errorContainer}>
<FormError closeWorkspace={closeWorkspace} />
<FormError closeWorkspace={handleCloseForm} />
</div>
);
}

return (
<>
{schema && (
<OHRIForm
<FormEngine
encounterUUID={encounterUuid}
patientUUID={patientUuid}
visit={visit}
formJson={schema}
handleClose={closeWorkspace}
onSubmit={closeWorkspaceWithSavedChanges}
handleClose={handleCloseForm}
markFormAsDirty={handleMarkFormAsDirty}
mode={additionalProps?.mode}
onSubmit={closeWorkspaceWithSavedChanges}
patientUUID={patientUuid}
visit={visit}
/>
)}
</>
Expand Down
Expand Up @@ -6,7 +6,7 @@ import useFormSchema from '../hooks/useFormSchema';
const mockUseFormSchema = useFormSchema as jest.Mock;

jest.mock('@openmrs/openmrs-form-engine-lib', () => ({
OHRIForm: jest
FormEngine: jest
.fn()
.mockImplementation(() => React.createElement('div', { 'data-testid': 'openmrs form' }, 'FORM ENGINE LIB')),
}));
Expand Down
4 changes: 2 additions & 2 deletions packages/esm-form-engine-app/src/hooks/useFormSchema.tsx
@@ -1,7 +1,7 @@
import useSWR from 'swr';

import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
import { type OHRIFormSchema } from '@openmrs/openmrs-form-engine-lib';
import { type FormSchema } from '@openmrs/openmrs-form-engine-lib';

/**
* Custom hook to fetch form schema based on its form UUID.
Expand All @@ -12,7 +12,7 @@ import { type OHRIFormSchema } from '@openmrs/openmrs-form-engine-lib';
const useFormSchema = (formUuid: string) => {
const url = formUuid ? `${restBaseUrl}/o3/forms/${formUuid}` : null;

const { data, error, isLoading } = useSWR<{ data: OHRIFormSchema }>(url, openmrsFetch);
const { data, error, isLoading } = useSWR<{ data: FormSchema }>(url, openmrsFetch);

return { schema: data?.data, error, isLoading };
};
Expand Down

0 comments on commit da12dea

Please sign in to comment.