+ No issue tracker configured
+
+ Please configure an issue tracker by expanding the report title, selecting the ‘Issue
+ tracker’ tab, and configuring an issue tracker.
+
+ >
}
- header={"No issue tracker configured"}
- trigger={}
- />
+ >
+
+
+
+
)
}
IssueWithoutTracker.propTypes = {
@@ -160,7 +169,18 @@ function IssueWithTracker({ issueStatus, settings }) {
popupContent = issuePopupContent(issueStatus)
}
if (popupContent) {
- label =
+ label = (
+
+ {popupHeader}
+ {popupContent}
+ >
+ }
+ >
+ {label}
+
+ )
}
return label
}
diff --git a/components/frontend/src/measurement/MeasurementTarget.js b/components/frontend/src/measurement/MeasurementTarget.js
index 134e21d7dc..160cda74b2 100644
--- a/components/frontend/src/measurement/MeasurementTarget.js
+++ b/components/frontend/src/measurement/MeasurementTarget.js
@@ -1,7 +1,8 @@
+import { Tooltip } from "@mui/material"
import { useContext } from "react"
import { DataModel } from "../context/DataModel"
-import { Label, Popup } from "../semantic_ui_react_wrappers"
+import { Label } from "../semantic_ui_react_wrappers"
import { metricPropType } from "../sharedPropTypes"
import {
formatMetricDirection,
@@ -59,11 +60,11 @@ export function MeasurementTarget({ metric }) {
const today = new Date()
debtEndDateInThePast = endDate.toISOString().split("T")[0] < today.toISOString().split("T")[0]
}
- const label = allIssuesDone || debtEndDateInThePast ? : {target}
+ const label = allIssuesDone || debtEndDateInThePast ? : target
return (
-
- {popupText(metric, debtEndDateInThePast, allIssuesDone, dataModel)}
-
+ {popupText(metric, debtEndDateInThePast, allIssuesDone, dataModel)}}>
+ {label}
+
)
}
MeasurementTarget.propTypes = {
diff --git a/components/frontend/src/measurement/MeasurementValue.js b/components/frontend/src/measurement/MeasurementValue.js
index f9d7e58c63..97471816dc 100644
--- a/components/frontend/src/measurement/MeasurementValue.js
+++ b/components/frontend/src/measurement/MeasurementValue.js
@@ -1,10 +1,11 @@
import "./MeasurementValue.css"
+import { Alert, Tooltip, Typography } from "@mui/material"
import { bool, string } from "prop-types"
import { useContext } from "react"
import { DataModel } from "../context/DataModel"
-import { Label, Message, Popup } from "../semantic_ui_react_wrappers"
+import { Label } from "../semantic_ui_react_wrappers"
import { datePropType, measurementPropType, metricPropType } from "../sharedPropTypes"
import { IGNORABLE_SOURCE_ENTITY_STATUSES, SOURCE_ENTITY_STATUS_NAME } from "../source/source_entity_status"
import {
@@ -30,14 +31,20 @@ function measurementValueLabel(hasIgnoredEntities, stale, updating, value) {
value
)
if (stale) {
- return
+ return (
+
+
+
+ )
}
if (updating) {
return (
-
+
+
+
)
}
return {measurementValue}
@@ -101,49 +108,43 @@ export function MeasurementValue({ metric, reportDate }) {
const requested = isMeasurementRequested(metric)
const hasIgnoredEntities = sum(ignoredEntitiesCount(metric.latest_measurement)) > 0
return (
-
+
+ This may indicate a problem with Quality-time itself. Please contact a system administrator.
+
+
+ The source configuration of this metric was changed after the latest measurement.
+
+
+ An update of the latest measurement was requested by a user.
+
+ {hasIgnoredEntities && (
+
+
+ {`Ignored ${unit}`}
+
+ {ignoredEntitiesMessage(metric.latest_measurement, unit)}
+
+ )}
+ {metric.latest_measurement && (
+ <>
+
+ {metric.status ? "The metric was last measured" : "Last measurement attempt"}
+
+
+
+ {metric.status ? "The current value was first measured" : "The value is unknown since"}
+
+ >
+ )}
+
+ }
>
-
-
-
- {hasIgnoredEntities && (
-
- {`Ignored ${unit}`}
-
- }
- content={ignoredEntitiesMessage(metric.latest_measurement, unit)}
- />
- )}
- {metric.latest_measurement && (
- <>
-
- {metric.status ? "The metric was last measured" : "Last measurement attempt"}
-
-
-
- {metric.status ? "The current value was first measured" : "The value is unknown since"}
-
- >
- )}
-
+ {measurementValueLabel(hasIgnoredEntities, stale, outdated || requested, value)}
+
)
}
MeasurementValue.propTypes = {
diff --git a/components/frontend/src/measurement/Overrun.js b/components/frontend/src/measurement/Overrun.js
index 4f29c6016d..a7d46fe9d7 100644
--- a/components/frontend/src/measurement/Overrun.js
+++ b/components/frontend/src/measurement/Overrun.js
@@ -1,8 +1,18 @@
+import {
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Tooltip,
+ Typography,
+} from "@mui/material"
import { string } from "prop-types"
import { useContext } from "react"
import { DataModel } from "../context/DataModel"
-import { Header, Popup, Table } from "../semantic_ui_react_wrappers"
import { datesPropType, measurementsPropType, metricPropType, reportPropType } from "../sharedPropTypes"
import { getMetricResponseOverrun, pluralize } from "../utils"
import { StatusIcon } from "./StatusIcon"
@@ -23,59 +33,60 @@ export function Overrun({ metric_uuid, metric, report, measurements, dates }) {
const period = `${sortedDates.at(0).toLocaleDateString()} - ${sortedDates.at(-1).toLocaleDateString()}`
const content = (
<>
-
-
- Metric reaction time overruns
- In the period {period}
-
-
-
-
-
-
- When did the metric need action?
-
-
- How long did it take to react?
-
-
-
- Status
- Start
- End
- Actual
- Desired
- Overrun
-
-
-
- {overruns.map((overrun) => (
-
-
-
-
- {overrun.start.split("T")[0]}
- {overrun.end.split("T")[0]}
- {formatDays(overrun.actual_response_time)}
- {formatDays(overrun.desired_response_time)}
- {formatDays(overrun.overrun)}
-
- ))}
-
-
-
-
- Total
-
-
- {triggerText}
-
-
-
-
+ Metric reaction time overruns in the period {period}
+
+
+
+
+
+ When did the metric need action?
+
+
+ How long did it take to react?
+
+
+
+ Status
+ Start
+ End
+ Actual
+ Desired
+ Overrun
+
+
+
+ {overruns.map((overrun) => (
+
+
+
+
+ {overrun.start.split("T")[0]}
+ {overrun.end.split("T")[0]}
+ {formatDays(overrun.actual_response_time)}
+ {formatDays(overrun.desired_response_time)}
+ {formatDays(overrun.overrun)}
+
+ ))}
+
+
+
+
+ Total
+
+
+ {triggerText}
+
+
+
+
+
>
)
- return
+ return (
+
+ {trigger}
+
+ )
}
Overrun.propTypes = {
dates: datesPropType,
diff --git a/components/frontend/src/measurement/SourceStatus.js b/components/frontend/src/measurement/SourceStatus.js
index 48ac30f5a4..178375c5ce 100644
--- a/components/frontend/src/measurement/SourceStatus.js
+++ b/components/frontend/src/measurement/SourceStatus.js
@@ -1,10 +1,11 @@
+import { Tooltip } from "@mui/material"
import { useContext } from "react"
import { DataModel } from "../context/DataModel"
-import { Label, Popup } from "../semantic_ui_react_wrappers"
import { measurementSourcePropType, metricPropType } from "../sharedPropTypes"
import { getMetricName, getSourceName } from "../utils"
import { HyperLink } from "../widgets/HyperLink"
+import { Label } from "../widgets/Label"
export function SourceStatus({ metric, measurement_source }) {
const dataModel = useContext(DataModel)
@@ -36,13 +37,18 @@ export function SourceStatus({ metric, measurement_source }) {
header = "Parse error"
}
return (
- {source_label()}}
- />
+
+ {header}
+ {content}
+ >
+ }
+ >
+
+
+
+
)
} else {
return source_label()
diff --git a/components/frontend/src/measurement/TimeLeft.js b/components/frontend/src/measurement/TimeLeft.js
index d89e8ae5d4..e60a565a23 100644
--- a/components/frontend/src/measurement/TimeLeft.js
+++ b/components/frontend/src/measurement/TimeLeft.js
@@ -1,6 +1,8 @@
-import { Label, Popup } from "../semantic_ui_react_wrappers"
+import { Tooltip } from "@mui/material"
+
import { metricPropType, reportPropType } from "../sharedPropTypes"
import { days, getMetricResponseDeadline, getMetricResponseTimeLeft, pluralize } from "../utils"
+import { Label } from "../widgets/Label"
import { TimeAgoWithDate } from "../widgets/TimeAgoWithDate"
export function TimeLeft({ metric, report }) {
@@ -12,15 +14,15 @@ export function TimeLeft({ metric, report }) {
const daysLeft = days(Math.max(0, timeLeft))
const triggerText = `${daysLeft} ${pluralize("day", daysLeft)}`
let deadlineLabel = "Deadline to address this metric was"
- let trigger =
+ let trigger =
if (timeLeft >= 0) {
deadlineLabel = "Time left to address this metric is"
- trigger = {triggerText}
+ trigger = triggerText
}
return (
-
- {deadlineLabel}.
-
+ {deadlineLabel}}>
+ {trigger}
+
)
}
TimeLeft.propTypes = {
diff --git a/components/frontend/src/metric/MetricConfigurationParameters.js b/components/frontend/src/metric/MetricConfigurationParameters.js
index abbdb6ac36..ba4dcb3099 100644
--- a/components/frontend/src/metric/MetricConfigurationParameters.js
+++ b/components/frontend/src/metric/MetricConfigurationParameters.js
@@ -228,7 +228,6 @@ export function MetricConfigurationParameters({ metric, metric_uuid, reload, rep
{label + defaultTargetLabel}{" "}
- }
- flowing
- header="How measurement values are evaluated"
- hoverable
- on={["hover", "focus"]}
- position={position}
- trigger={}
- />
+
+ How measurement values are evaluated
+
+ >
+ }
+ >
+
+
)
}
TargetLabel.propTypes = {
label: labelPropType,
metric: metricPropType,
- position: string,
targetType: string,
}
-export function Target({ label, labelPosition, metric, metric_uuid, reload, target_type }) {
+export function Target({ label, metric, metric_uuid, reload, target_type }) {
const dataModel = useContext(DataModel)
const metricScale = getMetricScale(metric, dataModel)
const metricDirectionPrefix = formatMetricDirection(metric, dataModel)
const targetValue = metric[target_type]
const unit = formatMetricScaleAndUnit(metric, dataModel)
- const targetLabel =
+ const targetLabel =
if (metricScale === "version_number") {
return (
{
it("shows help", async () => {
renderMetricTarget({ type: "violations", target: "10", near_target: "15" })
- await userEvent.tab()
+ await userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expect(screen.queryAllByText(/How measurement values are evaluated/).length).toBe(1)
})
@@ -100,7 +100,7 @@ function expectNotVisible(...matchers) {
it("shows help for evaluated metric without tech debt", async () => {
renderMetricTarget({ type: "violations", target: "10", near_target: "15" })
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(
/Target met/,
@@ -122,7 +122,7 @@ it("shows help for evaluated metric with tech debt", async () => {
near_target: "20",
accept_debt: true,
})
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(
/Target met/,
@@ -139,7 +139,7 @@ it("shows help for evaluated metric with tech debt", async () => {
it("shows help for evaluated metric with tech debt if debt target is missing", async () => {
renderMetricTarget({ type: "violations", target: "10", near_target: "20", accept_debt: true })
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(
/Target met/,
@@ -162,7 +162,7 @@ it("shows help for evaluated metric with tech debt with end date", async () => {
accept_debt: true,
debt_end_date: "3000-01-01",
})
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(
/Target met/,
@@ -186,7 +186,7 @@ it("shows help for evaluated metric with tech debt with end date in the past", a
accept_debt: true,
debt_end_date: "2000-01-01",
})
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(
/Target met/,
@@ -208,7 +208,7 @@ it("shows help for evaluated metric with tech debt completely overlapping near t
near_target: "20",
accept_debt: true,
})
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(
/Target met/,
@@ -224,7 +224,7 @@ it("shows help for evaluated metric with tech debt completely overlapping near t
it("shows help for evaluated metric without tech debt and target completely overlapping near target", async () => {
renderMetricTarget({ type: "violations", target: "10", near_target: "10" })
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(/Target met/, /≦ 10 violations/, /Target not met/, /> 10 violations/)
expectNotVisible(/Debt target met/, /Near target met/)
@@ -233,7 +233,7 @@ it("shows help for evaluated metric without tech debt and target completely over
it("shows help for evaluated more-is-better metric without tech debt", async () => {
renderMetricTarget({ type: "violations", target: "15", near_target: "10", direction: ">" })
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(
/Target not met/,
@@ -256,7 +256,7 @@ it("shows help for evaluated more-is-better metric with tech debt", async () =>
accept_debt: true,
direction: ">",
})
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(
/Target not met/,
@@ -279,7 +279,7 @@ it("shows help for evaluated more-is-better metric with tech debt and missing de
accept_debt: true,
direction: ">",
})
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(
/Target not met/,
@@ -302,7 +302,7 @@ it("shows help for evaluated more-is-better metric with tech debt completely ove
accept_debt: true,
direction: ">",
})
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(
/Target not met/,
@@ -318,7 +318,7 @@ it("shows help for evaluated more-is-better metric with tech debt completely ove
it("shows help for evaluated more-is-better metric without tech debt and target completely overlapping near target", async () => {
renderMetricTarget({ type: "violations", target: "15", near_target: "15", direction: ">" })
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(/Target not met/, /< 15 violations/, /Target met/, /≧ 15 violations/)
expectNotVisible(/Near target met/, /Debt target met/)
@@ -327,7 +327,7 @@ it("shows help for evaluated more-is-better metric without tech debt and target
it("shows help for evaluated metric without tech debt and zero target completely overlapping near target", async () => {
renderMetricTarget({ type: "violations", target: "0", near_target: "0", direction: ">" })
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(/Target met/, /≧ 0 violations/)
expectNotVisible(/Debt target met/, /Near target met/, /Target not met/)
@@ -336,7 +336,7 @@ it("shows help for evaluated metric without tech debt and zero target completely
it("shows help for informative metric", async () => {
renderMetricTarget({ type: "violations", evaluate_targets: false })
- await userEvent.tab()
+ userEvent.hover(screen.getByTestId("HelpIcon"))
await waitFor(() => {
expectVisible(/Informative/, /violations are not evaluated/)
expectNotVisible(/Target met/, /Debt target met/, /Near target met/, /Target not met/)
diff --git a/components/frontend/src/metric/TrendGraph.js b/components/frontend/src/metric/TrendGraph.js
index ca509a5201..6b804d0966 100644
--- a/components/frontend/src/metric/TrendGraph.js
+++ b/components/frontend/src/metric/TrendGraph.js
@@ -1,5 +1,4 @@
import { useContext } from "react"
-import { Message } from "semantic-ui-react"
import { VictoryAxis, VictoryChart, VictoryLabel, VictoryLine, VictoryTheme } from "victory"
import { DarkMode } from "../context/DarkMode"
@@ -7,7 +6,7 @@ import { DataModel } from "../context/DataModel"
import { loadingPropType, measurementsPropType, metricPropType } from "../sharedPropTypes"
import { capitalize, formatMetricScaleAndUnit, getMetricName, getMetricScale, niceNumber, scaledNumber } from "../utils"
import { LoadingPlaceHolder } from "../widgets/Placeholder"
-import { FailedToLoadMeasurementsWarningMessage, WarningMessage } from "../widgets/WarningMessage"
+import { FailedToLoadMeasurementsWarningMessage, InfoMessage, WarningMessage } from "../widgets/WarningMessage"
function measurementAttributeAsNumber(metric, measurement, field, dataModel) {
const scale = getMetricScale(metric, dataModel)
@@ -22,10 +21,9 @@ export function TrendGraph({ metric, measurements, loading }) {
const estimatedTotalChartHeight = chartHeight + 200 // Estimate of the height including title and axis
if (getMetricScale(metric, dataModel) === "version_number") {
return (
-
+
+ Trend graphs are not supported for metrics with a version number scale.
+
)
}
if (loading === "failed") {
@@ -36,10 +34,9 @@ export function TrendGraph({ metric, measurements, loading }) {
}
if (measurements.length === 0) {
return (
-
+
+ A trend graph can not be displayed until this metric has measurements.
+
)
}
const metricName = getMetricName(metric, dataModel)
diff --git a/components/frontend/src/notification/NotificationDestinations.js b/components/frontend/src/notification/NotificationDestinations.js
index 28ce09fc8e..2719af2d1e 100644
--- a/components/frontend/src/notification/NotificationDestinations.js
+++ b/components/frontend/src/notification/NotificationDestinations.js
@@ -9,13 +9,13 @@ import {
} from "../api/notification"
import { EDIT_REPORT_PERMISSION, ReadOnlyOrEditable } from "../context/Permissions"
import { StringInput } from "../fields/StringInput"
-import { Message } from "../semantic_ui_react_wrappers"
import { destinationPropType } from "../sharedPropTypes"
import { ButtonRow } from "../widgets/ButtonRow"
import { AddButton } from "../widgets/buttons/AddButton"
import { DeleteButton } from "../widgets/buttons/DeleteButton"
import { HyperLink } from "../widgets/HyperLink"
import { LabelWithHelp } from "../widgets/LabelWithHelp"
+import { InfoMessage } from "../widgets/WarningMessage"
function NotificationDestination({ destination, destination_uuid, reload, report_uuid }) {
const help_url =
@@ -48,7 +48,6 @@ function NotificationDestination({ destination, destination_uuid, reload, report
Paste a {teams_hyperlink} webhook URL here.>}
- hoverable
/>
}
placeholder="https://example.webhook.office.com/webhook..."
@@ -104,10 +103,9 @@ export function NotificationDestinations({ destinations, reload, report_uuid })
return (
<>
{notification_destinations.length === 0 ? (
-
- No notification destinations
- No notification destinations have been configured yet.
-
+
+ No notification destinations have been configured yet.
+
) : (
notification_destinations
)}
diff --git a/components/frontend/src/report/IssueTracker.js b/components/frontend/src/report/IssueTracker.js
index 6c8a8c9169..1285521169 100644
--- a/components/frontend/src/report/IssueTracker.js
+++ b/components/frontend/src/report/IssueTracker.js
@@ -227,9 +227,10 @@ export function IssueTracker({ report, reload }) {
/>
+ title="Epic links not supported"
+ >
+ {`The issue type '${issue_type}' in project '${project_key}' does not support adding epic links when creating issues, so no epic link will be added to new issues.`}
+
+ title="Labels not supported"
+ >
+ {`The issue type '${issue_type}' in project '${project_key}' does not support adding labels when creating issues, so no labels will be added to new issues.`}
+
diff --git a/components/frontend/src/report/Report.js b/components/frontend/src/report/Report.js
index 05db2beb3d..7b15cd948b 100644
--- a/components/frontend/src/report/Report.js
+++ b/components/frontend/src/report/Report.js
@@ -15,8 +15,8 @@ import { Subjects } from "../subject/Subjects"
import { SubjectsButtonRow } from "../subject/SubjectsButtonRow"
import { getReportTags } from "../utils"
import { CommentSegment } from "../widgets/CommentSegment"
+import { WarningMessage } from "../widgets/WarningMessage"
import { ReportDashboard } from "./ReportDashboard"
-import { ReportErrorMessage } from "./ReportErrorMessage"
import { ReportTitle } from "./ReportTitle"
export function Report({
@@ -42,7 +42,11 @@ export function Report({
}
if (!report) {
- return
+ return (
+
+ {report_date ? `Sorry, this report didn't exist at ${report_date}` : "Sorry, this report doesn't exist"}
+
+ )
}
// Sort measurements in reverse order so that if there multiple measurements on a day, we find the most recent one:
const reversedMeasurements = measurements.slice().sort((m1, m2) => (m1.start < m2.start ? 1 : -1))
diff --git a/components/frontend/src/report/ReportErrorMessage.js b/components/frontend/src/report/ReportErrorMessage.js
deleted file mode 100644
index fdc4373277..0000000000
--- a/components/frontend/src/report/ReportErrorMessage.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { string } from "prop-types"
-
-import { Message } from "../semantic_ui_react_wrappers"
-import { datePropType, optionalDatePropType } from "../sharedPropTypes"
-
-function ErrorMessage({ children }) {
- return (
-
- {children}
-
- )
-}
-ErrorMessage.propTypes = {
- children: string,
-}
-
-export function ReportErrorMessage({ reportDate }) {
- return (
-
- {reportDate ? `Sorry, this report didn't exist at ${reportDate}` : "Sorry, this report doesn't exist"}
-
- )
-}
-ReportErrorMessage.propTypes = {
- reportDate: optionalDatePropType,
-}
-
-export function ReportsOverviewErrorMessage({ reportDate }) {
- return {`Sorry, no reports existed at ${reportDate}`}
-}
-ReportsOverviewErrorMessage.propTypes = {
- reportDate: datePropType,
-}
diff --git a/components/frontend/src/report/ReportTitle.test.js b/components/frontend/src/report/ReportTitle.test.js
index 64494f9548..84197a402c 100644
--- a/components/frontend/src/report/ReportTitle.test.js
+++ b/components/frontend/src/report/ReportTitle.test.js
@@ -84,7 +84,7 @@ it("sets the unknown status reaction time", async () => {
await act(async () => {
fireEvent.click(screen.getByText(/reaction times/))
})
- await userEvent.type(screen.getByLabelText(/Unknown/), "4{Enter}}", {
+ await userEvent.type(screen.getByLabelText("Unknown"), "4{Enter}}", {
initialSelectionStart: 0,
initialSelectionEnd: 1,
})
@@ -102,7 +102,7 @@ it("sets the target not met status reaction time", async () => {
await act(async () => {
fireEvent.click(screen.getByText(/reaction times/))
})
- await userEvent.type(screen.getByLabelText(/Target not met/), "5{Enter}}", {
+ await userEvent.type(screen.getByLabelText("Target not met"), "5{Enter}}", {
initialSelectionStart: 0,
initialSelectionEnd: 1,
})
@@ -120,7 +120,7 @@ it("sets the near target met status reaction time", async () => {
await act(async () => {
fireEvent.click(screen.getByText(/reaction times/))
})
- await userEvent.type(screen.getByLabelText(/Near target met/), "6{Enter}}", {
+ await userEvent.type(screen.getByLabelText("Near target met"), "6{Enter}}", {
initialSelectionStart: 0,
initialSelectionEnd: 2,
})
@@ -156,7 +156,7 @@ it("sets the confirmed measurement entity status reaction time", async () => {
await act(async () => {
fireEvent.click(screen.getByText(/reaction times/))
})
- await userEvent.type(screen.getByLabelText(/Confirmed/), "60{Enter}}", {
+ await userEvent.type(screen.getByLabelText("Confirmed"), "60{Enter}}", {
initialSelectionStart: 0,
initialSelectionEnd: 3,
})
@@ -174,7 +174,7 @@ it("sets the false positive measurement entity status reaction time", async () =
await act(async () => {
fireEvent.click(screen.getByText(/reaction times/))
})
- await userEvent.type(screen.getByLabelText(/False positive/), "70{Enter}}", {
+ await userEvent.type(screen.getByLabelText("False positive"), "70{Enter}}", {
initialSelectionStart: 0,
initialSelectionEnd: 3,
})
@@ -192,7 +192,7 @@ it("sets the fixed measurement entity status reaction time", async () => {
await act(async () => {
fireEvent.click(screen.getByText(/reaction times/))
})
- await userEvent.type(screen.getByLabelText(/Fixed/), "80{Enter}}", {
+ await userEvent.type(screen.getByLabelText("Fixed"), "80{Enter}}", {
initialSelectionStart: 0,
initialSelectionEnd: 3,
})
@@ -210,7 +210,7 @@ it("sets the won't fixed measurement entity status reaction time", async () => {
await act(async () => {
fireEvent.click(screen.getByText(/reaction times/))
})
- await userEvent.type(screen.getByLabelText(/Won't fix/), "90{Enter}}", {
+ await userEvent.type(screen.getByLabelText("Won't fix"), "90{Enter}}", {
initialSelectionStart: 0,
initialSelectionEnd: 3,
})
diff --git a/components/frontend/src/report/ReportsOverview.js b/components/frontend/src/report/ReportsOverview.js
index ff25b1ef57..ea96f05aa1 100644
--- a/components/frontend/src/report/ReportsOverview.js
+++ b/components/frontend/src/report/ReportsOverview.js
@@ -21,7 +21,7 @@ import { AddButton } from "../widgets/buttons/AddButton"
import { CopyButton } from "../widgets/buttons/CopyButton"
import { CommentSegment } from "../widgets/CommentSegment"
import { report_options } from "../widgets/menu_options"
-import { ReportsOverviewErrorMessage } from "./ReportErrorMessage"
+import { WarningMessage } from "../widgets/WarningMessage"
import { ReportsOverviewDashboard } from "./ReportsOverviewDashboard"
import { ReportsOverviewTitle } from "./ReportsOverviewTitle"
@@ -63,7 +63,7 @@ export function ReportsOverview({
settings,
}) {
if (reports.length === 0 && report_date !== null) {
- return
+ return {`Sorry, no reports existed at ${report_date}`}
}
// Sort measurements in reverse order so that if there multiple measurements on a day, we find the most recent one:
const reversedMeasurements = measurements.slice().sort((m1, m2) => (m1.start < m2.start ? 1 : -1))
diff --git a/components/frontend/src/semantic_ui_react_wrappers.js b/components/frontend/src/semantic_ui_react_wrappers.js
index 53c7959114..cd42422e4e 100644
--- a/components/frontend/src/semantic_ui_react_wrappers.js
+++ b/components/frontend/src/semantic_ui_react_wrappers.js
@@ -3,8 +3,6 @@ export { Dropdown } from "./semantic_ui_react_wrappers/Dropdown"
export { Form } from "./semantic_ui_react_wrappers/Form"
export { Header } from "./semantic_ui_react_wrappers/Header"
export { Label } from "./semantic_ui_react_wrappers/Label"
-export { Message } from "./semantic_ui_react_wrappers/Message"
-export { Popup } from "./semantic_ui_react_wrappers/Popup"
export { Segment } from "./semantic_ui_react_wrappers/Segment"
export { Tab } from "./semantic_ui_react_wrappers/Tab"
export { Table } from "./semantic_ui_react_wrappers/Table"
diff --git a/components/frontend/src/semantic_ui_react_wrappers/Message.js b/components/frontend/src/semantic_ui_react_wrappers/Message.js
deleted file mode 100644
index dfdad0232a..0000000000
--- a/components/frontend/src/semantic_ui_react_wrappers/Message.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { useContext } from "react"
-import { Message as SemanticUIMessage } from "semantic-ui-react"
-
-import { DarkMode } from "../context/DarkMode"
-import { addInvertedClassNameWhenInDarkMode } from "./dark_mode"
-
-export function Message(props) {
- return
-}
-
-Message.Content = SemanticUIMessage.Content
-Message.Header = SemanticUIMessage.Header
-Message.Item = SemanticUIMessage.Item
-Message.List = SemanticUIMessage.List
diff --git a/components/frontend/src/semantic_ui_react_wrappers/Popup.css b/components/frontend/src/semantic_ui_react_wrappers/Popup.css
deleted file mode 100644
index 750287c6ec..0000000000
--- a/components/frontend/src/semantic_ui_react_wrappers/Popup.css
+++ /dev/null
@@ -1,14 +0,0 @@
-.ui.inverted.popup {
- background-color: rgba(60, 65, 70);
- box-shadow:
- 0 2px 4px 0 rgba(255, 255, 255, 0.1),
- 0 2px 8px 0 rgba(255, 255, 255, 0.15);
-}
-
-.ui.inverted.popup .negative.message .header {
- color: #912d2b; /* For some reason the header color is white within an inverted popup. Override. */
-}
-
-.ui.inverted.popup:before {
- background-color: rgba(60, 65, 70) !important;
-}
diff --git a/components/frontend/src/semantic_ui_react_wrappers/Popup.js b/components/frontend/src/semantic_ui_react_wrappers/Popup.js
deleted file mode 100644
index e5cdcacbbc..0000000000
--- a/components/frontend/src/semantic_ui_react_wrappers/Popup.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import "./Popup.css"
-
-import { useContext } from "react"
-import { Popup as SemanticUIPopup } from "semantic-ui-react"
-
-import { DarkMode } from "../context/DarkMode"
-
-export function Popup(props) {
- return
-}
-
-Popup.Content = SemanticUIPopup.Content
diff --git a/components/frontend/src/source/SourceEntities.js b/components/frontend/src/source/SourceEntities.js
index 15b44f2447..000e99464f 100644
--- a/components/frontend/src/source/SourceEntities.js
+++ b/components/frontend/src/source/SourceEntities.js
@@ -4,10 +4,9 @@ import HelpIcon from "@mui/icons-material/Help"
import { IconButton, Tooltip } from "@mui/material"
import { bool, func, object, string } from "prop-types"
import { useContext, useState } from "react"
-import { Message } from "semantic-ui-react"
import { DataModel } from "../context/DataModel"
-import { Popup, Table } from "../semantic_ui_react_wrappers"
+import { Table } from "../semantic_ui_react_wrappers"
import {
alignmentPropType,
childrenPropType,
@@ -25,7 +24,7 @@ import {
import { capitalize } from "../utils"
import { IgnoreIcon, ShowIcon } from "../widgets/icons"
import { LoadingPlaceHolder } from "../widgets/Placeholder"
-import { FailedToLoadMeasurementsWarningMessage } from "../widgets/WarningMessage"
+import { FailedToLoadMeasurementsWarningMessage, InfoMessage } from "../widgets/WarningMessage"
import { SourceEntity } from "./SourceEntity"
function entityStatus(source, entity) {
@@ -144,16 +143,12 @@ function EntityAttributeHeaderCell({ entityAttribute, ...sortProps }) {
>
{entityAttribute.name}
{entityAttribute.help ? (
-
-
-
-
- }
- content={entityAttribute.help}
- />
+
+
+
+
+
+
) : null}
)
@@ -270,10 +265,9 @@ export function SourceEntities({ loading, measurements, metric, metric_uuid, rel
const unit = dataModel.metrics[metric.type].unit || "entities"
const sourceTypeName = dataModel.sources[sourceType].name
return (
-
+
+ {`Showing individual ${unit} is not supported when using ${sourceTypeName} as source.`}
+
)
}
if (loading === "failed") {
@@ -284,20 +278,18 @@ export function SourceEntities({ loading, measurements, metric, metric_uuid, rel
}
if (measurements.length === 0) {
return (
-
+
+ Measurement details not available because Quality-time has not collected any measurements yet.
+
)
}
const lastMeasurement = measurements[measurements.length - 1]
const source = lastMeasurement.sources.find((source) => source.source_uuid === source_uuid)
if (!Array.isArray(source.entities) || source.entities.length === 0) {
return (
-
+
+ There are currently no measurement details available.
+
)
}
const entityAttributes = metricEntities.attributes.filter((attribute) => attribute?.visible ?? true)
diff --git a/components/frontend/src/source/SourceEntities.test.js b/components/frontend/src/source/SourceEntities.test.js
index 98cf824b89..844d0b14fb 100644
--- a/components/frontend/src/source/SourceEntities.test.js
+++ b/components/frontend/src/source/SourceEntities.test.js
@@ -131,7 +131,7 @@ it("renders a message if the metric does not support measurement entities", () =
).toBe(1)
})
-it("renders a message if the metric does not support measurement entities andhas no unit", () => {
+it("renders a message if the metric does not support measurement entities and has no unit", () => {
renderSourceEntities({
metric: {
type: "metric_type_without_unit",
diff --git a/components/frontend/src/source/Sources.js b/components/frontend/src/source/Sources.js
index 15a6a3b9a3..4e3064e848 100644
--- a/components/frontend/src/source/Sources.js
+++ b/components/frontend/src/source/Sources.js
@@ -4,7 +4,7 @@ import { useContext } from "react"
import { add_source, copy_source, move_source } from "../api/source"
import { DataModel } from "../context/DataModel"
import { EDIT_REPORT_PERMISSION, ReadOnlyOrEditable } from "../context/Permissions"
-import { Message, Segment } from "../semantic_ui_react_wrappers"
+import { Segment } from "../semantic_ui_react_wrappers"
import {
measurementPropType,
measurementSourcePropType,
@@ -20,6 +20,7 @@ import { CopyButton } from "../widgets/buttons/CopyButton"
import { MoveButton } from "../widgets/buttons/MoveButton"
import { source_options } from "../widgets/menu_options"
import { showMessage } from "../widgets/toast"
+import { InfoMessage } from "../widgets/WarningMessage"
import { Source } from "./Source"
import { sourceTypeOptions } from "./SourceType"
@@ -118,10 +119,7 @@ export function Sources({ reports, report, metric, metric_uuid, measurement, cha
return (
<>
{sourceSegments.length === 0 ? (
-
- No sources
- No sources have been configured yet.
-
+ No sources have been configured yet.
) : (
sourceSegments
)}
diff --git a/components/frontend/src/subject/SubjectTableRow.js b/components/frontend/src/subject/SubjectTableRow.js
index d338f4a430..0b22e8b2f0 100644
--- a/components/frontend/src/subject/SubjectTableRow.js
+++ b/components/frontend/src/subject/SubjectTableRow.js
@@ -1,3 +1,4 @@
+import { Tooltip } from "@mui/material"
import { bool, func, number, object, string } from "prop-types"
import { useContext } from "react"
@@ -12,7 +13,7 @@ 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 { Label, Table } from "../semantic_ui_react_wrappers"
import {
dataModelPropType,
datePropType,
@@ -131,14 +132,13 @@ function DeltaCell({ dateOrderAscending, index, metric, metricValue, previousVal
const description = deltaDescription(dataModel, metric, scale, delta, improved, oldValue, newValue)
const color = deltaColor(metric, improved)
label = (
-
+
+
+
- }
- />
+
+
)
}
return (
diff --git a/components/frontend/src/widgets/Label.js b/components/frontend/src/widgets/Label.js
new file mode 100644
index 0000000000..4cdf724227
--- /dev/null
+++ b/components/frontend/src/widgets/Label.js
@@ -0,0 +1,23 @@
+import { Box } from "@mui/material"
+import { bool } from "prop-types"
+
+import { childrenPropType } from "../sharedPropTypes"
+
+export function Label({ error, children }) {
+ return (
+
+ {children}
+
+ )
+}
+Label.propTypes = {
+ children: childrenPropType,
+ error: bool,
+}
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 (
)
}
@@ -23,5 +19,4 @@ LabelWithHelp.propTypes = {
labelFor: string,
label: labelPropType,
help: popupContentPropType,
- hoverable: bool,
}
diff --git a/components/frontend/src/widgets/WarningMessage.js b/components/frontend/src/widgets/WarningMessage.js
index 86dea71c07..088f026a5c 100644
--- a/components/frontend/src/widgets/WarningMessage.js
+++ b/components/frontend/src/widgets/WarningMessage.js
@@ -1,21 +1,41 @@
-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, showIf, title }) {
+ return (showIf ?? true) ? (
+
+ {title}
+ {children}
+
+ ) : null
+}
+InfoMessage.propTypes = {
+ children: childrenPropType,
+ showIf: bool,
+ 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() {