diff --git a/components/frontend/src/source/SourceEntities.js b/components/frontend/src/source/SourceEntities.js
index a6fb76373f..31281f37b8 100644
--- a/components/frontend/src/source/SourceEntities.js
+++ b/components/frontend/src/source/SourceEntities.js
@@ -10,7 +10,6 @@ import {
TableContainer,
TableHead,
TableRow,
- TableSortLabel,
Tooltip,
} from "@mui/material"
import { bool, func, object, string } from "prop-types"
@@ -19,7 +18,6 @@ import { useContext, useState } from "react"
import { DataModel } from "../context/DataModel"
import {
alignmentPropType,
- childrenPropType,
entityAttributePropType,
entityAttributesPropType,
entityAttributeTypePropType,
@@ -34,6 +32,7 @@ import {
import { capitalize } from "../utils"
import { IgnoreIcon, ShowIcon } from "../widgets/icons"
import { LoadingPlaceHolder } from "../widgets/Placeholder"
+import { SortableTableHeaderCell } from "../widgets/TableHeaderCell"
import { FailedToLoadMeasurementsWarningMessage, InfoMessage } from "../widgets/WarningMessage"
import { SourceEntity } from "./SourceEntity"
@@ -90,70 +89,19 @@ sorted.propTypes = {
sortDirection: sortDirectionPropType,
}
-function sort(column, columnType, setColumnType, setSortColumn, setSortDirection, sortColumn, sortDirection) {
- setColumnType(columnType)
- if (column === sortColumn) {
- setSortDirection(sortDirection === "ascending" ? "descending" : "ascending")
- } else {
- setSortColumn(column)
- }
-}
-sort.propTypes = {
- column: string,
- columnType: entityAttributeTypePropType,
- setColumnType: func,
- setSortColumn: func,
- setSortDirection: func,
- sortColumn: string,
- sortDirection: sortDirectionPropType,
-}
-
-function MuiSortDirection(sortDirection) {
- return sortDirection === "ascending" ? "asc" : "desc"
-}
-
-function SortableHeaderCell({
- children,
- column,
- columnType,
- setColumnType,
- setSortColumn,
- setSortDirection,
- sortColumn,
- sortDirection,
- textAlign,
-}) {
- return (
-
-
- sort(column, columnType, setColumnType, setSortColumn, setSortDirection, sortColumn, sortDirection)
- }
- >
- {children}
-
-
- )
-}
-SortableHeaderCell.propTypes = {
- children: childrenPropType,
- column: string,
- columnType: entityAttributeTypePropType,
- setColumnType: func,
- setSortColumn: func,
- setSortDirection: func,
- sortColumn: string,
- sortDirection: sortDirectionPropType,
- textAlign: alignmentPropType,
-}
-
function EntityAttributeHeaderCell({ entityAttribute, ...sortProps }) {
+ function handleSort(column) {
+ sortProps.setColumnType(entityAttribute.type || "text")
+ if (column === sortProps.sortColumn) {
+ sortProps.setSortDirection(sortProps.sortDirection === "ascending" ? "descending" : "ascending")
+ } else {
+ sortProps.setSortColumn(column)
+ }
+ }
return (
-
@@ -166,7 +114,7 @@ function EntityAttributeHeaderCell({ entityAttribute, ...sortProps }) {
) : null}
-
+
)
}
EntityAttributeHeaderCell.propTypes = {
@@ -188,6 +136,14 @@ function sourceEntitiesHeaders(
const entityName = metricEntities.name
const entityNamePlural = metricEntities.name_plural
const hideIgnoredEntitiesLabel = `${hideIgnoredEntities ? "Show" : "Hide"} ignored ${entityNamePlural}`
+ function handleSort(column, columnType) {
+ sortProps.setColumnType(columnType)
+ if (column === sortProps.sortColumn) {
+ sortProps.setSortDirection(sortProps.sortDirection === "ascending" ? "descending" : "ascending")
+ } else {
+ sortProps.setSortColumn(column)
+ }
+ }
return (
@@ -200,18 +156,34 @@ function sourceEntitiesHeaders(
-
+ handleSort(column, "text")}
+ {...sortProps}
+ >
{`${capitalize(entityName)} status`}
-
-
+
+ handleSort(column, "date")}
+ {...sortProps}
+ >
Status end date
-
-
+
+ handleSort(column, "text")}
+ {...sortProps}
+ >
Status rationale
-
-
+
+ handleSort(column, "datetime")}
+ {...sortProps}
+ >
{capitalize(entityName)} first seen
-
+
{entityAttributes.map((entityAttribute) => (
))}
diff --git a/components/frontend/src/source/SourceEntities.test.js b/components/frontend/src/source/SourceEntities.test.js
index 844d0b14fb..10f1e91e18 100644
--- a/components/frontend/src/source/SourceEntities.test.js
+++ b/components/frontend/src/source/SourceEntities.test.js
@@ -180,103 +180,59 @@ it("shows the show ignored entities button", async () => {
expect(hideEntitiesButton).toHaveAttribute("aria-label", "Show ignored entities")
})
-it("sorts the entities by status", async () => {
+async function expectColumnIsSortedCorrectly(header, ascending) {
renderSourceEntities()
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/Entity name status/))
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/Entity name status/))
- expectOrder(["A", "B", "C"])
+ expectOrder(["C", "B", "A"]) // Initial order
+ await userEvent.click(screen.getByText(header))
+ expectOrder(ascending)
+ await userEvent.click(screen.getByText(header))
+ expectOrder(ascending.toReversed())
+ await userEvent.click(screen.getByText(header))
+ expectOrder(ascending)
+}
+
+it("sorts the entities by status", async () => {
+ await expectColumnIsSortedCorrectly(/Entity name status/, ["C", "B", "A"])
})
it("sorts the entities by status end date", async () => {
- renderSourceEntities()
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/Status end date/))
- expectOrder(["A", "C", "B"])
- await userEvent.click(screen.getByText(/Status end date/))
- expectOrder(["B", "C", "A"])
+ await expectColumnIsSortedCorrectly(/Status end date/, ["A", "C", "B"])
})
it("sorts the entities by status rationale", async () => {
- renderSourceEntities()
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/Status rationale/))
- expectOrder(["A", "C", "B"])
- await userEvent.click(screen.getByText(/Status rationale/))
- expectOrder(["B", "C", "A"])
+ await expectColumnIsSortedCorrectly(/Status rationale/, ["A", "C", "B"])
})
it("sorts the entities by first seen date", async () => {
- renderSourceEntities()
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/first seen/))
- expectOrder(["C", "A", "B"])
- await userEvent.click(screen.getByText(/first seen/))
- expectOrder(["B", "A", "C"])
+ await expectColumnIsSortedCorrectly(/first seen/, ["C", "A", "B"])
})
it("sorts the entities by integer", async () => {
- renderSourceEntities()
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/integer/))
- expectOrder(["C", "A", "B"])
- await userEvent.click(screen.getByText(/integer/))
- expectOrder(["B", "A", "C"])
+ await expectColumnIsSortedCorrectly(/integer/, ["C", "A", "B"])
})
it("sorts the entities by integer percentage", async () => {
- renderSourceEntities()
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/int percentage/))
- expectOrder(["C", "A", "B"])
- await userEvent.click(screen.getByText(/int percentage/))
- expectOrder(["B", "A", "C"])
+ await expectColumnIsSortedCorrectly(/int percentage/, ["C", "A", "B"])
})
it("sorts the entities by float", async () => {
- renderSourceEntities()
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/float/))
- expectOrder(["A", "B", "C"])
- await userEvent.click(screen.getByText(/float/))
- expectOrder(["C", "B", "A"])
+ await expectColumnIsSortedCorrectly(/float/, ["A", "B", "C"])
})
it("sorts the entities by text", async () => {
- renderSourceEntities()
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/text/))
- expectOrder(["A", "B", "C"])
- await userEvent.click(screen.getByText(/text/))
- expectOrder(["C", "B", "A"])
+ await expectColumnIsSortedCorrectly(/text/, ["A", "B", "C"])
})
it("sorts the entities by date", async () => {
- renderSourceEntities()
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/date only/))
- expectOrder(["C", "A", "B"])
- await userEvent.click(screen.getByText(/date only/))
- expectOrder(["B", "A", "C"])
+ await expectColumnIsSortedCorrectly(/date only/, ["C", "A", "B"])
})
it("sorts the entities by datetime", async () => {
- renderSourceEntities()
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/datetime/))
- expectOrder(["C", "A", "B"])
- await userEvent.click(screen.getByText(/datetime/))
- expectOrder(["B", "A", "C"])
+ await expectColumnIsSortedCorrectly(/datetime/, ["C", "A", "B"])
})
it("sorts the entities by minutes", async () => {
- renderSourceEntities()
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/minutes/))
- expectOrder(["C", "B", "A"])
- await userEvent.click(screen.getByText(/minutes/))
- expectOrder(["A", "B", "C"])
+ await expectColumnIsSortedCorrectly(/minutes/, ["C", "B", "A"])
})
it("shows help", async () => {
diff --git a/components/frontend/src/subject/SubjectTableHeader.js b/components/frontend/src/subject/SubjectTableHeader.js
index 8a53f9be4a..88e0799fd6 100644
--- a/components/frontend/src/subject/SubjectTableHeader.js
+++ b/components/frontend/src/subject/SubjectTableHeader.js
@@ -291,7 +291,7 @@ MeasurementHeaderCells.propTypes = {
export function SubjectTableHeader({ columnDates, handleSort, settings }) {
const sortProps = {
- sortColumn: settings.sortColumn,
+ sortColumn: settings.sortColumn.value,
sortDirection: settings.sortDirection,
handleSort: handleSort,
}
diff --git a/components/frontend/src/widgets/TableHeaderCell.js b/components/frontend/src/widgets/TableHeaderCell.js
index 566a7060bf..618824b0f7 100644
--- a/components/frontend/src/widgets/TableHeaderCell.js
+++ b/components/frontend/src/widgets/TableHeaderCell.js
@@ -1,12 +1,12 @@
-import { TableCell, TableSortLabel, Tooltip } from "@mui/material"
+import { ButtonBase, TableCell, TableSortLabel, Tooltip } from "@mui/material"
import { func, string } from "prop-types"
import {
alignmentPropType,
+ childrenPropType,
labelPropType,
popupContentPropType,
- sortDirectionURLSearchQueryPropType,
- stringURLSearchQueryPropType,
+ sortDirectionPropType,
} from "../sharedPropTypes"
function TableHeaderCellContents({ help, label }) {
@@ -28,6 +28,7 @@ function MuiSortDirection(sortDirection) {
}
export function SortableTableHeaderCell({
+ children,
colSpan,
column,
sortColumn,
@@ -37,27 +38,41 @@ export function SortableTableHeaderCell({
textAlign,
help,
}) {
- const sorted = sortColumn.value === column ? MuiSortDirection(sortDirection.value) : null
+ const sorted = sortColumn === column ? MuiSortDirection(sortDirection) : null
+ const align = textAlign || "left"
return (
-
- handleSort(column)}
+
+
-
-
+ handleSort(column)}
+ >
+ {children || }
+
+
)
}
SortableTableHeaderCell.propTypes = {
+ children: childrenPropType,
colSpan: string,
column: string,
handleSort: func,
help: popupContentPropType,
label: labelPropType,
- sortColumn: stringURLSearchQueryPropType,
- sortDirection: sortDirectionURLSearchQueryPropType,
+ sortColumn: string,
+ sortDirection: sortDirectionPropType,
textAlign: alignmentPropType,
}
diff --git a/components/frontend/src/widgets/TableHeaderCell.test.js b/components/frontend/src/widgets/TableHeaderCell.test.js
index 72e793885f..03cb2cfb7d 100644
--- a/components/frontend/src/widgets/TableHeaderCell.test.js
+++ b/components/frontend/src/widgets/TableHeaderCell.test.js
@@ -14,8 +14,8 @@ function renderSortableTableHeaderCell(help) {
diff --git a/docs/src/changelog.md b/docs/src/changelog.md
index 77039ebf27..be781432bd 100644
--- a/docs/src/changelog.md
+++ b/docs/src/changelog.md
@@ -22,7 +22,7 @@ If your currently installed *Quality-time* version is not the latest version, pl
### Changed
-- Completed the replacement of Semantic UI React with Material UI as frontend component library. Fixes [#5180] (https://github.com/ICTU/quality-time/issues/5180), [#9904](https://github.com/ICTU/quality-time/issues/9904) and [#10159](https://github.com/ICTU/quality-time/issues/10159). Closes [#9796](https://github.com/ICTU/quality-time/issues/9796).
+- Completed the replacement of Semantic UI React with Material UI as frontend component library. Fixes [#5180](https://github.com/ICTU/quality-time/issues/5180), [#6443](https://github.com/ICTU/quality-time/issues/6443), [#9904](https://github.com/ICTU/quality-time/issues/9904) and [#10159](https://github.com/ICTU/quality-time/issues/10159). Closes [#9796](https://github.com/ICTU/quality-time/issues/9796).
## v5.22.0 - 2025-01-16