diff --git a/packages/esm-patient-vitals-app/src/biometrics/biometrics-base.component.tsx b/packages/esm-patient-vitals-app/src/biometrics/biometrics-base.component.tsx index a84fa56a61..78324efb5b 100644 --- a/packages/esm-patient-vitals-app/src/biometrics/biometrics-base.component.tsx +++ b/packages/esm-patient-vitals-app/src/biometrics/biometrics-base.component.tsx @@ -37,7 +37,7 @@ const BiometricsBase: React.FC = ({ patientUuid, pageSize, ); const tableHeaders = [ - { key: 'date', header: t('dateAndTime', 'Date and time') }, + { key: 'date', header: t('dateAndTime', 'Date and time'), isSortable: true }, { key: 'weight', header: withUnit(t('weight', 'Weight'), conceptUnits.get(config.concepts.weightUuid) ?? '') }, { key: 'height', header: withUnit(t('height', 'Height'), conceptUnits.get(config.concepts.heightUuid) ?? '') }, { key: 'bmi', header: `${t('bmi', 'BMI')} (${bmiUnit})` }, diff --git a/packages/esm-patient-vitals-app/src/biometrics/biometrics-overview.test.tsx b/packages/esm-patient-vitals-app/src/biometrics/biometrics-overview.test.tsx index 2b0131d79f..8711adfde8 100644 --- a/packages/esm-patient-vitals-app/src/biometrics/biometrics-overview.test.tsx +++ b/packages/esm-patient-vitals-app/src/biometrics/biometrics-overview.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { defineConfigSchema, getDefaultsFromConfigSchema, useConfig, usePagination } from '@openmrs/esm-framework'; +import { defineConfigSchema, getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework'; import { formattedBiometrics, mockBiometricsConfig, mockConceptMetadata, mockConceptUnits } from '__mocks__'; import { configSchema } from '../config-schema'; import { mockPatient, patientChartBasePath, renderWithSwr, waitForLoadingToFinish } from 'tools'; @@ -11,7 +11,6 @@ import BiometricsOverview from './biometrics-overview.component'; defineConfigSchema('@openmrs/esm-patient-vitals-app', configSchema); const mockedUseConfig = jest.mocked(useConfig); const mockedUseVitalsAndBiometrics = jest.mocked(useVitalsAndBiometrics); -const mockUsePagination = jest.mocked(usePagination); jest.mock('../common', () => { const originalModule = jest.requireActual('../common'); @@ -76,22 +75,12 @@ describe('BiometricsOverview: ', () => { }); it("renders a tabular overview of the patient's biometrics data when available", async () => { + const user = userEvent.setup(); + mockedUseVitalsAndBiometrics.mockReturnValue({ data: formattedBiometrics, } as ReturnType); - mockUsePagination.mockReturnValue({ - currentPage: 1, - goTo: () => {}, - results: formattedBiometrics.slice(0, 5), - totalPages: formattedBiometrics.length / 5, - paginated: true, - showNextButton: true, - showPreviousButton: false, - goToNext: () => {}, - goToPrevious: () => {}, - }); - renderBiometricsOverview(); await waitForLoadingToFinish(); @@ -102,13 +91,31 @@ describe('BiometricsOverview: ', () => { expect(screen.getByRole('tab', { name: /chart view/i })).toBeInTheDocument(); expect(screen.getByRole('link', { name: /see all/i })).toBeInTheDocument(); + const initialRowElements = screen.getAllByRole('row'); + const expectedColumnHeaders = [/date/, /weight/, /height/, /bmi/, /muac/]; expectedColumnHeaders.map((header) => expect(screen.getByRole('columnheader', { name: new RegExp(header, 'i') })).toBeInTheDocument(), ); - const expectedTableRows = [/90 186 26.0 17/, /80 198 20.4 23/, /50/, /61 160 23.8/, /90 198 23.0 25/]; + const expectedTableRows = [ + /12 — Aug — 2021, 12:00 AM 90 186 26.0 17/, + /18 — Jun — 2021, 12:00 AM 80 198 20.4 23/, + /10 — Jun — 2021, 12:00 AM 50 -- -- --/, + /26 — May — 2021, 12:00 AM 61 160 23.8 --/, + /10 — May — 2021, 12:00 AM 90 198 23.0 25/, + ]; expectedTableRows.map((row) => expect(screen.getByRole('row', { name: new RegExp(row, 'i') })).toBeInTheDocument()); + + const sortRowsButton = screen.getByRole('button', { name: /date and time/i }); + + await user.click(sortRowsButton); + + expect(screen.getAllByRole('row')).not.toEqual(initialRowElements); + + await user.click(sortRowsButton); + + expect(screen.getAllByRole('row')).toEqual(initialRowElements); }); it('toggles between rendering either a tabular view or a chart view', async () => { @@ -118,18 +125,6 @@ describe('BiometricsOverview: ', () => { data: formattedBiometrics.slice(0, 2), } as ReturnType); - mockUsePagination.mockReturnValue({ - currentPage: 1, - goTo: () => {}, - results: formattedBiometrics.slice(0, 5), - totalPages: formattedBiometrics.length / 5, - paginated: true, - showNextButton: true, - showPreviousButton: false, - goToNext: undefined, - goToPrevious: undefined, - }); - renderBiometricsOverview(); await waitForLoadingToFinish(); diff --git a/packages/esm-patient-vitals-app/src/biometrics/paginated-biometrics.component.tsx b/packages/esm-patient-vitals-app/src/biometrics/paginated-biometrics.component.tsx index 751b6fc9e0..abdb8d6c04 100644 --- a/packages/esm-patient-vitals-app/src/biometrics/paginated-biometrics.component.tsx +++ b/packages/esm-patient-vitals-app/src/biometrics/paginated-biometrics.component.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { DataTable, type DataTableRow, @@ -12,6 +12,7 @@ import { } from '@carbon/react'; import { useLayoutType, usePagination } from '@openmrs/esm-framework'; import { PatientChartPagination } from '@openmrs/esm-patient-common-lib'; +import orderBy from 'lodash/orderBy'; interface PaginatedBiometricsProps { tableRows: Array; @@ -28,12 +29,39 @@ const PaginatedBiometrics: React.FC = ({ urlLabel, tableHeaders, }) => { - const { results: paginatedBiometrics, goTo, currentPage } = usePagination(tableRows, pageSize); const isTablet = useLayoutType() === 'tablet'; + const [sortParams, setSortParams] = useState({ key: '', order: 'NONE' }); + + const handleSort = (cellA, cellB, { sortDirection }) => { + setSortParams({ key: 'date', order: sortDirection }); + }; + + const sortDate = (myArray, order) => + order === 'ASC' + ? orderBy(myArray, [(obj) => new Date(obj.encounterDate).getTime()], ['desc']) + : orderBy(myArray, [(obj) => new Date(obj.encounterDate).getTime()], ['asc']); + + const { key, order } = sortParams; + + const sortedData = + key === 'date' + ? sortDate(tableRows, order) + : order === 'DESC' + ? orderBy(tableRows, [key], ['desc']) + : orderBy(tableRows, [key], ['asc']); + + const { results: paginatedBiometrics, goTo, currentPage } = usePagination(sortedData, pageSize); + return (
- + {({ rows, headers, getHeaderProps, getTableProps }) => ( @@ -43,6 +71,7 @@ const PaginatedBiometrics: React.FC = ({ {header.header?.content ?? header.header} diff --git a/packages/esm-patient-vitals-app/src/vitals/paginated-vitals.component.tsx b/packages/esm-patient-vitals-app/src/vitals/paginated-vitals.component.tsx index 9ca8517088..9ac5d90c84 100644 --- a/packages/esm-patient-vitals-app/src/vitals/paginated-vitals.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/paginated-vitals.component.tsx @@ -54,6 +54,10 @@ const PaginatedVitals: React.FC = ({ const [sortParams, setSortParams] = useState({ key: '', order: 'none' }); + const handleSort = (cellA, cellB, { sortDirection }) => { + setSortParams({ key: 'date', order: sortDirection }); + }; + const sortDate = (myArray, order) => order === 'ASC' ? orderBy(myArray, [(obj) => new Date(obj.encounterDate).getTime()], ['desc']) @@ -62,7 +66,7 @@ const PaginatedVitals: React.FC = ({ const { key, order } = sortParams; const sortedData = - key === 'encounterDate' + key === 'date' ? sortDate(tableRows, order) : order === 'DESC' ? orderBy(tableRows, [key], ['desc']) @@ -74,14 +78,16 @@ const PaginatedVitals: React.FC = ({ return (
- - {({ rows, headers, getTableProps }) => ( + + {({ rows, headers, getTableProps, getHeaderProps }) => (
{headers.map((header) => ( - {header.header?.content ?? header.header} + + {header.header?.content ?? header.header} + ))} diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-overview.test.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-overview.test.tsx index f0b59a9db3..721ca24be2 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-overview.test.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-overview.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import userEvent from '@testing-library/user-event'; import { screen } from '@testing-library/react'; -import { defineConfigSchema, getDefaultsFromConfigSchema, useConfig, usePagination } from '@openmrs/esm-framework'; +import { defineConfigSchema, getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework'; import { configSchema } from '../config-schema'; import { formattedVitals, mockConceptMetadata, mockConceptUnits, mockVitalsConfig } from '__mocks__'; import { mockPatient, renderWithSwr, waitForLoadingToFinish } from 'tools'; @@ -11,7 +11,6 @@ import VitalsOverview from './vitals-overview.component'; defineConfigSchema('@openmrs/esm-patient-vitals-app', configSchema); const mockedUseConfig = jest.mocked(useConfig); const mockedUseVitalsAndBiometrics = jest.mocked(useVitalsAndBiometrics); -const mockUsePagination = jest.mocked(usePagination); global.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), @@ -91,34 +90,43 @@ describe('VitalsOverview', () => { }); it("renders a tabular overview of the patient's vital signs", async () => { + const user = userEvent.setup(); + mockedUseVitalsAndBiometrics.mockReturnValue({ data: formattedVitals, } as ReturnType); - mockUsePagination.mockReturnValue({ - currentPage: 1, - goTo: () => {}, - results: formattedVitals.slice(0, 5), - totalPages: formattedVitals.length / 5, - paginated: true, - showNextButton: true, - showPreviousButton: false, - goToNext: undefined, - goToPrevious: undefined, - } as ReturnType); - renderVitalsOverview(); await waitForLoadingToFinish(); expect(screen.getByRole('table', { name: /vitals/i })).toBeInTheDocument(); + const initialRowElements = screen.getAllByRole('row'); + const expectedColumnHeaders = [/date and time/, /bp/, /r. rate/, /pulse/, /spO2/, /temp/]; + expectedColumnHeaders.map((header) => expect(screen.getByRole('columnheader', { name: new RegExp(header, 'i') })).toBeInTheDocument(), ); - const expectedTableRows = [/37 76 12/, /37 66 45 90/, /36.5 78 65/]; + const expectedTableRows = [ + /19 — May — 2021, 04:26 AM 37 121 \/ 89 76 12 --/, + /10 — May — 2021, 06:41 AM 37 120 \/ 90 66 45 90/, + /07 — May — 2021, 09:04 AM -- 120 \/ 80 -- -- --/, + /08 — Apr — 2021, 02:44 PM 36.5 -- \/ -- 78 65 --/, + ]; + expectedTableRows.map((row) => expect(screen.getByRole('row', { name: new RegExp(row, 'i') })).toBeInTheDocument()); + + const sortRowsButton = screen.getByRole('button', { name: /date and time/i }); + + await user.click(sortRowsButton); + + expect(screen.getAllByRole('row')).not.toEqual(initialRowElements); + + await user.click(sortRowsButton); + + expect(screen.getAllByRole('row')).toEqual(initialRowElements); }); it('toggles between rendering either a tabular view or a chart view', async () => { @@ -128,18 +136,6 @@ describe('VitalsOverview', () => { data: formattedVitals, } as ReturnType); - mockUsePagination.mockReturnValue({ - currentPage: 1, - goTo: () => {}, - results: formattedVitals.slice(0, 5), - totalPages: formattedVitals.length / 5, - paginated: true, - showNextButton: true, - showPreviousButton: false, - goToNext: undefined, - goToPrevious: undefined, - } as ReturnType); - renderVitalsOverview(); await waitForLoadingToFinish();