Skip to content

Commit

Permalink
Migration to Material UI fixes: "Some buttons do not show their curso…
Browse files Browse the repository at this point in the history
…r state".

Fixes #6443.
  • Loading branch information
fniessink committed Jan 22, 2025
1 parent 3cbe16a commit 2c8abfc
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 155 deletions.
116 changes: 44 additions & 72 deletions components/frontend/src/source/SourceEntities.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
TableContainer,
TableHead,
TableRow,
TableSortLabel,
Tooltip,
} from "@mui/material"
import { bool, func, object, string } from "prop-types"
Expand All @@ -19,7 +18,6 @@ import { useContext, useState } from "react"
import { DataModel } from "../context/DataModel"
import {
alignmentPropType,
childrenPropType,
entityAttributePropType,
entityAttributesPropType,
entityAttributeTypePropType,
Expand All @@ -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"

Expand Down Expand Up @@ -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 (
<TableCell align={textAlign} direction={sorted(column, sortColumn, sortDirection)}>
<TableSortLabel
active={column === sortColumn}
direction={column === sortColumn ? MuiSortDirection(sortDirection) : "asc"}
onClick={() =>
sort(column, columnType, setColumnType, setSortColumn, setSortDirection, sortColumn, sortDirection)
}
>
{children}
</TableSortLabel>
</TableCell>
)
}
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 (
<SortableHeaderCell
<SortableTableHeaderCell
column={entityAttribute.key}
columnType={entityAttribute.type || "text"}
handleSort={handleSort}
textAlign={alignment(entityAttribute.type, entityAttribute.alignment)}
{...sortProps}
>
Expand All @@ -166,7 +114,7 @@ function EntityAttributeHeaderCell({ entityAttribute, ...sortProps }) {
</span>
</Tooltip>
) : null}
</SortableHeaderCell>
</SortableTableHeaderCell>
)
}
EntityAttributeHeaderCell.propTypes = {
Expand All @@ -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 (
<TableRow>
<TableCell align="center">
Expand All @@ -200,18 +156,34 @@ function sourceEntitiesHeaders(
</IconButton>
</Tooltip>
</TableCell>
<SortableHeaderCell column="entity_status" columnType="text" {...sortProps}>
<SortableTableHeaderCell
column="entity_status"
handleSort={(column) => handleSort(column, "text")}
{...sortProps}
>
{`${capitalize(entityName)} status`}
</SortableHeaderCell>
<SortableHeaderCell column="status_end_date" columnType="date" {...sortProps}>
</SortableTableHeaderCell>
<SortableTableHeaderCell
column="status_end_date"
handleSort={(column) => handleSort(column, "date")}
{...sortProps}
>
Status end date
</SortableHeaderCell>
<SortableHeaderCell column="rationale" columnType="text" {...sortProps}>
</SortableTableHeaderCell>
<SortableTableHeaderCell
column="rationale"
handleSort={(column) => handleSort(column, "text")}
{...sortProps}
>
Status rationale
</SortableHeaderCell>
<SortableHeaderCell column="first_seen" columnType="datetime" {...sortProps}>
</SortableTableHeaderCell>
<SortableTableHeaderCell
column="first_seen"
handleSort={(column) => handleSort(column, "datetime")}
{...sortProps}
>
{capitalize(entityName)} first seen
</SortableHeaderCell>
</SortableTableHeaderCell>
{entityAttributes.map((entityAttribute) => (
<EntityAttributeHeaderCell entityAttribute={entityAttribute} key={entityAttribute.key} {...sortProps} />
))}
Expand Down
88 changes: 22 additions & 66 deletions components/frontend/src/source/SourceEntities.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
2 changes: 1 addition & 1 deletion components/frontend/src/subject/SubjectTableHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
41 changes: 28 additions & 13 deletions components/frontend/src/widgets/TableHeaderCell.js
Original file line number Diff line number Diff line change
@@ -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 }) {
Expand All @@ -28,6 +28,7 @@ function MuiSortDirection(sortDirection) {
}

export function SortableTableHeaderCell({
children,
colSpan,
column,
sortColumn,
Expand All @@ -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 (
<TableCell align={textAlign || "left"} colSpan={colSpan} sortDirection={sorted}>
<TableSortLabel
active={column === sortColumn.value}
direction={column === sortColumn.value ? MuiSortDirection(sortDirection.value) : "asc"}
onClick={() => handleSort(column)}
<TableCell colSpan={colSpan} sortDirection={sorted}>
<ButtonBase
focusRipple
sx={{
display: "block",
fontSize: "inherit",
padding: "inherit",
textAlign: align,
width: "100%",
}}
tabIndex={-1}
>
<TableHeaderCellContents help={help} label={label} />
</TableSortLabel>
<TableSortLabel
active={column === sortColumn}
direction={column === sortColumn ? MuiSortDirection(sortDirection) : "asc"}
onClick={() => handleSort(column)}
>
{children || <TableHeaderCellContents help={help} label={label} />}
</TableSortLabel>
</ButtonBase>
</TableCell>
)
}
SortableTableHeaderCell.propTypes = {
children: childrenPropType,
colSpan: string,
column: string,
handleSort: func,
help: popupContentPropType,
label: labelPropType,
sortColumn: stringURLSearchQueryPropType,
sortDirection: sortDirectionURLSearchQueryPropType,
sortColumn: string,
sortDirection: sortDirectionPropType,
textAlign: alignmentPropType,
}

Expand Down
4 changes: 2 additions & 2 deletions components/frontend/src/widgets/TableHeaderCell.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ function renderSortableTableHeaderCell(help) {
<SortableTableHeaderCell
label="Header"
help={help}
sortColumn={settings.sortColumn}
sortDirection={settings.sortDirection}
sortColumn={settings.sortColumn.value}
sortDirection={settings.sortDirection.value}
/>
</TableRow>
</TableHead>
Expand Down
Loading

0 comments on commit 2c8abfc

Please sign in to comment.