+
-
+
}
/>
)
diff --git a/components/frontend/src/subject/SubjectTableFooter.test.js b/components/frontend/src/subject/SubjectTableFooter.test.js
index 72de24050c..62f2293724 100644
--- a/components/frontend/src/subject/SubjectTableFooter.test.js
+++ b/components/frontend/src/subject/SubjectTableFooter.test.js
@@ -1,5 +1,5 @@
+import { Table } from "@mui/material"
import { act, fireEvent, render, screen } from "@testing-library/react"
-import { Table } from "semantic-ui-react"
import { dataModel, report } from "../__fixtures__/fixtures"
import * as fetch_server_api from "../api/fetch_server_api"
diff --git a/components/frontend/src/subject/SubjectTableHeader.js b/components/frontend/src/subject/SubjectTableHeader.js
index c30cd58081..f6b5ff43c8 100644
--- a/components/frontend/src/subject/SubjectTableHeader.js
+++ b/components/frontend/src/subject/SubjectTableHeader.js
@@ -1,10 +1,8 @@
-import { List, ListItem, ListItemIcon, ListItemText } from "@mui/material"
+import { Chip, List, ListItem, ListItemIcon, ListItemText, Paper, TableHead, TableRow, Typography } from "@mui/material"
import { bool, func, string } from "prop-types"
-import { Table } from "semantic-ui-react"
import { StatusIcon } from "../measurement/StatusIcon"
import { STATUS_DESCRIPTION, STATUSES } from "../metric/status"
-import { Label } from "../semantic_ui_react_wrappers"
import { datesPropType, settingsPropType } from "../sharedPropTypes"
import { HyperLink } from "../widgets/HyperLink"
import { IgnoreIcon, TriangleRightIcon } from "../widgets/icons"
@@ -78,9 +76,9 @@ const measurementHelp = (
If the measurement value has a{" "}
-
+
red background
-
+
, the metric has not been measured recently. This indicates a problem with Quality-time itself, and
a system administrator should be notified.
@@ -99,9 +97,9 @@ const targetHelp = (
The value against which measurements are evaluated to determine whether a metric needs action.
The target value has a{" "}
-
+
grey background
- {" "}
+ {" "}
if the metric has accepted technical debt that is not applied because the technical debt end date is in the
past or all issues linked to the metric have been resolved.
@@ -172,9 +170,9 @@ const sourcesHelp = (
The tools and reports accessed to collect the measurement data. One metric can have multiple sources.
If a source has a{" "}
-
+
red background
-
+
, the source could not be accessed or the data could not be parsed. metric and navigate to
the source to see the error details.
@@ -194,9 +192,9 @@ const issuesHelp = (
If an issue has a{" "}
-
+
red background
-
+
, the issue tracker could not be accessed or the data could not be parsed. metric and
navigate to the technical debt tab to see the error details.
@@ -222,6 +220,18 @@ const tagsHelp = (
>
)
+function InlineChip({ color, label }) {
+ return (
+
+
+
+ )
+}
+InlineChip.propTypes = {
+ color: string,
+ label: string,
+}
+
function MeasurementHeaderCells({ columnDates, showDeltaColumns }) {
const cells = []
columnDates.forEach((date, index) => {
@@ -236,29 +246,16 @@ function MeasurementHeaderCells({ columnDates, showDeltaColumns }) {
and next date.
- A plus sign{" "}
-
- +
- {" "}
- indicates that the newer value is higher. A minus sign{" "}
-
- -
- {" "}
- indicates that the newer value is lower.
+ A plus sign indicates that the newer value is
+ higher. A minus sign indicates that the newer
+ value is lower.
- A{" "}
-
- green outline
- {" "}
+ A
indicates that the newer value is better. A{" "}
-
- red outline
- {" "}
+
indicates that the newer value is worse. A{" "}
-
- blue outline
- {" "}
+
is used for metrics that are informative.
@@ -289,8 +286,8 @@ export function SubjectTableHeader({ columnDates, handleSort, settings }) {
}
const nrDates = columnDates.length
return (
-
-
+
+
{nrDates > 1 && (
)}
-
-
+
+
)
}
SubjectTableHeader.propTypes = {
diff --git a/components/frontend/src/subject/SubjectTableHeader.test.js b/components/frontend/src/subject/SubjectTableHeader.test.js
index a6b6f3eceb..2e49aa59c8 100644
--- a/components/frontend/src/subject/SubjectTableHeader.test.js
+++ b/components/frontend/src/subject/SubjectTableHeader.test.js
@@ -1,7 +1,7 @@
+import { Table } from "@mui/material"
import { render, screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import history from "history/browser"
-import { Table } from "semantic-ui-react"
import { createTestableSettings } from "../__fixtures__/fixtures"
import { SubjectTableHeader } from "./SubjectTableHeader"
@@ -83,3 +83,13 @@ it("shows help for column headers", async () => {
expect(screen.queryByText(/Click the column header to sort the metrics by name/)).not.toBe(null)
})
})
+
+it("shows help for delta column headers", async () => {
+ const date1 = new Date("2022-02-02")
+ const date2 = new Date("2022-02-03")
+ renderSubjectTableHeader([date1, date2])
+ await userEvent.hover(screen.getByText(/𝚫/))
+ await waitFor(() => {
+ expect(screen.queryByText(/shows the difference/)).not.toBe(null)
+ })
+})
diff --git a/components/frontend/src/subject/SubjectTableRow.js b/components/frontend/src/subject/SubjectTableRow.js
index d338f4a430..1e1d041fee 100644
--- a/components/frontend/src/subject/SubjectTableRow.js
+++ b/components/frontend/src/subject/SubjectTableRow.js
@@ -1,7 +1,7 @@
+import { Chip, TableCell, Tooltip } from "@mui/material"
import { bool, func, number, object, string } from "prop-types"
import { useContext } from "react"
-import { DarkMode } from "../context/DarkMode"
import { DataModel } from "../context/DataModel"
import { IssueStatus } from "../issue/IssueStatus"
import { MeasurementSources } from "../measurement/MeasurementSources"
@@ -12,7 +12,6 @@ import { StatusIcon } from "../measurement/StatusIcon"
import { TimeLeft } from "../measurement/TimeLeft"
import { TrendSparkline } from "../measurement/TrendSparkline"
import { MetricDetails } from "../metric/MetricDetails"
-import { Label, Popup, Table } from "../semantic_ui_react_wrappers"
import {
dataModelPropType,
datePropType,
@@ -68,9 +67,9 @@ didValueImprove.propTypes = {
function deltaColor(metric, improved) {
const evaluateTarget = metric.evaluate_targets ?? true
if (evaluateTarget) {
- return improved ? "green" : "red"
+ return improved ? "success" : "error"
}
- return "blue"
+ return "info"
}
deltaColor.propTypes = {
metric: metricPropType,
@@ -131,20 +130,15 @@ function DeltaCell({ dateOrderAscending, index, metric, metricValue, previousVal
const description = deltaDescription(dataModel, metric, scale, delta, improved, oldValue, newValue)
const color = deltaColor(metric, improved)
label = (
-
- {delta}
-
- }
- />
+
+
+
)
}
return (
-
+
{label}
-
+
)
}
DeltaCell.propTypes = {
@@ -208,10 +202,10 @@ function MeasurementCells({ dates, metric, metric_uuid, measurements, settings }
)
}
cells.push(
-
+
{formatMetricValue(scale, metricValue)}
{formatMetricScale(metric, dataModel)}
- ,
+ ,
)
previousValue = metricValue === "?" ? previousValue : metricValue
})
@@ -252,15 +246,14 @@ export function SubjectTableRow({
subject_uuid,
}) {
const dataModel = useContext(DataModel)
- const darkMode = useContext(DarkMode)
const metricName = getMetricName(metric, dataModel)
const scale = getMetricScale(metric, dataModel)
const unit = getMetricUnit(metric, dataModel)
const nrDates = dates.length
- const style = nrDates > 1 ? { background: darkMode ? "rgba(60, 60, 60, 1)" : "#f9fafb" } : {}
return (
}
expanded={settings.expandedItems.value.filter((item) => item?.startsWith(metric_uuid)).length > 0}
id={metric_uuid}
onExpand={(expand) => expandOrCollapseItem(expand, metric_uuid, settings.expandedItems)}
- style={style}
>
- {metricName}
+ {metricName}
{nrDates > 1 && (
)}
{nrDates === 1 && settings.hiddenColumns.excludes("trend") && (
-
+
-
+
)}
{nrDates === 1 && settings.hiddenColumns.excludes("status") && (
-
+
-
+
)}
{nrDates === 1 && settings.hiddenColumns.excludes("measurement") && (
-
+
-
+
)}
{nrDates === 1 && settings.hiddenColumns.excludes("target") && (
-
+
-
+
)}
- {settings.hiddenColumns.excludes("unit") && {unit} }
+ {settings.hiddenColumns.excludes("unit") && {unit} }
{settings.hiddenColumns.excludes("source") && (
-
+
-
+
)}
{settings.hiddenColumns.excludes("time_left") && (
-
+
-
+
)}
{nrDates > 1 && settings.hiddenColumns.excludes("overrun") && (
-
+
-
+
)}
{settings.hiddenColumns.excludes("comment") && (
-
+
{metric.comment}
-
+
)}
{settings.hiddenColumns.excludes("issues") && (
-
+
-
+
)}
{settings.hiddenColumns.excludes("tags") && (
-
+
{getMetricTags(metric).map((tag) => (
))}
-
+
)}
)
diff --git a/components/frontend/src/subject/SubjectTableRow.test.js b/components/frontend/src/subject/SubjectTableRow.test.js
index 1d254d2470..8cbc517666 100644
--- a/components/frontend/src/subject/SubjectTableRow.test.js
+++ b/components/frontend/src/subject/SubjectTableRow.test.js
@@ -1,9 +1,9 @@
+import { Table, TableBody } from "@mui/material"
import { render, screen } from "@testing-library/react"
import history from "history/browser"
import { createTestableSettings, dataModel, report } from "../__fixtures__/fixtures"
import { DataModel } from "../context/DataModel"
-import { Table } from "../semantic_ui_react_wrappers"
import { SubjectTableRow } from "./SubjectTableRow"
beforeEach(() => {
@@ -47,7 +47,7 @@ function renderSubjectTableRow({
render(
,
)
diff --git a/components/frontend/src/subject/SubjectTitle.js b/components/frontend/src/subject/SubjectTitle.js
index 23bc9fcc2d..a8ea598a5e 100644
--- a/components/frontend/src/subject/SubjectTitle.js
+++ b/components/frontend/src/subject/SubjectTitle.js
@@ -1,33 +1,35 @@
+import HistoryIcon from "@mui/icons-material/History"
+import SettingsIcon from "@mui/icons-material/Settings"
import { bool, func, object, string } from "prop-types"
import { useContext } from "react"
import { delete_subject, set_subject_attribute } from "../api/subject"
-import { activeTabIndex, tabChangeHandler } from "../app_ui_settings"
import { ChangeLog } from "../changelog/ChangeLog"
import { DataModel } from "../context/DataModel"
import { EDIT_REPORT_PERMISSION, ReadOnlyOrEditable } from "../context/Permissions"
-import { Header, Tab } from "../semantic_ui_react_wrappers"
import { reportPropType, settingsPropType } from "../sharedPropTypes"
import { getSubjectType, referenceDocumentationURL } from "../utils"
import { ButtonRow } from "../widgets/ButtonRow"
import { DeleteButton } from "../widgets/buttons/DeleteButton"
import { PermLinkButton } from "../widgets/buttons/PermLinkButton"
import { ReorderButtonGroup } from "../widgets/buttons/ReorderButtonGroup"
+import { Header } from "../widgets/Header"
import { HeaderWithDetails } from "../widgets/HeaderWithDetails"
import { ReadTheDocsLink } from "../widgets/ReadTheDocsLink"
-import { changelogTabPane, configurationTabPane } from "../widgets/TabPane"
+import { Tabs } from "../widgets/Tabs"
import { SubjectParameters } from "./SubjectParameters"
function SubjectHeader({ subjectType }) {
return (
-
-
- {subjectType.name}
-
+
{subjectType.description}
-
-
-
+ >
+ }
+ />
)
}
SubjectHeader.propTypes = {
@@ -74,42 +76,33 @@ export function SubjectTitle({
settings,
}) {
const dataModel = useContext(DataModel)
- const tabIndex = activeTabIndex(settings.expandedItems, subject_uuid)
const subjectType = getSubjectType(subject.type, dataModel.subjects) || { name: "Unknown subject type" }
const subjectName = subject.name || subjectType.name
const subjectTitle = (atReportsOverview ? report.title + " ❯ " : "") + subjectName
const subjectUrl = `${window.location}#${subject_uuid}`
- const panes = [
- configurationTabPane(
- ,
- ),
- changelogTabPane( ),
- ]
-
return (
-
+ },
+ { label: "Changelog", icon: },
+ ]}
+ >
+
+
+
{
- history.push("?expanded=subject_uuid:0")
+ history.push("?expanded=subject_uuid")
})
const dataModel = {
@@ -53,19 +53,14 @@ async function renderSubjectTitle(subject_type = "subject_type") {
it("changes the subject type", async () => {
fetch_server_api.fetch_server_api = jest.fn().mockResolvedValue({ ok: true })
await renderSubjectTitle()
- await userEvent.click(screen.getAllByText(/Default subject type/)[1])
+ fireEvent.mouseDown(screen.getByLabelText(/Subject type/))
+ //await userEvent.click(screen.getAllByText(/Default subject type/)[1])
await userEvent.click(screen.getByText(/Other subject type/))
expect(fetch_server_api.fetch_server_api).toHaveBeenLastCalledWith("post", "subject/subject_uuid/attribute/type", {
type: "subject_type2",
})
})
-it("deals with unknown subject types", async () => {
- fetch_server_api.fetch_server_api = jest.fn().mockResolvedValue({ ok: true })
- await renderSubjectTitle("unknown_subject_type")
- expect(screen.getAllByText("Unknown subject type").length).toBe(2)
-})
-
it("changes the subject title", async () => {
fetch_server_api.fetch_server_api = jest.fn().mockResolvedValue({ ok: true })
await renderSubjectTitle()
diff --git a/components/frontend/src/subject/SubjectType.js b/components/frontend/src/subject/SubjectType.js
index d471c5bbb0..f04244e9ad 100644
--- a/components/frontend/src/subject/SubjectType.js
+++ b/components/frontend/src/subject/SubjectType.js
@@ -1,11 +1,11 @@
import CircleIcon from "@mui/icons-material/Circle"
-import { Stack, Typography } from "@mui/material"
+import { MenuItem, Stack, Typography } from "@mui/material"
import { func, number, objectOf, string } from "prop-types"
import { useContext } from "react"
import { DataModel } from "../context/DataModel"
-import { EDIT_REPORT_PERMISSION } from "../context/Permissions"
-import { SingleChoiceInput } from "../fields/SingleChoiceInput"
+import { accessGranted, EDIT_REPORT_PERMISSION, Permissions } from "../context/Permissions"
+import { TextField } from "../fields/TextField"
import { subjectPropType } from "../sharedPropTypes"
export function subjectTypes(subjectTypesMapping, level = 0) {
@@ -30,10 +30,10 @@ export function subjectTypes(subjectTypesMapping, level = 0) {
content: (
{bullet}
-
+
{subjectType.name}
{subjectType.description}
-
+
),
})
@@ -47,15 +47,22 @@ subjectTypes.propTypes = {
}
export function SubjectType({ subjectType, setValue }) {
+ const permissions = useContext(Permissions)
+ const disabled = !accessGranted(permissions, [EDIT_REPORT_PERMISSION])
return (
- setValue(value)}
- sort={false}
+ onChange={(value) => setValue(value)}
+ select
value={subjectType}
- />
+ >
+ {subjectTypes(useContext(DataModel).subjects).map((subjectType) => (
+
+ {subjectType.content}
+
+ ))}
+
)
}
SubjectType.propTypes = {
diff --git a/components/frontend/src/subject/SubjectsButtonRow.js b/components/frontend/src/subject/SubjectsButtonRow.js
index 450169b2a7..37fe01c417 100644
--- a/components/frontend/src/subject/SubjectsButtonRow.js
+++ b/components/frontend/src/subject/SubjectsButtonRow.js
@@ -23,7 +23,7 @@ export function SubjectsButtonRow({ reload, report, reports, settings }) {
+
-
-
+
+
+
)
}
return null
diff --git a/components/frontend/src/widgets/Header.js b/components/frontend/src/widgets/Header.js
new file mode 100644
index 0000000000..a48ae577cc
--- /dev/null
+++ b/components/frontend/src/widgets/Header.js
@@ -0,0 +1,18 @@
+import { Typography } from "@mui/material"
+import { element, oneOfType, string } from "prop-types"
+
+export function Header({ header, level, subheader }) {
+ return (
+
+ {header}
+
+ {subheader}
+
+
+ )
+}
+Header.propTypes = {
+ header: oneOfType([element, string]),
+ level: string,
+ subheader: oneOfType([element, string]),
+}
diff --git a/components/frontend/src/widgets/HeaderWithDetails.css b/components/frontend/src/widgets/HeaderWithDetails.css
deleted file mode 100644
index f0f4f35cd9..0000000000
--- a/components/frontend/src/widgets/HeaderWithDetails.css
+++ /dev/null
@@ -1,9 +0,0 @@
-@media print {
- .Caret {
- display: none !important;
- }
-}
-
-div.sticky {
- background-color: white;
-}
diff --git a/components/frontend/src/widgets/HeaderWithDetails.js b/components/frontend/src/widgets/HeaderWithDetails.js
index 5d287a780d..ed0703b358 100644
--- a/components/frontend/src/widgets/HeaderWithDetails.js
+++ b/components/frontend/src/widgets/HeaderWithDetails.js
@@ -1,43 +1,52 @@
-import "./HeaderWithDetails.css"
+import { Accordion, AccordionDetails, AccordionSummary } from "@mui/material"
+import { accordionSummaryClasses } from "@mui/material/AccordionSummary"
+import { string } from "prop-types"
-import { node, object, string } from "prop-types"
-
-import { Header, Segment } from "../semantic_ui_react_wrappers"
import { childrenPropType, settingsPropType } from "../sharedPropTypes"
-import { ExpandButton } from "./buttons/ExpandButton"
+import { Header } from "./Header"
+import { CaretRight } from "./icons"
-export function HeaderWithDetails({ children, className, header, item_uuid, level, style, settings, subheader }) {
- const showDetails = settings.expandedItems.includes(item_uuid)
- const segmentStyle = { paddingLeft: "0px", paddingRight: "0px" }
+export function HeaderWithDetails({ children, header, item_uuid, level, settings, subheader }) {
+ const showDetails = Boolean(settings.expandedItems.includes(item_uuid))
return (
-
- settings.expandedItems.toggle(item_uuid)}
- onKeyPress={(event) => {
- event.preventDefault()
- settings.expandedItems.toggle(item_uuid)
+ settings.expandedItems.toggle(item_uuid)}
+ slotProps={{ transition: { unmountOnExit: true } }} // Make testing for (dis)appearance of contents easier
+ sx={{
+ "&:before": {
+ display: "none", // Remove top border
+ },
+ }}
+ >
+ }
+ id={`accordion-header-${item_uuid}`}
+ sx={{
+ border: "0",
+ flexDirection: "row-reverse",
+ height: "80px",
+ padding: "0px",
+ [`& .${accordionSummaryClasses.expandIconWrapper}.${accordionSummaryClasses.expanded}`]: {
+ transform: "rotate(90deg)",
+ },
+ color: "primary.main",
}}
- style={style}
- tabIndex="0"
>
-
-
- {header}
- {subheader}
-
-
- {showDetails && {children} }
-
+
+
+ {children}
+
)
}
HeaderWithDetails.propTypes = {
children: childrenPropType,
- className: string,
- header: node,
+ header: string,
item_uuid: string,
level: string,
settings: settingsPropType,
- style: object,
subheader: string,
}
diff --git a/components/frontend/src/widgets/HeaderWithDetails.test.js b/components/frontend/src/widgets/HeaderWithDetails.test.js
index 78f8ea0e6e..8cc13e9502 100644
--- a/components/frontend/src/widgets/HeaderWithDetails.test.js
+++ b/components/frontend/src/widgets/HeaderWithDetails.test.js
@@ -11,22 +11,20 @@ beforeEach(() => {
it("expands the details on click", () => {
render(
-
+
Hello
,
)
- expect(screen.queryAllByText("Hello").length).toBe(0)
- fireEvent.click(screen.getByTitle("expand"))
+ fireEvent.click(screen.getByText("Expand"))
expect(history.location.search).toBe("?expanded=uuid")
})
it("expands the details on space", async () => {
render(
-
+
Hello
,
)
- expect(screen.queryAllByText("Hello").length).toBe(0)
await userEvent.tab()
await userEvent.keyboard(" ")
expect(history.location.search).toBe("?expanded=uuid")
@@ -35,7 +33,7 @@ it("expands the details on space", async () => {
it("is expanded on load when listed in the query string", () => {
history.push("?expanded=uuid")
render(
-
+
Hello
,
)
diff --git a/components/frontend/src/widgets/HyperLink.js b/components/frontend/src/widgets/HyperLink.js
index f63eb294de..dc08102421 100644
--- a/components/frontend/src/widgets/HyperLink.js
+++ b/components/frontend/src/widgets/HyperLink.js
@@ -13,6 +13,7 @@ export function HyperLink({ url, children }) {
target="_blank"
title="Opens new window or tab"
underline="always"
+ variant="inherit"
>
{children}
diff --git a/components/frontend/src/widgets/Label.js b/components/frontend/src/widgets/Label.js
new file mode 100644
index 0000000000..554c7e9204
--- /dev/null
+++ b/components/frontend/src/widgets/Label.js
@@ -0,0 +1,28 @@
+import { Box } from "@mui/material"
+import { string } from "prop-types"
+
+import { childrenPropType } from "../sharedPropTypes"
+
+export function Label({ color, children }) {
+ const bgcolor = `${color}.main`
+ const fgcolor = `${color}.contrastText`
+ return (
+
+ {children}
+
+ )
+}
+Label.propTypes = {
+ color: string,
+ children: childrenPropType,
+}
diff --git a/components/frontend/src/widgets/LabelWithDropdown.js b/components/frontend/src/widgets/LabelWithDropdown.js
index 52e16c3d01..04f79d6a92 100644
--- a/components/frontend/src/widgets/LabelWithDropdown.js
+++ b/components/frontend/src/widgets/LabelWithDropdown.js
@@ -1,29 +1,31 @@
+import { MenuItem, Select } from "@mui/material"
import { array, func, string } from "prop-types"
-import { Dropdown } from "../semantic_ui_react_wrappers"
-import { alignmentPropType, labelPropType } from "../sharedPropTypes"
+import { labelPropType } from "../sharedPropTypes"
-export function LabelWithDropdown({ color, direction, label, onChange, options, value }) {
+export function LabelWithDropdown({ label, onChange, options, value }) {
return (
{label}
-
-
-
+ onChange(event.target.value)}
+ value={value}
+ inputProps={{ sx: { paddingBottom: "2px", paddingTop: "2px" } }}
+ sx={{
+ color: options.find((option) => option.value === value).color,
+ marginLeft: "6px",
+ }}
+ >
+ {options.map((option) => (
+
+ {option.text}
+
+ ))}
+
)
}
LabelWithDropdown.propTypes = {
- color: string,
- direction: alignmentPropType,
label: labelPropType,
onChange: func,
options: array,
diff --git a/components/frontend/src/widgets/LabelWithDropdown.test.js b/components/frontend/src/widgets/LabelWithDropdown.test.js
index 6d38d0771b..6929dd4b07 100644
--- a/components/frontend/src/widgets/LabelWithDropdown.test.js
+++ b/components/frontend/src/widgets/LabelWithDropdown.test.js
@@ -1,80 +1,32 @@
-import { fireEvent, render, screen } from "@testing-library/react"
+import { render, screen } from "@testing-library/react"
+import userEvent from "@testing-library/user-event"
import { LabelWithDropdown } from "./LabelWithDropdown"
-it("shows the label", () => {
- render( )
- expect(screen.getByText(/Hello/)).not.toBe(null)
-})
-
-it("can be colored", () => {
- render(
- ,
- )
- expect(screen.getByRole("listbox")).toHaveAttribute("color", "red")
-})
-
-it("has default color black", () => {
- render(
- ,
- )
- expect(screen.getByRole("listbox")).not.toHaveAttribute("color")
-})
-
-it("changes the option", () => {
+function renderLabelWithDropdown() {
const mockCallback = jest.fn()
render(
,
)
- fireEvent.click(screen.getByText(/Option 2/))
- expect(mockCallback).toHaveBeenCalled()
+ return mockCallback
+}
+
+it("shows the label", () => {
+ renderLabelWithDropdown()
+ expect(screen.getByText(/Hello/)).not.toBe(null)
})
-it("opens the dropdown when clicking the current option", () => {
- const mockCallback = jest.fn()
- render(
- ,
- )
- expect(screen.getByRole("listbox")).toHaveAttribute("aria-expanded", "false")
- fireEvent.click(screen.getAllByText(/Option 1/)[0])
- expect(screen.getByRole("listbox")).toHaveAttribute("aria-expanded", "true")
+it("changes the option", async () => {
+ const mockCallback = renderLabelWithDropdown()
+ await userEvent.click(screen.getByText(/Option 1/))
+ await userEvent.click(screen.getByText(/Option 2/))
+ expect(mockCallback).toHaveBeenCalledWith("2")
})
diff --git a/components/frontend/src/widgets/LabelWithHelp.js b/components/frontend/src/widgets/LabelWithHelp.js
index 1d744eb063..48febb721e 100644
--- a/components/frontend/src/widgets/LabelWithHelp.js
+++ b/components/frontend/src/widgets/LabelWithHelp.js
@@ -1,20 +1,16 @@
import HelpIcon from "@mui/icons-material/Help"
-import { bool, string } from "prop-types"
+import { Tooltip } from "@mui/material"
+import { string } from "prop-types"
-import { Popup } from "../semantic_ui_react_wrappers"
import { labelPropType, popupContentPropType } from "../sharedPropTypes"
-export function LabelWithHelp({ labelId, labelFor, label, help, hoverable }) {
+export function LabelWithHelp({ labelId, labelFor, label, help }) {
return (
{label}{" "}
- }
- wide
- />
+
+
+
)
}
@@ -23,5 +19,4 @@ LabelWithHelp.propTypes = {
labelFor: string,
label: labelPropType,
help: popupContentPropType,
- hoverable: bool,
}
diff --git a/components/frontend/src/widgets/LabelWithHyperLink.js b/components/frontend/src/widgets/LabelWithHyperLink.js
index 954149b09f..b0ea07ffeb 100644
--- a/components/frontend/src/widgets/LabelWithHyperLink.js
+++ b/components/frontend/src/widgets/LabelWithHyperLink.js
@@ -9,7 +9,7 @@ export function LabelWithHyperLink({ labelId, label, url }) {
{label}{" "}
-
+
)
diff --git a/components/frontend/src/widgets/ReadTheDocsLink.js b/components/frontend/src/widgets/ReadTheDocsLink.js
index 87b6dde7b6..f0e8ecda5f 100644
--- a/components/frontend/src/widgets/ReadTheDocsLink.js
+++ b/components/frontend/src/widgets/ReadTheDocsLink.js
@@ -1,14 +1,9 @@
-import HelpIcon from "@mui/icons-material/Help"
import { string } from "prop-types"
import { HyperLink } from "./HyperLink"
export function ReadTheDocsLink({ url }) {
- return (
-
- Read the Docs
-
- )
+ return Read the Docs
}
ReadTheDocsLink.propTypes = {
url: string,
diff --git a/components/frontend/src/widgets/TabPane.css b/components/frontend/src/widgets/TabPane.css
deleted file mode 100644
index 5dadaf7bbc..0000000000
--- a/components/frontend/src/widgets/TabPane.css
+++ /dev/null
@@ -1,14 +0,0 @@
-.tabbutton {
- border: none;
- background: none;
- font: inherit;
- padding: 0px;
-}
-
-.tabbutton.inverted {
- color: rgba(255, 255, 255, 0.87);
-}
-
-.tabbutton:focus {
- outline: thin dotted;
-}
diff --git a/components/frontend/src/widgets/TabPane.js b/components/frontend/src/widgets/TabPane.js
deleted file mode 100644
index 5d578bd426..0000000000
--- a/components/frontend/src/widgets/TabPane.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import "./TabPane.css"
-
-import HistoryIcon from "@mui/icons-material/History"
-import SettingsIcon from "@mui/icons-material/Settings"
-import { bool, element, oneOfType, string } from "prop-types"
-import { useContext } from "react"
-import { Menu } from "semantic-ui-react"
-
-import { DarkMode } from "../context/DarkMode"
-import { Label, Tab } from "../semantic_ui_react_wrappers"
-
-function FocusableTab({ error, icon, image, label, warning }) {
- const className = useContext(DarkMode) ? "tabbutton inverted" : "tabbutton"
- let tabLabel = label
- if (error || warning) {
- const color = error ? "red" : "yellow"
- tabLabel = {label}
- }
- return (
- <>
- {icon || image} {tabLabel}
- >
- )
-}
-FocusableTab.propTypes = {
- error: bool,
- icon: element,
- image: element,
- label: oneOfType([element, string]),
- warning: bool,
-}
-
-export function tabPane(label, pane, options) {
- // Return a tab and pane, to be used as follows:
- return {
- menuItem: (
-
-
-
- ),
- render: () => {pane} ,
- }
-}
-
-export function configurationTabPane(pane, options) {
- return tabPane("Configuration", pane, { ...options, icon: })
-}
-
-export function changelogTabPane(pane, options) {
- return tabPane("Changelog", pane, { ...options, icon: })
-}
diff --git a/components/frontend/src/widgets/TabPane.test.js b/components/frontend/src/widgets/TabPane.test.js
deleted file mode 100644
index 8587d2c9bd..0000000000
--- a/components/frontend/src/widgets/TabPane.test.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import StorageIcon from "@mui/icons-material/Storage"
-import { render, screen } from "@testing-library/react"
-
-import { DarkMode } from "../context/DarkMode"
-import { Tab } from "../semantic_ui_react_wrappers"
-import { tabPane } from "./TabPane"
-
-it("shows the tab", () => {
- render( )
- expect(screen.queryAllByText("Tab").length).toBe(1)
-})
-
-it("is inverted in dark mode", () => {
- const { container } = render(
-
-
- ,
- )
- expect(container.firstChild.firstChild.className).toEqual(expect.stringContaining("inverted"))
-})
-
-it("shows the tab red when there is an error", () => {
- render(Pane, { error: true })]} />)
- expect(screen.getByText("Tab").className).toEqual(expect.stringContaining("red"))
-})
-
-it("shows the tab yellow when there is a warning", () => {
- render(Pane, { warning: true })]} />)
- expect(screen.getByText("Tab").className).toEqual(expect.stringContaining("yellow"))
-})
-
-it("shows an icon", () => {
- render(Pane, { icon: })]} />)
- expect(screen.getAllByTestId("StorageIcon").length).toBe(1)
-})
-
-it("shows an image", () => {
- const image =
- const { container } = render(Pane, { image: image })]} />)
- expect(container.firstChild.firstChild.firstChild.firstChild.className).toEqual(expect.stringContaining("image"))
-})
diff --git a/components/frontend/src/widgets/TableHeaderCell.js b/components/frontend/src/widgets/TableHeaderCell.js
index f861f89f97..566a7060bf 100644
--- a/components/frontend/src/widgets/TableHeaderCell.js
+++ b/components/frontend/src/widgets/TableHeaderCell.js
@@ -1,7 +1,6 @@
-import { Tooltip } from "@mui/material"
+import { TableCell, TableSortLabel, Tooltip } from "@mui/material"
import { func, string } from "prop-types"
-import { Table } from "../semantic_ui_react_wrappers"
import {
alignmentPropType,
labelPropType,
@@ -24,6 +23,10 @@ TableHeaderCellContents.propTypes = {
label: labelPropType,
}
+function MuiSortDirection(sortDirection) {
+ return sortDirection === "ascending" ? "asc" : "desc"
+}
+
export function SortableTableHeaderCell({
colSpan,
column,
@@ -34,16 +37,17 @@ export function SortableTableHeaderCell({
textAlign,
help,
}) {
- const sorted = sortColumn.value === column ? sortDirection.value : null
+ const sorted = sortColumn.value === column ? MuiSortDirection(sortDirection.value) : null
return (
- handleSort(column)}
- sorted={sorted}
- textAlign={textAlign || "left"}
- >
-
-
+
+ handleSort(column)}
+ >
+
+
+
)
}
SortableTableHeaderCell.propTypes = {
@@ -59,9 +63,9 @@ SortableTableHeaderCell.propTypes = {
export function UnsortableTableHeaderCell({ help, label, textAlign, width }) {
return (
-
+
-
+
)
}
UnsortableTableHeaderCell.propTypes = {
diff --git a/components/frontend/src/widgets/TableHeaderCell.test.js b/components/frontend/src/widgets/TableHeaderCell.test.js
index 40b05e8df2..72e793885f 100644
--- a/components/frontend/src/widgets/TableHeaderCell.test.js
+++ b/components/frontend/src/widgets/TableHeaderCell.test.js
@@ -1,6 +1,6 @@
+import { Table, TableHead, TableRow } from "@mui/material"
import { render, screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
-import { Table } from "semantic-ui-react"
import { createTestableSettings } from "../__fixtures__/fixtures"
import { SortableTableHeaderCell, UnsortableTableHeaderCell } from "./TableHeaderCell"
@@ -9,16 +9,16 @@ function renderSortableTableHeaderCell(help) {
const settings = createTestableSettings()
render(
,
)
}
@@ -39,11 +39,11 @@ it("shows the help of the sortable header", async () => {
function renderUnsortableTableHeaderCell(help) {
render(
,
)
}
diff --git a/components/frontend/src/widgets/TableRowWithDetails.js b/components/frontend/src/widgets/TableRowWithDetails.js
index c805f59a48..ebbed94fc8 100644
--- a/components/frontend/src/widgets/TableRowWithDetails.js
+++ b/components/frontend/src/widgets/TableRowWithDetails.js
@@ -1,31 +1,42 @@
-import { bool, func, object } from "prop-types"
+import { TableCell, TableRow } from "@mui/material"
+import { bool, func, string } from "prop-types"
-import { Table } from "../semantic_ui_react_wrappers"
import { childrenPropType } from "../sharedPropTypes"
import { ExpandButton } from "./buttons/ExpandButton"
export function TableRowWithDetails(props) {
- const { children, details, expanded, onExpand, style, ...otherProps } = props
+ const { color, children, details, expanded, onExpand, ...otherProps } = props
return (
<>
-
-
+
+
onExpand(!expanded)} size="1.5em" />
-
+
{children}
-
+
{expanded && (
-
- {details}
-
+
+ {details}
+
)}
>
)
}
TableRowWithDetails.propTypes = {
children: childrenPropType,
+ color: string,
details: childrenPropType,
expanded: bool,
onExpand: func,
- style: object,
}
diff --git a/components/frontend/src/widgets/TableRowWithDetails.test.js b/components/frontend/src/widgets/TableRowWithDetails.test.js
index 5bcac28cb3..72da7d5376 100644
--- a/components/frontend/src/widgets/TableRowWithDetails.test.js
+++ b/components/frontend/src/widgets/TableRowWithDetails.test.js
@@ -1,15 +1,15 @@
+import { Table, TableBody } from "@mui/material"
import { fireEvent, render, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
-import { Table } from "semantic-ui-react"
import { TableRowWithDetails } from "./TableRowWithDetails"
function renderTableRowWithDetails(expanded, onExpand) {
render(
,
)
}
diff --git a/components/frontend/src/widgets/Tabs.js b/components/frontend/src/widgets/Tabs.js
new file mode 100644
index 0000000000..c9d7f0f920
--- /dev/null
+++ b/components/frontend/src/widgets/Tabs.js
@@ -0,0 +1,46 @@
+import { Box, Tab, Tabs as MUITabs } from "@mui/material"
+import { arrayOf, object } from "prop-types"
+import { useId, useState } from "react"
+
+import { childrenPropType } from "../sharedPropTypes"
+import { Label } from "./Label"
+
+export function Tabs({ children, tabs }) {
+ const tabsId = useId()
+ const [tabIndex, setTabIndex] = useState(0)
+ return (
+ <>
+
+ setTabIndex(newTabIndex)}>
+ {tabs.map((tab, index) => {
+ let tabLabel = tab.label
+ if (tab.error || tab.warning) {
+ const color = tab.error ? "error" : "warning"
+ tabLabel = {tab.label}
+ }
+ return (
+
+ )
+ })}
+
+
+
+ {children[tabIndex]}
+
+ >
+ )
+}
+Tabs.propTypes = {
+ children: childrenPropType,
+ tabs: arrayOf(object),
+}
diff --git a/components/frontend/src/widgets/WarningMessage.js b/components/frontend/src/widgets/WarningMessage.js
index 86dea71c07..18de1cfe31 100644
--- a/components/frontend/src/widgets/WarningMessage.js
+++ b/components/frontend/src/widgets/WarningMessage.js
@@ -1,21 +1,40 @@
-import { bool } from "prop-types"
+import { Alert, AlertTitle } from "@mui/material"
+import { bool, string } from "prop-types"
-import { Message } from "../semantic_ui_react_wrappers"
+import { childrenPropType } from "../sharedPropTypes"
-export function WarningMessage(props) {
+export function WarningMessage({ children, title, showIf }) {
// Show a warning message if showIf is true or undefined
- const { showIf, ...messageProps } = props
- return (showIf ?? true) ? : null
+ return (showIf ?? true) ? (
+
+ {title}
+ {children}
+
+ ) : null
}
WarningMessage.propTypes = {
+ children: childrenPropType,
showIf: bool,
+ title: string,
}
export function FailedToLoadMeasurementsWarningMessage() {
return (
-
+
+ Loading the measurements from the API-server failed.
+
)
}
+
+export function InfoMessage({ children, title }) {
+ return (
+
+ {title}
+ {children}
+
+ )
+}
+InfoMessage.propTypes = {
+ children: childrenPropType,
+ title: string,
+}
diff --git a/components/frontend/src/widgets/WarningMessage.test.js b/components/frontend/src/widgets/WarningMessage.test.js
index bc859c756f..cfc5fb9ab7 100644
--- a/components/frontend/src/widgets/WarningMessage.test.js
+++ b/components/frontend/src/widgets/WarningMessage.test.js
@@ -3,16 +3,16 @@ import { render, screen } from "@testing-library/react"
import { WarningMessage } from "./WarningMessage"
it("shows a warning message if showIf is true", () => {
- render( )
+ render(Warning )
expect(screen.getAllByText("Warning").length).toBe(1)
})
it("does not show a warning message if showIf is false", () => {
- render( )
+ render(Warning )
expect(screen.queryAllByText("Warning").length).toBe(0)
})
it("shows a warning message if showIf is undefined", () => {
- render( )
+ render(Warning )
expect(screen.getAllByText("Warning").length).toBe(1)
})
diff --git a/components/frontend/src/widgets/icons.js b/components/frontend/src/widgets/icons.js
index 3d5fcb39cf..55fa0fe45f 100644
--- a/components/frontend/src/widgets/icons.js
+++ b/components/frontend/src/widgets/icons.js
@@ -42,7 +42,7 @@ export function DeleteItemIcon() {
}
export function IgnoreIcon() {
- return
+ return
}
export function MoveItemIcon() {
diff --git a/tests/application_tests/src/test_report.py b/tests/application_tests/src/test_report.py
index 35466abc4f..f39aeaba57 100644
--- a/tests/application_tests/src/test_report.py
+++ b/tests/application_tests/src/test_report.py
@@ -45,6 +45,7 @@ class OpenReportTest(unittest.TestCase):
# Class names of MUI-components used in the tests
DASHBOARD_CARD_CLASS_NAME = "MuiCard-root"
DASHBOARD_CARD_HEADER_CONTENT_CLASS_NAME = "MuiCardHeader-content"
+ REPORT_HEADER_CLASS_NAME = "MuiAccordionSummary-content"
def setUp(self):
"""Override to setup the driver."""
@@ -84,9 +85,8 @@ def test_open_report(self):
report = self.dashboard_cards()[-1] # The last card is a report
report_title = report.find_element(By.CLASS_NAME, self.DASHBOARD_CARD_HEADER_CONTENT_CLASS_NAME)
report.click()
- self.assertTrue(
- expect.text_to_be_present_in_element(self.driver.find_element(By.CLASS_NAME, "header"), report_title)
- )
+ report_header = self.driver.find_element(By.CLASS_NAME, self.REPORT_HEADER_CLASS_NAME)
+ self.assertTrue(expect.text_to_be_present_in_element(report_header, report_title))
def test_login_and_logout(self):
"""Test that a user can login and logout."""