@bathiran212/esm-patient-chart-app 7.0.0
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/README.md +3 -0
- package/dist/108.js +1 -0
- package/dist/108.js.map +1 -0
- package/dist/1339.js +1 -0
- package/dist/1339.js.map +1 -0
- package/dist/1480.js +1 -0
- package/dist/1480.js.map +1 -0
- package/dist/1543.js +1 -0
- package/dist/1543.js.map +1 -0
- package/dist/1582.js +1 -0
- package/dist/1582.js.map +1 -0
- package/dist/1646.js +1 -0
- package/dist/1646.js.map +1 -0
- package/dist/1797.js +1 -0
- package/dist/1797.js.map +1 -0
- package/dist/1869.js +1 -0
- package/dist/1869.js.map +1 -0
- package/dist/1877.js +1 -0
- package/dist/1877.js.map +1 -0
- package/dist/2020.js +1 -0
- package/dist/2020.js.map +1 -0
- package/dist/2246.js +1 -0
- package/dist/2246.js.map +1 -0
- package/dist/2317.js +1 -0
- package/dist/2317.js.map +1 -0
- package/dist/2416.js +1 -0
- package/dist/2416.js.map +1 -0
- package/dist/2790.js +1 -0
- package/dist/2790.js.map +1 -0
- package/dist/282.js +1 -0
- package/dist/282.js.map +1 -0
- package/dist/2881.js +1 -0
- package/dist/2881.js.map +1 -0
- package/dist/3137.js +1 -0
- package/dist/3137.js.map +1 -0
- package/dist/3378.js +1 -0
- package/dist/3378.js.map +1 -0
- package/dist/3390.js +1 -0
- package/dist/3390.js.map +1 -0
- package/dist/3536.js +1 -0
- package/dist/3536.js.map +1 -0
- package/dist/3720.js +1 -0
- package/dist/3720.js.map +1 -0
- package/dist/3857.js +1 -0
- package/dist/3857.js.map +1 -0
- package/dist/3925.js +1 -0
- package/dist/3925.js.map +1 -0
- package/dist/3963.js +1 -0
- package/dist/3963.js.map +1 -0
- package/dist/3989.js +1 -0
- package/dist/3989.js.map +1 -0
- package/dist/4092.js +1 -0
- package/dist/4092.js.map +1 -0
- package/dist/4106.js +1 -0
- package/dist/4106.js.map +1 -0
- package/dist/4111.js +1 -0
- package/dist/4111.js.map +1 -0
- package/dist/4145.js +1 -0
- package/dist/4145.js.map +1 -0
- package/dist/434.js +1 -0
- package/dist/434.js.map +1 -0
- package/dist/4348.js +1 -0
- package/dist/4348.js.map +1 -0
- package/dist/4383.js +1 -0
- package/dist/4383.js.map +1 -0
- package/dist/4540.js +1 -0
- package/dist/4540.js.map +1 -0
- package/dist/4658.js +1 -0
- package/dist/4658.js.map +1 -0
- package/dist/466.js +1 -0
- package/dist/466.js.map +1 -0
- package/dist/4913.js +1 -0
- package/dist/4913.js.map +1 -0
- package/dist/4928.js +1 -0
- package/dist/4928.js.map +1 -0
- package/dist/5069.js +1 -0
- package/dist/5069.js.map +1 -0
- package/dist/5117.js +1 -0
- package/dist/5117.js.map +1 -0
- package/dist/5132.js +1 -0
- package/dist/5132.js.map +1 -0
- package/dist/5145.js +1 -0
- package/dist/5145.js.map +1 -0
- package/dist/52.js +1 -0
- package/dist/52.js.map +1 -0
- package/dist/5422.js +1 -0
- package/dist/5422.js.map +1 -0
- package/dist/5503.js +1 -0
- package/dist/5503.js.map +1 -0
- package/dist/5549.js +1 -0
- package/dist/5549.js.map +1 -0
- package/dist/556.js +1 -0
- package/dist/556.js.map +1 -0
- package/dist/5644.js +1 -0
- package/dist/5644.js.map +1 -0
- package/dist/5697.js +1 -0
- package/dist/5697.js.map +1 -0
- package/dist/5793.js +1 -0
- package/dist/5793.js.map +1 -0
- package/dist/5940.js +1 -0
- package/dist/5940.js.map +1 -0
- package/dist/5952.js +1 -0
- package/dist/5952.js.map +1 -0
- package/dist/6047.js +1 -0
- package/dist/6047.js.map +1 -0
- package/dist/6371.js +1 -0
- package/dist/6371.js.map +1 -0
- package/dist/6377.js +1 -0
- package/dist/6377.js.map +1 -0
- package/dist/6444.js +1 -0
- package/dist/6444.js.map +1 -0
- package/dist/6479.js +1 -0
- package/dist/6479.js.map +1 -0
- package/dist/6508.js +1 -0
- package/dist/6508.js.map +1 -0
- package/dist/6724.js +1 -0
- package/dist/6724.js.map +1 -0
- package/dist/6759.js +27 -0
- package/dist/6759.js.map +1 -0
- package/dist/689.js +1 -0
- package/dist/689.js.map +1 -0
- package/dist/6904.js +1 -0
- package/dist/6904.js.map +1 -0
- package/dist/7045.js +1 -0
- package/dist/7045.js.map +1 -0
- package/dist/7175.js +1 -0
- package/dist/7175.js.map +1 -0
- package/dist/7182.js +1 -0
- package/dist/7182.js.map +1 -0
- package/dist/7302.js +1 -0
- package/dist/7302.js.map +1 -0
- package/dist/7646.js +17 -0
- package/dist/7646.js.map +1 -0
- package/dist/7742.js +1 -0
- package/dist/7742.js.map +1 -0
- package/dist/7912.js +1 -0
- package/dist/7912.js.map +1 -0
- package/dist/8105.js +21 -0
- package/dist/8105.js.map +1 -0
- package/dist/8202.js +1 -0
- package/dist/8202.js.map +1 -0
- package/dist/8349.js +1 -0
- package/dist/8349.js.map +1 -0
- package/dist/8358.js +1 -0
- package/dist/8358.js.map +1 -0
- package/dist/8359.js +1 -0
- package/dist/8359.js.map +1 -0
- package/dist/8695.js +1 -0
- package/dist/8695.js.map +1 -0
- package/dist/8702.js +1 -0
- package/dist/8702.js.map +1 -0
- package/dist/8894.js +1 -0
- package/dist/8894.js.map +1 -0
- package/dist/8958.js +1 -0
- package/dist/8958.js.map +1 -0
- package/dist/903.js +1 -0
- package/dist/903.js.map +1 -0
- package/dist/9061.js +1 -0
- package/dist/9061.js.map +1 -0
- package/dist/9072.js +1 -0
- package/dist/9072.js.map +1 -0
- package/dist/9105.js +1 -0
- package/dist/9105.js.map +1 -0
- package/dist/9107.js +1 -0
- package/dist/9107.js.map +1 -0
- package/dist/9456.js +1 -0
- package/dist/9456.js.map +1 -0
- package/dist/9586.js +1 -0
- package/dist/9586.js.map +1 -0
- package/dist/9712.js +1 -0
- package/dist/9712.js.map +1 -0
- package/dist/9771.js +1 -0
- package/dist/9771.js.map +1 -0
- package/dist/9806.js +1 -0
- package/dist/9806.js.map +1 -0
- package/dist/9873.js +1 -0
- package/dist/9873.js.map +1 -0
- package/dist/9927.js +1 -0
- package/dist/9927.js.map +1 -0
- package/dist/main.js +6 -0
- package/dist/main.js.map +1 -0
- package/dist/openmrs-esm-patient-chart-app.js +6 -0
- package/dist/openmrs-esm-patient-chart-app.js.buildmanifest.json +2386 -0
- package/dist/openmrs-esm-patient-chart-app.js.map +1 -0
- package/dist/routes.json +1 -0
- package/package.json +63 -0
- package/rspack.config.js +1 -0
- package/src/actions-buttons/action-button.scss +3 -0
- package/src/actions-buttons/delete-visit.component.tsx +41 -0
- package/src/actions-buttons/delete-visit.test.tsx +26 -0
- package/src/actions-buttons/mark-patient-alive.component.tsx +42 -0
- package/src/actions-buttons/mark-patient-deceased.component.tsx +35 -0
- package/src/actions-buttons/start-visit.component.tsx +41 -0
- package/src/actions-buttons/start-visit.test.tsx +44 -0
- package/src/actions-buttons/stop-visit.component.tsx +39 -0
- package/src/actions-buttons/stop-visit.test.tsx +27 -0
- package/src/clinical-views/encounter-list/encounter-list-tabs.extension.tsx +78 -0
- package/src/clinical-views/encounter-list/encounter-list-tabs.scss +7 -0
- package/src/clinical-views/encounter-list/encounter-list.component.tsx +306 -0
- package/src/clinical-views/encounter-list/encounter-list.scss +36 -0
- package/src/clinical-views/encounter-list/table.component.tsx +63 -0
- package/src/clinical-views/encounter-list/table.scss +11 -0
- package/src/clinical-views/encounter-list/tag.component.test.tsx +307 -0
- package/src/clinical-views/encounter-list/tag.component.tsx +43 -0
- package/src/clinical-views/encounter-tile/clinical-views-summary.component.tsx +40 -0
- package/src/clinical-views/encounter-tile/encounter-tile.component.tsx +94 -0
- package/src/clinical-views/encounter-tile/tile.scss +82 -0
- package/src/clinical-views/hooks/index.ts +3 -0
- package/src/clinical-views/hooks/useEncounterRows.ts +60 -0
- package/src/clinical-views/hooks/useEncountersByVisit.ts +13 -0
- package/src/clinical-views/hooks/useFormsJson.ts +15 -0
- package/src/clinical-views/hooks/useLastEncounter.ts +29 -0
- package/src/clinical-views/types.ts +305 -0
- package/src/clinical-views/utils/concept-utils.ts +24 -0
- package/src/clinical-views/utils/encounter-list-config-builder.ts +160 -0
- package/src/clinical-views/utils/encounter-list.resource.ts +26 -0
- package/src/clinical-views/utils/helpers.ts +226 -0
- package/src/clinical-views/utils/index.ts +90 -0
- package/src/config-schema.ts +235 -0
- package/src/constants.ts +11 -0
- package/src/dashboard.meta.ts +15 -0
- package/src/data.resource.ts +117 -0
- package/src/declarations.d.ts +4 -0
- package/src/index.ts +204 -0
- package/src/loader/loader.component.tsx +11 -0
- package/src/loader/loader.scss +9 -0
- package/src/mark-patient-alive/mark-patient-alive.modal.tsx +54 -0
- package/src/mark-patient-deceased/mark-patient-deceased-form.scss +175 -0
- package/src/mark-patient-deceased/mark-patient-deceased-form.test.tsx +203 -0
- package/src/mark-patient-deceased/mark-patient-deceased-form.workspace.tsx +295 -0
- package/src/offline.ts +41 -0
- package/src/patient-banner-tags/visit-attribute-tags.extension.tsx +62 -0
- package/src/patient-banner-tags/visit-attribute-tags.scss +8 -0
- package/src/patient-chart/chart-review/chart-review.component.tsx +138 -0
- package/src/patient-chart/chart-review/chart-review.test.tsx +77 -0
- package/src/patient-chart/chart-review/dashboard-view.component.tsx +85 -0
- package/src/patient-chart/chart-review/dashboard-view.scss +84 -0
- package/src/patient-chart/patient-chart.component.tsx +71 -0
- package/src/patient-chart/patient-chart.resources.test.ts +238 -0
- package/src/patient-chart/patient-chart.resources.ts +231 -0
- package/src/patient-chart/patient-chart.scss +65 -0
- package/src/patient-details-tile/patient-details-tile.component.tsx +25 -0
- package/src/patient-details-tile/patient-details-tile.scss +24 -0
- package/src/root.component.tsx +35 -0
- package/src/root.scss +54 -0
- package/src/routes.json +267 -0
- package/src/side-nav/side-menu.component.tsx +10 -0
- package/src/side-nav/side-menu.scss +38 -0
- package/src/side-nav/side-menu.test.tsx +27 -0
- package/src/utils.test.ts +17 -0
- package/src/utils.ts +5 -0
- package/src/visit/hooks/useDefaultFacilityLocation.tsx +15 -0
- package/src/visit/hooks/useDefaultVisitLocation.tsx +24 -0
- package/src/visit/hooks/useDeleteVisit.test.tsx +267 -0
- package/src/visit/hooks/useDeleteVisit.tsx +103 -0
- package/src/visit/hooks/useOfflineVisitType.tsx +18 -0
- package/src/visit/hooks/useRecommendedVisitTypes.tsx +34 -0
- package/src/visit/hooks/useVisitAttributeType.tsx +102 -0
- package/src/visit/start-visit-button.component.tsx +47 -0
- package/src/visit/start-visit-button.test.tsx +32 -0
- package/src/visit/visit-action-items/delete-visit-action-item.component.tsx +60 -0
- package/src/visit/visit-action-items/delete-visit-action-item.test.tsx +48 -0
- package/src/visit/visit-action-items/edit-visit-details.component.tsx +79 -0
- package/src/visit/visit-form/base-visit-type.component.tsx +121 -0
- package/src/visit/visit-form/base-visit-type.scss +75 -0
- package/src/visit/visit-form/base-visit-type.test.tsx +153 -0
- package/src/visit/visit-form/exported-visit-form.workspace.tsx +755 -0
- package/src/visit/visit-form/location-selector.component.tsx +86 -0
- package/src/visit/visit-form/location-selector.test.tsx +146 -0
- package/src/visit/visit-form/recommended-visit-type.component.tsx +32 -0
- package/src/visit/visit-form/visit-attribute-type.component.tsx +258 -0
- package/src/visit/visit-form/visit-attribute-type.scss +5 -0
- package/src/visit/visit-form/visit-date-time.component.tsx +206 -0
- package/src/visit/visit-form/visit-form.resource.ts +401 -0
- package/src/visit/visit-form/visit-form.scss +167 -0
- package/src/visit/visit-form/visit-form.test.tsx +1233 -0
- package/src/visit/visit-form/visit-form.workspace.tsx +61 -0
- package/src/visit/visit-form/visit-type.test.tsx +88 -0
- package/src/visit/visit-history-table/visit-actions-cell.component.tsx +20 -0
- package/src/visit/visit-history-table/visit-actions-cell.scss +4 -0
- package/src/visit/visit-history-table/visit-date-cell.component.tsx +19 -0
- package/src/visit/visit-history-table/visit-diagnoses-cell.component.tsx +18 -0
- package/src/visit/visit-history-table/visit-history-table.component.tsx +145 -0
- package/src/visit/visit-history-table/visit-history-table.scss +25 -0
- package/src/visit/visit-history-table/visit-type-cell.component.tsx +15 -0
- package/src/visit/visit-prompt/delete-visit-dialog.modal.tsx +46 -0
- package/src/visit/visit-prompt/delete-visit-dialog.test.tsx +79 -0
- package/src/visit/visit-prompt/end-visit-dialog.modal.tsx +82 -0
- package/src/visit/visit-prompt/end-visit-dialog.scss +7 -0
- package/src/visit/visit-prompt/end-visit-dialog.test.tsx +131 -0
- package/src/visit/visit-prompt/modify-visit-date.modal.tsx +40 -0
- package/src/visit/visit-prompt/start-visit-dialog.modal.tsx +64 -0
- package/src/visit/visit-prompt/start-visit-dialog.scss +10 -0
- package/src/visit/visit-prompt/start-visit-dialog.test.tsx +40 -0
- package/src/visit/visits-widget/active-visit-buttons/active-visit-buttons.scss +7 -0
- package/src/visit/visits-widget/active-visit-buttons/active-visit-buttons.tsx +178 -0
- package/src/visit/visits-widget/current-visit-summary.extension.tsx +48 -0
- package/src/visit/visits-widget/current-visit-summary.scss +10 -0
- package/src/visit/visits-widget/current-visit-summary.test.tsx +85 -0
- package/src/visit/visits-widget/encounter-observations/encounter-observations.component.tsx +67 -0
- package/src/visit/visits-widget/encounter-observations/index.ts +3 -0
- package/src/visit/visits-widget/encounter-observations/styles.scss +22 -0
- package/src/visit/visits-widget/past-visits-components/delete-encounter.modal.tsx +47 -0
- package/src/visit/visits-widget/past-visits-components/delete-encounter.scss +9 -0
- package/src/visit/visits-widget/past-visits-components/encounters-table/all-encounters-table.component.tsx +49 -0
- package/src/visit/visits-widget/past-visits-components/encounters-table/completed-forms-table.component.tsx +67 -0
- package/src/visit/visits-widget/past-visits-components/encounters-table/completed-forms-table.test.tsx +146 -0
- package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.component.tsx +452 -0
- package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.resource.test.ts +156 -0
- package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.resource.ts +215 -0
- package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.scss +113 -0
- package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.test.tsx +432 -0
- package/src/visit/visits-widget/past-visits-components/encounters-table/visit-completed-forms-table.component.tsx +61 -0
- package/src/visit/visits-widget/past-visits-components/encounters-table/visit-completed-forms-table.test.tsx +125 -0
- package/src/visit/visits-widget/past-visits-components/encounters-table/visit-encounters-table.component.tsx +47 -0
- package/src/visit/visits-widget/past-visits-components/medications-summary.component.tsx +163 -0
- package/src/visit/visits-widget/past-visits-components/notes-summary.component.tsx +66 -0
- package/src/visit/visits-widget/past-visits-components/patient-notes-summary.component.tsx +318 -0
- package/src/visit/visits-widget/past-visits-components/tests-summary.component.tsx +16 -0
- package/src/visit/visits-widget/past-visits-components/visit-summary.component.tsx +192 -0
- package/src/visit/visits-widget/past-visits-components/visit-summary.scss +72 -0
- package/src/visit/visits-widget/past-visits-components/visit-summary.test.tsx +105 -0
- package/src/visit/visits-widget/single-visit-details/visit-timeline/visit-timeline.component.tsx +94 -0
- package/src/visit/visits-widget/single-visit-details/visit-timeline/visit-timeline.scss +60 -0
- package/src/visit/visits-widget/visit-context/retrospective-data-date-time-picker/restrospective-date-time-picker.scss +35 -0
- package/src/visit/visits-widget/visit-context/retrospective-data-date-time-picker/retrospective-date-time-picker.component.tsx +140 -0
- package/src/visit/visits-widget/visit-context/visit-context-header.extension.tsx +61 -0
- package/src/visit/visits-widget/visit-context/visit-context-header.scss +45 -0
- package/src/visit/visits-widget/visit-context/visit-context-header.test.tsx +59 -0
- package/src/visit/visits-widget/visit-context/visit-context-info.component.tsx +37 -0
- package/src/visit/visits-widget/visit-context/visit-context-info.scss +12 -0
- package/src/visit/visits-widget/visit-context/visit-context-switcher.modal.tsx +166 -0
- package/src/visit/visits-widget/visit-context/visit-context-switcher.scss +83 -0
- package/src/visit/visits-widget/visit-context/visit-context-switcher.test.tsx +79 -0
- package/src/visit/visits-widget/visit-detail-overview.component.tsx +67 -0
- package/src/visit/visits-widget/visit-detail-overview.scss +301 -0
- package/src/visit/visits-widget/visit-detail-overview.test.tsx +205 -0
- package/src/visit/visits-widget/visit.resource.tsx +146 -0
- package/translations/am.json +209 -0
- package/translations/ar.json +209 -0
- package/translations/ar_SY.json +209 -0
- package/translations/bn.json +209 -0
- package/translations/cs.json +209 -0
- package/translations/de.json +209 -0
- package/translations/en.json +209 -0
- package/translations/en_US.json +209 -0
- package/translations/es.json +209 -0
- package/translations/es_MX.json +209 -0
- package/translations/fr.json +209 -0
- package/translations/he.json +209 -0
- package/translations/hi.json +209 -0
- package/translations/hi_IN.json +209 -0
- package/translations/id.json +209 -0
- package/translations/it.json +209 -0
- package/translations/ka.json +209 -0
- package/translations/km.json +209 -0
- package/translations/ku.json +209 -0
- package/translations/ky.json +209 -0
- package/translations/lg.json +209 -0
- package/translations/ne.json +209 -0
- package/translations/pl.json +209 -0
- package/translations/pt.json +209 -0
- package/translations/pt_BR.json +209 -0
- package/translations/qu.json +209 -0
- package/translations/ro_RO.json +209 -0
- package/translations/ru_RU.json +209 -0
- package/translations/si.json +209 -0
- package/translations/sq.json +209 -0
- package/translations/sw.json +209 -0
- package/translations/sw_KE.json +209 -0
- package/translations/tr.json +209 -0
- package/translations/tr_TR.json +209 -0
- package/translations/uk.json +209 -0
- package/translations/uz.json +209 -0
- package/translations/uz@Latn.json +209 -0
- package/translations/uz_UZ.json +209 -0
- package/translations/vi.json +209 -0
- package/translations/zh.json +209 -0
- package/translations/zh_CN.json +209 -0
- package/translations/zh_TW.json +209 -0
- package/tsconfig.json +4 -0
- package/vitest.config.ts +4 -0
package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.test.tsx
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/* eslint-disable testing-library/no-node-access */
|
|
2
|
+
import { vi, describe, it, expect, test, beforeEach, afterEach } from 'vitest';
|
|
3
|
+
/* Please re-enable this ESLint rule if you are able to find a practical way to test the overflow menu buttons
|
|
4
|
+
without using parentElement and the expanded row functionality without using nextElementSibling. */
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import {
|
|
8
|
+
ExtensionSlot,
|
|
9
|
+
getDefaultsFromConfigSchema,
|
|
10
|
+
showModal,
|
|
11
|
+
useConfig,
|
|
12
|
+
useFeatureFlag,
|
|
13
|
+
userHasAccess,
|
|
14
|
+
} from '@openmrs/esm-framework';
|
|
15
|
+
import { usePatientChartStore } from '@openmrs/esm-patient-common-lib';
|
|
16
|
+
import { screen, waitFor, within } from '@testing-library/react';
|
|
17
|
+
import userEvent from '@testing-library/user-event';
|
|
18
|
+
import { mockEncountersAlice, mockEncounterTypes, mockFhirPatient, mockPatientAlice } from '__mocks__';
|
|
19
|
+
import { renderWithSwr } from 'tools';
|
|
20
|
+
import { type EncountersTableProps, useEncounterTypes } from './encounters-table.resource';
|
|
21
|
+
import { type ChartConfig, esmPatientChartSchema } from '../../../../config-schema';
|
|
22
|
+
import { jsonSchemaResourceName } from '../../../../constants';
|
|
23
|
+
import EncountersTable from './encounters-table.component';
|
|
24
|
+
|
|
25
|
+
const testProps: EncountersTableProps = {
|
|
26
|
+
patientUuid: mockPatientAlice.uuid,
|
|
27
|
+
paginatedEncounters: mockEncountersAlice,
|
|
28
|
+
totalCount: mockEncountersAlice.length,
|
|
29
|
+
currentPage: 1,
|
|
30
|
+
goTo: vi.fn(),
|
|
31
|
+
isLoading: false,
|
|
32
|
+
showVisitType: true,
|
|
33
|
+
showEncounterTypeFilter: false,
|
|
34
|
+
pageSize: 10,
|
|
35
|
+
setPageSize: vi.fn(),
|
|
36
|
+
isSelectable: true,
|
|
37
|
+
canPrintEncounters: true,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const mockShowModal = vi.mocked(showModal);
|
|
41
|
+
const mockUserHasAccess = vi.mocked(userHasAccess).mockReturnValue(true);
|
|
42
|
+
const mockUseFeatureFlag = vi.mocked(useFeatureFlag);
|
|
43
|
+
const mockExtensionSlot = vi.mocked(ExtensionSlot);
|
|
44
|
+
const mockUsePatientChartStore = vi.mocked(usePatientChartStore);
|
|
45
|
+
|
|
46
|
+
const mockUseEncounterTypes = vi.fn(useEncounterTypes).mockReturnValue({
|
|
47
|
+
data: mockEncounterTypes,
|
|
48
|
+
totalCount: mockEncounterTypes.length,
|
|
49
|
+
hasMore: false,
|
|
50
|
+
loadMore: vi.fn(),
|
|
51
|
+
error: undefined,
|
|
52
|
+
mutate: vi.fn(),
|
|
53
|
+
isValidating: false,
|
|
54
|
+
isLoading: false,
|
|
55
|
+
nextUri: '',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const mockUseConfig = vi.mocked(useConfig);
|
|
59
|
+
|
|
60
|
+
vi.mock('./encounters-table.resource', async () => ({
|
|
61
|
+
...((await vi.importActual('./encounters-table.resource')) as object),
|
|
62
|
+
useEncounterTypes: () => mockUseEncounterTypes(),
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
vi.mock('@openmrs/esm-patient-common-lib', async () => ({
|
|
66
|
+
...((await vi.importActual('@openmrs/esm-patient-common-lib')) as object),
|
|
67
|
+
usePatientChartStore: vi.fn(),
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
mockUseFeatureFlag.mockReturnValue(true);
|
|
72
|
+
mockUsePatientChartStore.mockReturnValue({
|
|
73
|
+
patientUuid: mockPatientAlice.uuid,
|
|
74
|
+
patient: mockFhirPatient,
|
|
75
|
+
visitContext: null,
|
|
76
|
+
mutateVisitContext: vi.fn(),
|
|
77
|
+
setPatient: vi.fn(),
|
|
78
|
+
setVisitContext: vi.fn(),
|
|
79
|
+
} as any);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('EncountersTable', () => {
|
|
83
|
+
it('renders an empty state when no encounters are available', async () => {
|
|
84
|
+
mockUseConfig.mockImplementation((options) => {
|
|
85
|
+
if (options?.externalModuleName === '@openmrs/esm-patient-forms-app') {
|
|
86
|
+
return { htmlFormEntryForms: [] };
|
|
87
|
+
}
|
|
88
|
+
return getDefaultsFromConfigSchema(esmPatientChartSchema);
|
|
89
|
+
});
|
|
90
|
+
renderEncountersTable({ totalCount: 0, paginatedEncounters: [] });
|
|
91
|
+
|
|
92
|
+
expect(screen.getByText(/No encounters to display/i)).toBeInTheDocument();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("renders a tabular overview of the patient's clinical encounters", async () => {
|
|
96
|
+
renderEncountersTable();
|
|
97
|
+
|
|
98
|
+
await screen.findByRole('table');
|
|
99
|
+
|
|
100
|
+
const expectedColumnHeaders = [/date & time/, /visit type/, /encounter type/, /form name/, /provider/];
|
|
101
|
+
const expectedTableRows = [
|
|
102
|
+
/select row 18\-jan\-2022, 04:25 pm facility visit admission poc consent form \-\- options/,
|
|
103
|
+
/select row 03\-aug\-2021, 12:47 am facility visit visit note \-\- user one options/,
|
|
104
|
+
/select row 05\-jul\-2021, 10:07 am facility visit consultation covid 19 dennis the doctor options/,
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
expectedColumnHeaders.forEach((header) => {
|
|
108
|
+
expect(screen.getByRole('columnheader', { name: new RegExp(header, 'i') })).toBeInTheDocument();
|
|
109
|
+
});
|
|
110
|
+
expectedTableRows.forEach((row) => {
|
|
111
|
+
expect(screen.getByRole('row', { name: new RegExp(row, 'i') })).toBeInTheDocument();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('passes visit and patient context to embedded form slot state', async () => {
|
|
116
|
+
const user = userEvent.setup();
|
|
117
|
+
mockUseConfig.mockImplementation((options) => {
|
|
118
|
+
if (options?.externalModuleName === '@openmrs/esm-patient-forms-app') {
|
|
119
|
+
return { htmlFormEntryForms: [] };
|
|
120
|
+
}
|
|
121
|
+
return getDefaultsFromConfigSchema(esmPatientChartSchema);
|
|
122
|
+
});
|
|
123
|
+
const encounterWithEmbeddedForm = {
|
|
124
|
+
...mockEncountersAlice[0],
|
|
125
|
+
form: {
|
|
126
|
+
...mockEncountersAlice[0].form,
|
|
127
|
+
resources: [
|
|
128
|
+
{
|
|
129
|
+
uuid: 'embedded-form-resource',
|
|
130
|
+
name: jsonSchemaResourceName,
|
|
131
|
+
dataType: 'AmpathJsonSchema',
|
|
132
|
+
valueReference: 'embedded-schema-reference',
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
renderEncountersTable({
|
|
139
|
+
paginatedEncounters: [encounterWithEmbeddedForm],
|
|
140
|
+
totalCount: 1,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const [expandButton] = screen.getAllByRole('button', { name: /expand current row/i });
|
|
144
|
+
await user.click(expandButton);
|
|
145
|
+
|
|
146
|
+
await waitFor(() => {
|
|
147
|
+
expect(mockExtensionSlot.mock.calls.find((call) => call[0].name === 'form-widget-slot')).toBeDefined();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const formWidgetCall = mockExtensionSlot.mock.calls.find((call) => call[0].name === 'form-widget-slot');
|
|
151
|
+
expect(formWidgetCall?.[0]?.state).toEqual(
|
|
152
|
+
expect.objectContaining({
|
|
153
|
+
visitUuid: encounterWithEmbeddedForm.visit.uuid,
|
|
154
|
+
visitTypeUuid: encounterWithEmbeddedForm.visit.visitType.uuid,
|
|
155
|
+
patientUuid: mockPatientAlice.uuid,
|
|
156
|
+
patient: mockFhirPatient,
|
|
157
|
+
}),
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('passes null visit context values to embedded form slot state for visitless encounters', async () => {
|
|
162
|
+
const user = userEvent.setup();
|
|
163
|
+
mockUseConfig.mockImplementation((options) => {
|
|
164
|
+
if (options?.externalModuleName === '@openmrs/esm-patient-forms-app') {
|
|
165
|
+
return { htmlFormEntryForms: [] };
|
|
166
|
+
}
|
|
167
|
+
return getDefaultsFromConfigSchema(esmPatientChartSchema);
|
|
168
|
+
});
|
|
169
|
+
const visitlessEncounterWithEmbeddedForm = {
|
|
170
|
+
...mockEncountersAlice[0],
|
|
171
|
+
visit: null,
|
|
172
|
+
form: {
|
|
173
|
+
...mockEncountersAlice[0].form,
|
|
174
|
+
resources: [
|
|
175
|
+
{
|
|
176
|
+
uuid: 'visitless-embedded-form-resource',
|
|
177
|
+
name: jsonSchemaResourceName,
|
|
178
|
+
dataType: 'AmpathJsonSchema',
|
|
179
|
+
valueReference: 'visitless-embedded-schema-reference',
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
renderEncountersTable({
|
|
186
|
+
paginatedEncounters: [visitlessEncounterWithEmbeddedForm],
|
|
187
|
+
totalCount: 1,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const [expandButton] = screen.getAllByRole('button', { name: /expand current row/i });
|
|
191
|
+
await user.click(expandButton);
|
|
192
|
+
|
|
193
|
+
await waitFor(() => {
|
|
194
|
+
expect(mockExtensionSlot.mock.calls.find((call) => call[0].name === 'form-widget-slot')).toBeDefined();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const formWidgetCall = mockExtensionSlot.mock.calls.find((call) => call[0].name === 'form-widget-slot');
|
|
198
|
+
expect(formWidgetCall?.[0]?.state).toEqual(
|
|
199
|
+
expect.objectContaining({
|
|
200
|
+
visitUuid: null,
|
|
201
|
+
visitTypeUuid: null,
|
|
202
|
+
visitStartDatetime: null,
|
|
203
|
+
visitStopDatetime: null,
|
|
204
|
+
patientUuid: mockPatientAlice.uuid,
|
|
205
|
+
patient: mockFhirPatient,
|
|
206
|
+
}),
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe('Encounter editability', () => {
|
|
212
|
+
let dateNowSpy: ReturnType<typeof vi.spyOn>;
|
|
213
|
+
|
|
214
|
+
beforeEach(() => {
|
|
215
|
+
dateNowSpy = vi.spyOn(Date, 'now').mockImplementation(() => new Date('2022-01-18T20:00:00.000Z').getTime());
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
afterEach(() => {
|
|
219
|
+
dateNowSpy.mockRestore();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('displays edit and delete encounter buttons by default', async () => {
|
|
223
|
+
mockUseConfig.mockImplementation((options) => {
|
|
224
|
+
if (options?.externalModuleName === '@openmrs/esm-patient-forms-app') {
|
|
225
|
+
return { htmlFormEntryForms: [] };
|
|
226
|
+
}
|
|
227
|
+
return getDefaultsFromConfigSchema(esmPatientChartSchema);
|
|
228
|
+
});
|
|
229
|
+
mockUserHasAccess.mockImplementation((privilege) => privilege == null);
|
|
230
|
+
const user = userEvent.setup();
|
|
231
|
+
|
|
232
|
+
renderEncountersTable();
|
|
233
|
+
|
|
234
|
+
const row = screen.getByRole('row', {
|
|
235
|
+
name: /Select row 18-Jan-2022, 04:25 PM Facility Visit Admission POC Consent Form -- Options/i,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Check overflow menu buttons
|
|
239
|
+
await user.click(within(row).getByRole('button', { name: /options/i }));
|
|
240
|
+
const overflowMenu = screen.getAllByText('Focus sentinel')[0].parentElement;
|
|
241
|
+
expect(within(overflowMenu).getByText(/edit this encounter/i)).toBeInTheDocument();
|
|
242
|
+
expect(within(overflowMenu).getByText(/Delete this encounter/i)).toBeInTheDocument();
|
|
243
|
+
await user.click(within(row).getByRole('button', { name: /options/i }));
|
|
244
|
+
expect(screen.queryByText('Focus sentinel')).not.toBeInTheDocument();
|
|
245
|
+
|
|
246
|
+
// Check big buttons in expanded row
|
|
247
|
+
await user.click(within(row).getByRole('button', { name: /expand current row/i }));
|
|
248
|
+
const expandedRow = row.nextElementSibling as HTMLElement;
|
|
249
|
+
expect(within(expandedRow).getByRole('button', { name: /edit this encounter/i })).toBeInTheDocument();
|
|
250
|
+
expect(within(expandedRow).getByRole('button', { name: /danger\s*Delete this encounter/i })).toBeInTheDocument();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('displays edit and delete encounter buttons only if the encounter is within the editable duration', async () => {
|
|
254
|
+
mockUseConfig.mockImplementation((options) => {
|
|
255
|
+
if (options?.externalModuleName === '@openmrs/esm-patient-forms-app') {
|
|
256
|
+
return { htmlFormEntryForms: [] };
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
...(getDefaultsFromConfigSchema(esmPatientChartSchema) as ChartConfig),
|
|
260
|
+
encounterEditableDuration: 1440,
|
|
261
|
+
encounterEditableDurationOverridePrivileges: ['Super Edit Encounter', 'Magic Superpowers'],
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
mockUserHasAccess.mockImplementation((privilege) => privilege == null);
|
|
265
|
+
|
|
266
|
+
const user = userEvent.setup();
|
|
267
|
+
|
|
268
|
+
renderEncountersTable();
|
|
269
|
+
|
|
270
|
+
// Check today's encounter -- should be editable
|
|
271
|
+
const todayRow = screen.getByRole('row', {
|
|
272
|
+
name: /Select row 18-Jan-2022, 04:25 PM Facility Visit Admission POC Consent Form -- Options/i,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Check overflow menu buttons
|
|
276
|
+
await user.click(within(todayRow).getByRole('button', { name: /options/i }));
|
|
277
|
+
const overflowMenu = screen.getAllByText('Focus sentinel')[0].parentElement;
|
|
278
|
+
expect(within(overflowMenu).getByText(/edit this encounter/i)).toBeInTheDocument();
|
|
279
|
+
expect(within(overflowMenu).getByText(/Delete this encounter/i)).toBeInTheDocument();
|
|
280
|
+
await user.click(within(todayRow).getByRole('button', { name: /options/i }));
|
|
281
|
+
expect(screen.queryByText('Focus sentinel')).not.toBeInTheDocument();
|
|
282
|
+
|
|
283
|
+
// Check big buttons in expanded row
|
|
284
|
+
await user.click(within(todayRow).getByRole('button', { name: /expand current row/i }));
|
|
285
|
+
const expandedTodayRow = todayRow.nextElementSibling as HTMLElement;
|
|
286
|
+
await user.click(within(expandedTodayRow).getByRole('button', { name: /edit this encounter/i }));
|
|
287
|
+
expect(within(expandedTodayRow).getByRole('button', { name: /edit this encounter/i })).toBeInTheDocument();
|
|
288
|
+
expect(
|
|
289
|
+
within(expandedTodayRow).getByRole('button', { name: /danger\s*Delete this encounter/i }),
|
|
290
|
+
).toBeInTheDocument();
|
|
291
|
+
|
|
292
|
+
// Check old encounter -- should not be editable
|
|
293
|
+
const oldRow = screen.getByRole('row', {
|
|
294
|
+
name: /Select row 03-Aug-2021, 12:47 AM Facility Visit Visit Note -- User One/i,
|
|
295
|
+
});
|
|
296
|
+
expect(within(oldRow).queryByRole('button', { name: /options/i })).not.toBeInTheDocument();
|
|
297
|
+
await user.click(within(oldRow).getByRole('button', { name: /expand current row/i }));
|
|
298
|
+
const expandedOldRow = oldRow.nextElementSibling as HTMLElement;
|
|
299
|
+
expect(within(expandedOldRow).queryByRole('button', { name: /edit this encounter/i })).not.toBeInTheDocument();
|
|
300
|
+
expect(
|
|
301
|
+
within(expandedOldRow).queryByRole('button', { name: /danger\s*Delete this encounter/i }),
|
|
302
|
+
).not.toBeInTheDocument();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('displays edit and delete buttons if the user has the override privilege, even if the encounter is outside the editable duration', async () => {
|
|
306
|
+
mockUseConfig.mockImplementation((options) => {
|
|
307
|
+
if (options?.externalModuleName === '@openmrs/esm-patient-forms-app') {
|
|
308
|
+
return { htmlFormEntryForms: [] };
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
...(getDefaultsFromConfigSchema(esmPatientChartSchema) as ChartConfig),
|
|
312
|
+
encounterEditableDuration: 1440,
|
|
313
|
+
encounterEditableDurationOverridePrivileges: ['Super Edit Encounter', 'Magic Superpowers'],
|
|
314
|
+
};
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
mockUserHasAccess.mockImplementation((privilege) => privilege == null || privilege === 'Magic Superpowers');
|
|
318
|
+
|
|
319
|
+
const user = userEvent.setup();
|
|
320
|
+
|
|
321
|
+
renderEncountersTable();
|
|
322
|
+
|
|
323
|
+
const oldRow = screen.getByRole('row', {
|
|
324
|
+
name: /Select row 03-Aug-2021, 12:47 AM Facility Visit Visit Note -- User One Options/i,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// Check overflow menu buttons
|
|
328
|
+
await user.click(within(oldRow).getByRole('button', { name: /options/i }));
|
|
329
|
+
const overflowMenu = screen.getAllByText('Focus sentinel')[0].parentElement;
|
|
330
|
+
expect(within(overflowMenu).getByText(/edit this encounter/i)).toBeInTheDocument();
|
|
331
|
+
expect(within(overflowMenu).getByText(/Delete this encounter/i)).toBeInTheDocument();
|
|
332
|
+
await user.click(within(oldRow).getByRole('button', { name: /options/i }));
|
|
333
|
+
expect(screen.queryByText('Focus sentinel')).not.toBeInTheDocument();
|
|
334
|
+
|
|
335
|
+
// Check big buttons in expanded row
|
|
336
|
+
await user.click(within(oldRow).getByRole('button', { name: /expand current row/i }));
|
|
337
|
+
const expandedOldRow = oldRow.nextElementSibling as HTMLElement;
|
|
338
|
+
expect(within(expandedOldRow).getByRole('button', { name: /edit this encounter/i })).toBeInTheDocument();
|
|
339
|
+
expect(within(expandedOldRow).getByRole('button', { name: /danger\s*Delete this encounter/i })).toBeInTheDocument();
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
describe('Delete Encounter', () => {
|
|
344
|
+
beforeEach(() => {
|
|
345
|
+
mockUseConfig.mockImplementation((options) => {
|
|
346
|
+
if (options?.externalModuleName === '@openmrs/esm-patient-forms-app') {
|
|
347
|
+
return { htmlFormEntryForms: [] };
|
|
348
|
+
}
|
|
349
|
+
return getDefaultsFromConfigSchema(esmPatientChartSchema);
|
|
350
|
+
});
|
|
351
|
+
mockUserHasAccess.mockReturnValue(true);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('Clicking the `Delete` button deletes an encounter', async () => {
|
|
355
|
+
const user = userEvent.setup();
|
|
356
|
+
|
|
357
|
+
renderEncountersTable();
|
|
358
|
+
|
|
359
|
+
await screen.findByRole('table');
|
|
360
|
+
expect(screen.getByRole('table')).toBeInTheDocument();
|
|
361
|
+
|
|
362
|
+
const row = screen.getByRole('row', {
|
|
363
|
+
name: /Select row 18-Jan-2022, 04:25 PM Facility Visit Admission POC Consent Form -- Options/i,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
await user.click(within(row).getByRole('button', { name: /expand current row/i }));
|
|
367
|
+
const expandedRow = row.nextElementSibling as HTMLElement;
|
|
368
|
+
await user.click(within(expandedRow).getByRole('button', { name: /danger\s*Delete this encounter/i }));
|
|
369
|
+
|
|
370
|
+
expect(mockShowModal).toHaveBeenCalledTimes(1);
|
|
371
|
+
expect(mockShowModal).toHaveBeenCalledWith(
|
|
372
|
+
'delete-encounter-modal',
|
|
373
|
+
expect.objectContaining({
|
|
374
|
+
encounterTypeName: 'POC Consent Form',
|
|
375
|
+
}),
|
|
376
|
+
);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
function renderEncountersTable(props: Partial<EncountersTableProps> = {}) {
|
|
381
|
+
renderWithSwr(<EncountersTable {...testProps} {...props} />);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
describe('EncountersTable print functionality', () => {
|
|
385
|
+
beforeEach(() => {
|
|
386
|
+
mockUseConfig.mockImplementation((options) => {
|
|
387
|
+
if (options?.externalModuleName === '@openmrs/esm-patient-forms-app') {
|
|
388
|
+
return { htmlFormEntryForms: [] };
|
|
389
|
+
}
|
|
390
|
+
return getDefaultsFromConfigSchema(esmPatientChartSchema);
|
|
391
|
+
});
|
|
392
|
+
mockUserHasAccess.mockReturnValue(true);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('hides print button and selection checkboxes when canPrintEncounters is false', async () => {
|
|
396
|
+
renderEncountersTable({ isSelectable: true, canPrintEncounters: false, showEncounterTypeFilter: true });
|
|
397
|
+
|
|
398
|
+
await screen.findByRole('table');
|
|
399
|
+
|
|
400
|
+
expect(screen.queryByRole('button', { name: /print selected/i })).not.toBeInTheDocument();
|
|
401
|
+
expect(screen.queryByRole('checkbox', { name: /select all rows/i })).not.toBeInTheDocument();
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('shows print button and selection checkboxes when isSelectable and canPrintEncounters are true', async () => {
|
|
405
|
+
renderEncountersTable({ isSelectable: true, canPrintEncounters: true, showEncounterTypeFilter: true });
|
|
406
|
+
|
|
407
|
+
await screen.findByRole('table');
|
|
408
|
+
|
|
409
|
+
expect(screen.getByRole('button', { name: /print selected/i })).toBeInTheDocument();
|
|
410
|
+
expect(screen.getByRole('checkbox', { name: /select all rows/i })).toBeInTheDocument();
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('disables print button when no rows are selected', async () => {
|
|
414
|
+
renderEncountersTable({ isSelectable: true, canPrintEncounters: true, showEncounterTypeFilter: true });
|
|
415
|
+
|
|
416
|
+
await screen.findByRole('table');
|
|
417
|
+
|
|
418
|
+
expect(screen.getByRole('button', { name: /print selected/i })).toBeDisabled();
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('enables print button after selecting a row', async () => {
|
|
422
|
+
const user = userEvent.setup();
|
|
423
|
+
renderEncountersTable({ isSelectable: true, canPrintEncounters: true, showEncounterTypeFilter: true });
|
|
424
|
+
|
|
425
|
+
await screen.findByRole('table');
|
|
426
|
+
|
|
427
|
+
const firstRowCheckbox = screen.getAllByRole('checkbox', { name: /select row/i })[0];
|
|
428
|
+
await user.click(firstRowCheckbox);
|
|
429
|
+
|
|
430
|
+
expect(screen.getByRole('button', { name: /print selected/i })).toBeEnabled();
|
|
431
|
+
});
|
|
432
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
|
+
import { userHasAccess, useSession, type Visit } from '@openmrs/esm-framework';
|
|
3
|
+
import { type EncountersTableProps, encounterHasJsonSchemaForm } from './encounters-table.resource';
|
|
4
|
+
import EncountersTable from './encounters-table.component';
|
|
5
|
+
|
|
6
|
+
interface VisitCompletedFormsTableProps {
|
|
7
|
+
patientUuid: string;
|
|
8
|
+
visit: Visit;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This component shows a table of only completed forms from a single visit of a patient
|
|
13
|
+
*/
|
|
14
|
+
const VisitCompletedFormsTable: React.FC<VisitCompletedFormsTableProps> = ({ patientUuid, visit }) => {
|
|
15
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
16
|
+
const [pageSize, setPageSize] = useState(10);
|
|
17
|
+
|
|
18
|
+
const mappedFilteredEncounters = useMemo(() => {
|
|
19
|
+
if (!visit || !visit.encounters) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const completedForms = visit.encounters.filter(encounterHasJsonSchemaForm);
|
|
24
|
+
|
|
25
|
+
return completedForms.map((encounter) => {
|
|
26
|
+
return { ...encounter, visit };
|
|
27
|
+
});
|
|
28
|
+
}, [visit]);
|
|
29
|
+
|
|
30
|
+
const paginatedEncounters = useMemo(() => {
|
|
31
|
+
const startIndex = (currentPage - 1) * pageSize;
|
|
32
|
+
const endIndex = startIndex + pageSize;
|
|
33
|
+
return mappedFilteredEncounters.slice(startIndex, endIndex);
|
|
34
|
+
}, [mappedFilteredEncounters, currentPage, pageSize]);
|
|
35
|
+
|
|
36
|
+
const goTo = (pageNumber: number) => {
|
|
37
|
+
setCurrentPage(pageNumber);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const session = useSession();
|
|
41
|
+
const canPrintEncounters = userHasAccess('App: Print encounter forms', session?.user);
|
|
42
|
+
|
|
43
|
+
const encountersTableProps: EncountersTableProps = {
|
|
44
|
+
patientUuid,
|
|
45
|
+
totalCount: mappedFilteredEncounters.length,
|
|
46
|
+
currentPage,
|
|
47
|
+
goTo,
|
|
48
|
+
isLoading: false,
|
|
49
|
+
showVisitType: false,
|
|
50
|
+
paginatedEncounters: paginatedEncounters,
|
|
51
|
+
showEncounterTypeFilter: false,
|
|
52
|
+
pageSize,
|
|
53
|
+
setPageSize,
|
|
54
|
+
isSelectable: false,
|
|
55
|
+
canPrintEncounters,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return <EncountersTable {...encountersTableProps} />;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default VisitCompletedFormsTable;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { vi, describe, it, expect } from 'vitest';
|
|
3
|
+
import { screen } from '@testing-library/react';
|
|
4
|
+
import { type Visit } from '@openmrs/esm-framework';
|
|
5
|
+
import { mockPatientAlice } from '__mocks__';
|
|
6
|
+
import { renderWithSwr } from 'tools';
|
|
7
|
+
import VisitCompletedFormsTable from './visit-completed-forms-table.component';
|
|
8
|
+
|
|
9
|
+
vi.mock('./encounters-table.component', () => ({
|
|
10
|
+
default: function MockedEncountersTable(props: any) {
|
|
11
|
+
return (
|
|
12
|
+
<div
|
|
13
|
+
data-testid="encounters-table"
|
|
14
|
+
data-is-selectable={props.isSelectable}
|
|
15
|
+
data-show-encounter-type-filter={props.showEncounterTypeFilter}
|
|
16
|
+
data-show-visit-type={props.showVisitType}
|
|
17
|
+
data-is-loading={props.isLoading}
|
|
18
|
+
data-total-count={props.totalCount}
|
|
19
|
+
>
|
|
20
|
+
EncountersTable
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
},
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
describe('VisitCompletedFormsTable', () => {
|
|
27
|
+
const mockVisit: Visit = {
|
|
28
|
+
uuid: 'visit-123',
|
|
29
|
+
display: 'Facility Visit',
|
|
30
|
+
patient: mockPatientAlice,
|
|
31
|
+
visitType: {
|
|
32
|
+
uuid: 'visit-type-1',
|
|
33
|
+
display: 'Facility Visit',
|
|
34
|
+
},
|
|
35
|
+
encounters: [],
|
|
36
|
+
startDatetime: '2022-01-18T12:25:27.000+0000',
|
|
37
|
+
stopDatetime: '2022-01-18T17:25:27.000+0000',
|
|
38
|
+
} as Visit;
|
|
39
|
+
|
|
40
|
+
it('renders the encounters table with empty array when visit has no encounters', () => {
|
|
41
|
+
renderWithSwr(<VisitCompletedFormsTable patientUuid={mockPatientAlice.uuid} visit={mockVisit} />);
|
|
42
|
+
|
|
43
|
+
expect(screen.getByTestId('encounters-table')).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('renders the encounters table with row selection disabled', () => {
|
|
47
|
+
renderWithSwr(<VisitCompletedFormsTable patientUuid={mockPatientAlice.uuid} visit={mockVisit} />);
|
|
48
|
+
|
|
49
|
+
const table = screen.getByTestId('encounters-table');
|
|
50
|
+
expect(table).toHaveAttribute('data-is-selectable', 'false');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('renders the encounters table without the encounter type filter', () => {
|
|
54
|
+
renderWithSwr(<VisitCompletedFormsTable patientUuid={mockPatientAlice.uuid} visit={mockVisit} />);
|
|
55
|
+
|
|
56
|
+
const table = screen.getByTestId('encounters-table');
|
|
57
|
+
expect(table).toHaveAttribute('data-show-encounter-type-filter', 'false');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('renders the encounters table without the visit type column', () => {
|
|
61
|
+
renderWithSwr(<VisitCompletedFormsTable patientUuid={mockPatientAlice.uuid} visit={mockVisit} />);
|
|
62
|
+
|
|
63
|
+
const table = screen.getByTestId('encounters-table');
|
|
64
|
+
expect(table).toHaveAttribute('data-show-visit-type', 'false');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('renders the encounters table without a loading state', () => {
|
|
68
|
+
renderWithSwr(<VisitCompletedFormsTable patientUuid={mockPatientAlice.uuid} visit={mockVisit} />);
|
|
69
|
+
|
|
70
|
+
const table = screen.getByTestId('encounters-table');
|
|
71
|
+
expect(table).toHaveAttribute('data-is-loading', 'false');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('handles null visit gracefully', () => {
|
|
75
|
+
renderWithSwr(<VisitCompletedFormsTable patientUuid={mockPatientAlice.uuid} visit={null as any} />);
|
|
76
|
+
|
|
77
|
+
const table = screen.getByTestId('encounters-table');
|
|
78
|
+
expect(table).toHaveAttribute('data-total-count', '0');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('only includes encounters that have a form with a JSON schema resource', () => {
|
|
82
|
+
const visitWithMixedEncounters = {
|
|
83
|
+
...mockVisit,
|
|
84
|
+
encounters: [
|
|
85
|
+
{
|
|
86
|
+
uuid: 'enc-with-schema',
|
|
87
|
+
form: {
|
|
88
|
+
uuid: 'form-1',
|
|
89
|
+
display: 'Form With Schema',
|
|
90
|
+
resources: [{ uuid: 'r1', name: 'JSON schema', valueReference: '{}' }],
|
|
91
|
+
},
|
|
92
|
+
encounterDatetime: '2022-01-18T16:25:27.000+0000',
|
|
93
|
+
encounterType: { uuid: 'type-1', display: 'Admission' },
|
|
94
|
+
obs: [],
|
|
95
|
+
encounterProviders: [],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
uuid: 'enc-without-schema',
|
|
99
|
+
form: {
|
|
100
|
+
uuid: 'form-2',
|
|
101
|
+
display: 'Form Without Schema',
|
|
102
|
+
resources: [],
|
|
103
|
+
},
|
|
104
|
+
encounterDatetime: '2022-01-18T14:00:00.000+0000',
|
|
105
|
+
encounterType: { uuid: 'type-1', display: 'Admission' },
|
|
106
|
+
obs: [],
|
|
107
|
+
encounterProviders: [],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
uuid: 'enc-no-form',
|
|
111
|
+
form: null,
|
|
112
|
+
encounterDatetime: '2022-01-18T12:00:00.000+0000',
|
|
113
|
+
encounterType: { uuid: 'type-2', display: 'Visit Note' },
|
|
114
|
+
obs: [],
|
|
115
|
+
encounterProviders: [],
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
} as Visit;
|
|
119
|
+
|
|
120
|
+
renderWithSwr(<VisitCompletedFormsTable patientUuid={mockPatientAlice.uuid} visit={visitWithMixedEncounters} />);
|
|
121
|
+
|
|
122
|
+
const table = screen.getByTestId('encounters-table');
|
|
123
|
+
expect(table).toHaveAttribute('data-total-count', '1');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
|
+
import { usePagination, userHasAccess, useSession, type Visit } from '@openmrs/esm-framework';
|
|
3
|
+
import EncountersTable from './encounters-table.component';
|
|
4
|
+
import { type EncountersTableProps } from './encounters-table.resource';
|
|
5
|
+
|
|
6
|
+
interface VisitEncountersTableProps {
|
|
7
|
+
patientUuid: string;
|
|
8
|
+
visit: Visit;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This component shows a table of encounters from a single visit of a patient
|
|
13
|
+
*/
|
|
14
|
+
const VisitEncountersTable: React.FC<VisitEncountersTableProps> = ({ patientUuid, visit }) => {
|
|
15
|
+
const [pageSize, setPageSize] = useState(10);
|
|
16
|
+
const mappedEncounters = useMemo(
|
|
17
|
+
() =>
|
|
18
|
+
(visit.encounters ?? []).map((encounter) => {
|
|
19
|
+
encounter.visit = visit;
|
|
20
|
+
return encounter;
|
|
21
|
+
}),
|
|
22
|
+
[visit],
|
|
23
|
+
);
|
|
24
|
+
const { results: paginatedEncounters, currentPage, goTo } = usePagination(mappedEncounters, pageSize);
|
|
25
|
+
|
|
26
|
+
const session = useSession();
|
|
27
|
+
const canPrintEncounters = userHasAccess('App: Print encounter forms', session?.user);
|
|
28
|
+
|
|
29
|
+
const encountersTableProps: EncountersTableProps = {
|
|
30
|
+
patientUuid,
|
|
31
|
+
totalCount: mappedEncounters.length,
|
|
32
|
+
currentPage,
|
|
33
|
+
goTo,
|
|
34
|
+
isLoading: false,
|
|
35
|
+
showVisitType: false,
|
|
36
|
+
paginatedEncounters: paginatedEncounters,
|
|
37
|
+
showEncounterTypeFilter: false,
|
|
38
|
+
pageSize,
|
|
39
|
+
setPageSize,
|
|
40
|
+
isSelectable: false,
|
|
41
|
+
canPrintEncounters,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return <EncountersTable {...encountersTableProps} />;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default VisitEncountersTable;
|