From 500508c8b4b013b7fdc2c39023d203d8a0353675 Mon Sep 17 00:00:00 2001 From: Vineet Sharma Date: Fri, 12 Apr 2024 13:32:34 +0530 Subject: [PATCH] (fix)O3-3046: Allow sorting vitals, biometrics and conditions rows based on different sort functions (#1779) * Allow sorting vitals row based on different sort functions * Allow sorting biometrics row based on different sort functions * Allow sorting conditions row based on different sort functions * Finalizing changes * Fixed the default sorting * Fixed failing tests * Updated tests for vitals and biometrics app --------- Co-authored-by: Dennis Kigen --- .../conditions-detailed-summary.component.tsx | 35 +++---- .../conditions-overview.component.tsx | 91 +++++++++++++------ .../src/conditions/conditions.resource.ts | 43 +++++++++ .../biometrics/biometrics-base.component.tsx | 47 +++++++--- .../biometrics/biometrics-overview.test.tsx | 7 ++ .../paginated-biometrics.component.tsx | 55 +++++++---- .../src/biometrics/types.ts | 17 ++++ .../src/vitals/paginated-vitals.component.tsx | 62 +++++++++---- .../src/vitals/types.ts | 24 +++++ .../src/vitals/vitals-overview.component.tsx | 59 +++++++++--- .../src/vitals/vitals-overview.test.tsx | 7 ++ 11 files changed, 335 insertions(+), 112 deletions(-) create mode 100644 packages/esm-patient-vitals-app/src/biometrics/types.ts create mode 100644 packages/esm-patient-vitals-app/src/vitals/types.ts diff --git a/packages/esm-patient-conditions-app/src/conditions/conditions-detailed-summary.component.tsx b/packages/esm-patient-conditions-app/src/conditions/conditions-detailed-summary.component.tsx index 80ff4e92a8..0821e491b9 100644 --- a/packages/esm-patient-conditions-app/src/conditions/conditions-detailed-summary.component.tsx +++ b/packages/esm-patient-conditions-app/src/conditions/conditions-detailed-summary.component.tsx @@ -20,8 +20,7 @@ import { Add } from '@carbon/react/icons'; import { formatDate, parseDate, useLayoutType } from '@openmrs/esm-framework'; import { CardHeader, EmptyState, ErrorState, launchPatientWorkspace } from '@openmrs/esm-patient-common-lib'; import { ConditionsActionMenu } from './conditions-action-menu.component'; -import { compare } from './utils'; -import { useConditions } from './conditions.resource'; +import { useConditions, type ConditionTableHeader, useConditionsSorting } from './conditions.resource'; import styles from './conditions-detailed-summary.scss'; function ConditionsDetailedSummary({ patient }) { @@ -47,19 +46,28 @@ function ConditionsDetailedSummary({ patient }) { return conditions; }, [filter, conditions]); - const headers = useMemo( + const headers: Array = useMemo( () => [ { key: 'display', header: t('condition', 'Condition'), + isSortable: true, + sortFunc: (valueA, valueB) => valueA.display?.localeCompare(valueB.display), }, { - key: 'onsetDateTime', + key: 'onsetDateTimeRender', header: t('dateOfOnset', 'Date of onset'), + isSortable: true, + sortFunc: (valueA, valueB) => + valueA.onsetDateTime && valueB.onsetDateTime + ? new Date(valueA.onsetDateTime).getTime() - new Date(valueB.onsetDateTime).getTime() + : 0, }, { - key: 'clinicalStatus', + key: 'status', header: t('status', 'Status'), + isSortable: true, + sortFunc: (valueA, valueB) => valueA.clinicalStatus?.localeCompare(valueB.clinicalStatus), }, ], [t], @@ -72,22 +80,15 @@ function ConditionsDetailedSummary({ patient }) { id: condition.id, condition: condition.display, abatementDateTime: condition.abatementDateTime, - onsetDateTime: { - sortKey: condition.onsetDateTime ?? '', - content: condition.onsetDateTime - ? formatDate(parseDate(condition.onsetDateTime), { time: false, day: false }) - : '--', - }, + onsetDateTimeRender: condition.onsetDateTime + ? formatDate(parseDate(condition.onsetDateTime), { time: false, day: false }) + : '--', status: condition.clinicalStatus, }; }); }, [filteredConditions]); - const sortRow = (cellA, cellB, { sortDirection, sortStates }) => { - return sortDirection === sortStates.DESC - ? compare(cellB.sortKey, cellA.sortKey) - : compare(cellA.sortKey, cellB.sortKey); - }; + const { sortedRows, sortRow } = useConditionsSorting(headers, tableRows); const launchConditionsForm = useCallback( () => @@ -131,7 +132,7 @@ function ConditionsDetailedSummary({ patient }) { number; +} + interface ConditionsOverviewProps { patientUuid: string; } @@ -74,35 +88,51 @@ const ConditionsOverview: React.FC = ({ patientUuid }) return conditions; }, [filter, conditions]); - const { - results: paginatedConditions, - goTo, - currentPage, - } = usePagination(filteredConditions ?? [], conditionPageSize); - - const tableHeaders = [ - { - key: 'display', - header: t('condition', 'Condition'), - }, - { - key: 'onsetDateTime', - header: t('dateOfOnset', 'Date of onset'), - }, - { - key: 'clinicalStatus', - header: t('status', 'Status'), - }, - ]; + const headers: Array = useMemo( + () => [ + { + key: 'display', + header: t('condition', 'Condition'), + isSortable: true, + sortFunc: (valueA, valueB) => valueA.display?.localeCompare(valueB.display), + }, + { + key: 'onsetDateTimeRender', + header: t('dateOfOnset', 'Date of onset'), + isSortable: true, + sortFunc: (valueA, valueB) => + valueA.onsetDateTime && valueB.onsetDateTime + ? new Date(valueA.onsetDateTime).getTime() - new Date(valueB.onsetDateTime).getTime() + : 0, + }, + { + key: 'status', + header: t('status', 'Status'), + isSortable: true, + sortFunc: (valueA, valueB) => valueA.clinicalStatus?.localeCompare(valueB.clinicalStatus), + }, + ], + [t], + ); const tableRows = useMemo(() => { - return paginatedConditions?.map((condition) => ({ - ...condition, - onsetDateTime: condition.onsetDateTime - ? formatDate(parseDate(condition.onsetDateTime), { time: false, day: false }) - : '--', - })); - }, [paginatedConditions]); + return filteredConditions?.map((condition) => { + return { + ...condition, + id: condition.id, + condition: condition.display, + abatementDateTime: condition.abatementDateTime, + onsetDateTimeRender: condition.onsetDateTime + ? formatDate(parseDate(condition.onsetDateTime), { time: false, day: false }) + : '--', + status: condition.clinicalStatus, + }; + }); + }, [filteredConditions]); + + const { sortedRows, sortRow } = useConditionsSorting(headers, tableRows); + + const { results: paginatedConditions, goTo, currentPage } = usePagination(sortedRows, conditionPageSize); const handleConditionStatusChange = ({ selectedItem }) => setFilter(selectedItem); @@ -139,12 +169,13 @@ const ConditionsOverview: React.FC = ({ patientUuid }) {({ rows, headers, getHeaderProps, getTableProps }) => ( <> diff --git a/packages/esm-patient-conditions-app/src/conditions/conditions.resource.ts b/packages/esm-patient-conditions-app/src/conditions/conditions.resource.ts index b04a2185e1..e3a5d91e6a 100644 --- a/packages/esm-patient-conditions-app/src/conditions/conditions.resource.ts +++ b/packages/esm-patient-conditions-app/src/conditions/conditions.resource.ts @@ -1,6 +1,7 @@ import useSWR from 'swr'; import { fhirBaseUrl, openmrsFetch, restBaseUrl, useConfig } from '@openmrs/esm-framework'; import { type FHIRCondition, type FHIRConditionResponse } from '../types'; +import { useMemo, useState } from 'react'; export type Condition = { clinicalStatus: string; @@ -233,3 +234,45 @@ export async function deleteCondition(conditionId: string) { return res; } + +export interface ConditionTableRow extends Condition { + id: string; + condition: string; + abatementDateTime: string; + onsetDateTimeRender: string; +} + +export interface ConditionTableHeader { + key: 'display' | 'onsetDateTimeRender' | 'status'; + header: string; + isSortable: true; + sortFunc: (valueA: ConditionTableRow, valueB: ConditionTableRow) => number; +} + +export function useConditionsSorting(tableHeaders: Array, tableRows: Array) { + const [sortParams, setSortParams] = useState<{ + key: ConditionTableHeader['key'] | ''; + sortDirection: 'ASC' | 'DESC' | 'NONE'; + }>({ key: '', sortDirection: 'NONE' }); + const sortRow = (cellA, cellB, { key, sortDirection }) => { + setSortParams({ key, sortDirection }); + }; + const sortedRows = useMemo(() => { + if (sortParams.sortDirection === 'NONE') { + return tableRows; + } + + const { key, sortDirection } = sortParams; + const tableHeader = tableHeaders.find((h) => h.key === key); + + return tableRows?.slice().sort((a, b) => { + const sortingNum = tableHeader.sortFunc(a, b); + return sortDirection === 'DESC' ? sortingNum : -sortingNum; + }); + }, [sortParams, tableRows, tableHeaders]); + + return { + sortedRows, + sortRow, + }; +} 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 78324efb5b..d78dce49d4 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 @@ -10,6 +10,7 @@ import { type ConfigObject } from '../config-schema'; import BiometricsChart from './biometrics-chart.component'; import PaginatedBiometrics from './paginated-biometrics.component'; import styles from './biometrics-base.scss'; +import type { BiometricsTableHeader, BiometricsTableRow } from './types'; interface BiometricsBaseProps { pageSize: number; @@ -36,28 +37,50 @@ const BiometricsBase: React.FC = ({ patientUuid, pageSize, [config, currentVisit], ); - const tableHeaders = [ - { 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})` }, + const tableHeaders: Array = [ { - key: 'muac', + key: 'dateRender', + header: t('dateAndTime', 'Date and time'), + isSortable: true, + sortFunc: (valueA, valueB) => new Date(valueA.date).getTime() - new Date(valueB.date).getTime(), + }, + { + key: 'weightRender', + header: withUnit(t('weight', 'Weight'), conceptUnits.get(config.concepts.weightUuid) ?? ''), + isSortable: true, + sortFunc: (valueA, valueB) => (valueA.weight && valueB.weight ? valueA.weight - valueB.weight : 0), + }, + { + key: 'heightRender', + header: withUnit(t('height', 'Height'), conceptUnits.get(config.concepts.heightUuid) ?? ''), + isSortable: true, + sortFunc: (valueA, valueB) => (valueA.height && valueB.height ? valueA.height - valueB.height : 0), + }, + { + key: 'bmiRender', + header: `${t('bmi', 'BMI')} (${bmiUnit})`, + isSortable: true, + sortFunc: (valueA, valueB) => (valueA.bmi && valueB.bmi ? valueA.bmi - valueB.bmi : 0), + }, + { + key: 'muacRender', header: withUnit(t('muac', 'MUAC'), conceptUnits.get(config.concepts.midUpperArmCircumferenceUuid) ?? ''), + isSortable: true, + sortFunc: (valueA, valueB) => (valueA.muac && valueB.muac ? valueA.muac - valueB.muac : 0), }, ]; - const tableRows = useMemo( + const tableRows: Array = useMemo( () => biometrics?.map((biometricsData, index) => { return { ...biometricsData, id: `${index}`, - date: formatDatetime(parseDate(biometricsData.date.toString()), { mode: 'wide' }), - weight: biometricsData.weight ?? '--', - height: biometricsData.height ?? '--', - bmi: biometricsData.bmi ?? '--', - muac: biometricsData.muac ?? '--', + dateRender: formatDatetime(parseDate(biometricsData.date.toString()), { mode: 'wide' }), + weightRender: biometricsData.weight ?? '--', + heightRender: biometricsData.height ?? '--', + bmiRender: biometricsData.bmi ?? '--', + muacRender: biometricsData.muac ?? '--', }; }), [biometrics], 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 8711adfde8..b16828b3c2 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 @@ -109,10 +109,17 @@ describe('BiometricsOverview: ', () => { const sortRowsButton = screen.getByRole('button', { name: /date and time/i }); + // Sorting in descending order + // Since the date order is already in descending order, the rows should be the same + await user.click(sortRowsButton); + // Sorting in ascending order await user.click(sortRowsButton); expect(screen.getAllByRole('row')).not.toEqual(initialRowElements); + // Sorting order = NONE, hence it is still in the ascending order + await user.click(sortRowsButton); + // Sorting in descending order await user.click(sortRowsButton); expect(screen.getAllByRole('row')).toEqual(initialRowElements); 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 abdb8d6c04..968d11a73f 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, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { DataTable, type DataTableRow, @@ -12,14 +12,14 @@ import { } from '@carbon/react'; import { useLayoutType, usePagination } from '@openmrs/esm-framework'; import { PatientChartPagination } from '@openmrs/esm-patient-common-lib'; -import orderBy from 'lodash/orderBy'; +import type { BiometricsTableHeader, BiometricsTableRow } from './types'; interface PaginatedBiometricsProps { - tableRows: Array; + tableRows: Array; pageSize: number; pageUrl: string; urlLabel: string; - tableHeaders: Array<{ key: string; header: string }>; + tableHeaders: Array; } const PaginatedBiometrics: React.FC = ({ @@ -31,25 +31,41 @@ const PaginatedBiometrics: React.FC = ({ }) => { const isTablet = useLayoutType() === 'tablet'; - const [sortParams, setSortParams] = useState({ key: '', order: 'NONE' }); + const [sortParams, setSortParams] = useState<{ key: string; sortDirection: 'ASC' | 'DESC' | 'NONE' }>({ + key: '', + sortDirection: 'NONE', + }); - const handleSort = (cellA, cellB, { sortDirection }) => { - setSortParams({ key: 'date', order: sortDirection }); + const handleSorting = ( + cellA, + cellB, + { key, sortDirection }: { key: string; sortDirection: 'ASC' | 'DESC' | 'NONE' }, + ) => { + if (sortDirection === 'NONE') { + setSortParams({ key: '', sortDirection }); + } else { + setSortParams({ key, 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 sortedData: Array = useMemo(() => { + if (sortParams.sortDirection === 'NONE') { + return tableRows; + } - const { key, order } = sortParams; + const header = tableHeaders.find((header) => header.key === sortParams.key); - const sortedData = - key === 'date' - ? sortDate(tableRows, order) - : order === 'DESC' - ? orderBy(tableRows, [key], ['desc']) - : orderBy(tableRows, [key], ['asc']); + if (!header) { + return tableRows; + } + + const sortedRows = tableRows.slice().sort((rowA, rowB) => { + const sortingNum = header.sortFunc(rowA, rowB); + return sortParams.sortDirection === 'DESC' ? sortingNum : -sortingNum; + }); + + return sortedRows; + }, [tableRows, tableHeaders, sortParams]); const { results: paginatedBiometrics, goTo, currentPage } = usePagination(sortedData, pageSize); @@ -60,7 +76,8 @@ const PaginatedBiometrics: React.FC = ({ headers={tableHeaders} size={isTablet ? 'lg' : 'sm'} useZebraStyles - sortRow={handleSort} + sortRow={handleSorting} + isSortable > {({ rows, headers, getHeaderProps, getTableProps }) => ( diff --git a/packages/esm-patient-vitals-app/src/biometrics/types.ts b/packages/esm-patient-vitals-app/src/biometrics/types.ts new file mode 100644 index 0000000000..491fd8e85e --- /dev/null +++ b/packages/esm-patient-vitals-app/src/biometrics/types.ts @@ -0,0 +1,17 @@ +import { type PatientVitalsAndBiometrics } from '../common'; + +export interface BiometricsTableRow extends PatientVitalsAndBiometrics { + id: string; + dateRender: string; + weightRender: string | number; + heightRender: string | number; + bmiRender: string | number; + muacRender: string | number; +} + +export interface BiometricsTableHeader { + key: 'dateRender' | 'weightRender' | 'heightRender' | 'bmiRender' | 'muacRender'; + header: string; + isSortable?: boolean; + sortFunc: (valueA: BiometricsTableRow, valueB: BiometricsTableRow) => number; +} 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 9ac5d90c84..3cbcacda39 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 @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import orderBy from 'lodash-es/orderBy'; import { DataTable, @@ -15,15 +15,14 @@ import { import { useLayoutType, usePagination } from '@openmrs/esm-framework'; import { PatientChartPagination } from '@openmrs/esm-patient-common-lib'; import styles from './paginated-vitals.scss'; +import type { VitalsTableHeader, VitalsTableRow } from './types'; interface PaginatedVitalsProps { pageSize: number; pageUrl: string; urlLabel: string; - // @ts-ignore - tableRows: Array; - // @ts-ignore - tableHeaders: Array; + tableRows: Array; + tableHeaders: Array; isPrinting?: boolean; } @@ -52,25 +51,41 @@ const PaginatedVitals: React.FC = ({ } }; - const [sortParams, setSortParams] = useState({ key: '', order: 'none' }); + const [sortParams, setSortParams] = useState<{ key: string; sortDirection: 'ASC' | 'DESC' | 'NONE' }>({ + key: '', + sortDirection: 'NONE', + }); - const handleSort = (cellA, cellB, { sortDirection }) => { - setSortParams({ key: 'date', order: sortDirection }); + const handleSorting = ( + cellA, + cellB, + { key, sortDirection }: { key: string; sortDirection: 'ASC' | 'DESC' | 'NONE' }, + ) => { + if (sortDirection === 'NONE') { + setSortParams({ key: '', sortDirection }); + } else { + setSortParams({ key, 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 sortedData: Array = useMemo(() => { + if (sortParams.sortDirection === 'NONE') { + return tableRows; + } + + const header = tableHeaders.find((header) => header.key === sortParams.key); + + if (!header) { + return tableRows; + } - const { key, order } = sortParams; + const sortedRows = tableRows.slice().sort((rowA, rowB) => { + const sortingNum = header.sortFunc(rowA, rowB); + return sortParams.sortDirection === 'DESC' ? sortingNum : -sortingNum; + }); - const sortedData = - key === 'date' - ? sortDate(tableRows, order) - : order === 'DESC' - ? orderBy(tableRows, [key], ['desc']) - : orderBy(tableRows, [key], ['asc']); + return sortedRows; + }, [tableHeaders, tableRows, sortParams]); const { results: paginatedVitals, goTo, currentPage } = usePagination(sortedData, pageSize); @@ -78,7 +93,14 @@ const PaginatedVitals: React.FC = ({ return (
- + {({ rows, headers, getTableProps, getHeaderProps }) => ( diff --git a/packages/esm-patient-vitals-app/src/vitals/types.ts b/packages/esm-patient-vitals-app/src/vitals/types.ts new file mode 100644 index 0000000000..b63618855c --- /dev/null +++ b/packages/esm-patient-vitals-app/src/vitals/types.ts @@ -0,0 +1,24 @@ +import { type PatientVitalsAndBiometrics } from '../common'; + +export interface VitalsTableRow extends PatientVitalsAndBiometrics { + id: string; + dateRender: string; + bloodPressureRender: string; + pulseRender: string | number; + spo2Render: string | number; + temperatureRender: string | number; + respiratoryRateRender: string | number; +} + +export interface VitalsTableHeader { + key: + | 'dateRender' + | 'temperatureRender' + | 'bloodPressureRender' + | 'pulseRender' + | 'respiratoryRateRender' + | 'spo2Render'; + header: string; + isSortable?: boolean; + sortFunc: (valueA: VitalsTableRow, valueB: VitalsTableRow) => number; +} diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-overview.component.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-overview.component.tsx index 82818565e3..ad00d39391 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-overview.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-overview.component.tsx @@ -12,6 +12,7 @@ import PaginatedVitals from './paginated-vitals.component'; import PrintComponent from './print/print.component'; import VitalsChart from './vitals-chart.component'; import styles from './vitals-overview.scss'; +import type { VitalsTableHeader, VitalsTableRow } from './types'; interface VitalsOverviewProps { patientUuid: string; @@ -70,39 +71,69 @@ const VitalsOverview: React.FC = ({ patientUuid, pageSize, }; }, [patient, t, excludePatientIdentifierCodeTypes?.uuids]); - const tableHeaders = [ - { key: 'date', header: t('dateAndTime', 'Date and time'), isSortable: true }, + const tableHeaders: Array = [ { - key: 'temperature', + key: 'dateRender', + header: t('dateAndTime', 'Date and time'), + isSortable: true, + sortFunc: (valueA, valueB) => new Date(valueA.date).getTime() - new Date(valueB.date).getTime(), + }, + { + key: 'temperatureRender', header: withUnit(t('temperature', 'Temp'), conceptUnits.get(config.concepts.temperatureUuid) ?? ''), + isSortable: true, + + sortFunc: (valueA, valueB) => + valueA.temperature && valueB.temperature ? valueA.temperature - valueB.temperature : 0, }, { - key: 'bloodPressure', + key: 'bloodPressureRender', header: withUnit(t('bloodPressure', 'BP'), conceptUnits.get(config.concepts.systolicBloodPressureUuid) ?? ''), + isSortable: true, + + sortFunc: (valueA, valueB) => + valueA.systolic && valueB.systolic && valueA.diastolic && valueB.diastolic + ? valueA.systolic !== valueB.systolic + ? valueA.systolic - valueB.systolic + : valueA.diastolic - valueB.diastolic + : 0, }, - { key: 'pulse', header: withUnit(t('pulse', 'Pulse'), conceptUnits.get(config.concepts.pulseUuid) ?? '') }, { - key: 'respiratoryRate', + key: 'pulseRender', + header: withUnit(t('pulse', 'Pulse'), conceptUnits.get(config.concepts.pulseUuid) ?? ''), + isSortable: true, + + sortFunc: (valueA, valueB) => (valueA.pulse && valueB.pulse ? valueA.pulse - valueB.pulse : 0), + }, + { + key: 'respiratoryRateRender', header: withUnit(t('respiratoryRate', 'R. Rate'), conceptUnits.get(config.concepts.respiratoryRateUuid) ?? ''), + isSortable: true, + + sortFunc: (valueA, valueB) => + valueA.respiratoryRate && valueB.respiratoryRate ? valueA.respiratoryRate - valueB.respiratoryRate : 0, }, { - key: 'spo2', + key: 'spo2Render', header: withUnit(t('spo2', 'SPO2'), conceptUnits.get(config.concepts.oxygenSaturationUuid) ?? ''), + isSortable: true, + + sortFunc: (valueA, valueB) => (valueA.spo2 && valueB.spo2 ? valueA.spo2 - valueB.spo2 : 0), }, ]; - const tableRows = useMemo( + const tableRows: Array = useMemo( () => vitals?.map((vitalSigns, index) => { return { ...vitalSigns, id: `${index}`, - date: formatDate(parseDate(vitalSigns.date.toString()), { mode: 'wide', time: true }), - bloodPressure: `${vitalSigns.systolic ?? '--'} / ${vitalSigns.diastolic ?? '--'}`, - pulse: vitalSigns.pulse ?? '--', - spo2: vitalSigns.spo2 ?? '--', - temperature: vitalSigns.temperature ?? '--', - respiratoryRate: vitalSigns.respiratoryRate ?? '--', + dateRender: formatDate(parseDate(vitalSigns.date.toString()), { mode: 'wide', time: true }), + bloodPressureRender: `${vitalSigns.systolic ?? '--'} / ${vitalSigns.diastolic ?? '--'}`, + pulseRender: vitalSigns.pulse ?? '--', + spo2Render: vitalSigns.spo2 ?? '--', + temperatureRender: vitalSigns.temperature ?? '--', + respiratoryRateRender: vitalSigns.respiratoryRate ?? '--', }; }), [vitals], 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 7d1df29e13..26a553b63b 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 @@ -119,10 +119,17 @@ describe('VitalsOverview', () => { const sortRowsButton = screen.getByRole('button', { name: /date and time/i }); + // Sorting in descending order + // Since the date order is already in descending order, the rows should be the same + await user.click(sortRowsButton); + // Sorting in ascending order await user.click(sortRowsButton); expect(screen.getAllByRole('row')).not.toEqual(initialRowElements); + // Sorting order = NONE, hence it is still in the ascending order + await user.click(sortRowsButton); + // Sorting in descending order await user.click(sortRowsButton); expect(screen.getAllByRole('row')).toEqual(initialRowElements);