diff --git a/new/src/components/ScenarioOverview/DonutChart.tsx b/new/src/components/ScenarioOverview/DonutChart.tsx
index 79fae00e..616617f5 100644
--- a/new/src/components/ScenarioOverview/DonutChart.tsx
+++ b/new/src/components/ScenarioOverview/DonutChart.tsx
@@ -1,31 +1,43 @@
import { ReportStatistics } from "../../reportModel";
-import { ArcElement, Chart as ChartJS, Legend, Tooltip } from "chart.js";
+import { ArcElement, BubbleDataPoint, Chart as ChartJS, Legend, Point, Tooltip } from "chart.js";
import { Doughnut } from "react-chartjs-2";
+import { useRef } from "react";
+import { ChartJSOrUndefined } from "react-chartjs-2/dist/types";
+import { useFilters } from "../../hooks/useFilters";
+import { ScenarioStatusFilter } from "./ScenarioCollectionHead";
-export function createReportCircle(props: { statistic: ReportStatistics }) {
+export interface DonutChartProps {
+ statistic: ReportStatistics;
+}
+
+export function DonutChart(props: DonutChartProps) {
+ const successLabel = "Successful:";
+ const failedLabel = "Failed:";
ChartJS.register(ArcElement, Tooltip, Legend);
+ const { statistic } = props;
+
+ const { setUrlSearchParams } = useFilters();
+
+ const chartRef =
+ useRef<
+ ChartJSOrUndefined<
+ "doughnut",
+ (number | [number, number] | Point | BubbleDataPoint | null)[],
+ unknown
+ >
+ >(null);
+
const width = 240; // set default width to 100 if none is provided via props
const height = 120; // set default height to 100 if none is provided via props
const data = {
- labels: ["Successful:", "Failed:"],
+ labels: [successLabel, failedLabel],
datasets: [
{
- data: [props.statistic.numSuccessfulScenarios, props.statistic.numFailedScenarios],
+ data: [statistic.numSuccessfulScenarios, statistic.numFailedScenarios],
backgroundColor: ["rgba(60, 179, 113)", "rgba(255, 0, 0)"],
borderWidth: 1,
- onClick: (event: MouseEvent, elements: any[], chart: any) => {
- if (elements.length === 0) {
- return; // user did not click on a chart element
- }
- const label = chart.data.labels[elements[0].index];
- if (label === "Successful") {
- window.location.href = "/successful";
- } else if (label === "Failed") {
- window.location.href = "/failed";
- }
- },
hoverBackgroundColor: ["rgba(60,179,113,0.63)", "rgba(255,20,20,0.63)"]
}
]
@@ -53,5 +65,36 @@ export function createReportCircle(props: { statistic: ReportStatistics }) {
}
};
- return ;
+ const handleClick = (event: React.MouseEvent) => {
+ const chart = chartRef.current;
+ if (chart == null) {
+ return;
+ }
+
+ const clickedElementIndex = chart.getElementsAtEventForMode(
+ event.nativeEvent,
+ "nearest",
+ { intersect: true },
+ false
+ )[0].index;
+
+ const label = chart.data.labels?.at(clickedElementIndex);
+
+ if (label === successLabel) {
+ setUrlSearchParams({ status: ScenarioStatusFilter.SUCCESS });
+ } else if (label === failedLabel) {
+ setUrlSearchParams({ status: ScenarioStatusFilter.FAILED });
+ }
+ };
+
+ return (
+
+ );
}
diff --git a/new/src/components/ScenarioOverview/ScenarioCollectionHead.tsx b/new/src/components/ScenarioOverview/ScenarioCollectionHead.tsx
index deb291f3..7d3e4ae5 100644
--- a/new/src/components/ScenarioOverview/ScenarioCollectionHead.tsx
+++ b/new/src/components/ScenarioOverview/ScenarioCollectionHead.tsx
@@ -4,7 +4,7 @@ import RemoveIcon from "@mui/icons-material/Remove";
import AddIcon from "@mui/icons-material/Add";
import PrintOutlinedIcon from "@mui/icons-material/PrintOutlined";
import BookmarkOutlinedIcon from "@mui/icons-material/BookmarkOutlined";
-import { createReportCircle } from "./DonutChart";
+import { DonutChart } from "./DonutChart";
import { PropsWithChildren, useMemo } from "react";
import { processWords } from "../../wordProcessor";
import {
@@ -50,7 +50,7 @@ export function ScenarioCollectionHead(props: ScenarioCollectionHeadProps) {
- {createReportCircle({ statistic })}
+ {DonutChart({ statistic })}
diff --git a/new/src/components/Scenarios/__test__/StatisticsBreadcrumbs.test.tsx b/new/src/components/Scenarios/__test__/StatisticsBreadcrumbs.test.tsx
index d2950814..3f732402 100644
--- a/new/src/components/Scenarios/__test__/StatisticsBreadcrumbs.test.tsx
+++ b/new/src/components/Scenarios/__test__/StatisticsBreadcrumbs.test.tsx
@@ -1,7 +1,20 @@
import { createReportStatistics } from "./scenarioTestData";
import { StatisticBreadcrumbs } from "../StatisticsBreadcrumbs";
import { render, screen } from "@testing-library/react";
-import { MemoryRouter } from "react-router-dom";
+import { MemoryRouter, Route, Routes } from "react-router-dom";
+import userEvent from "@testing-library/user-event";
+import * as useFilters from "../../../hooks/useFilters";
+import { ScenarioStatusFilter } from "../../ScenarioOverview/ScenarioCollectionHead";
+
+const setUrlSearchParamsMock = jest.fn();
+
+beforeEach(() => {
+ jest.resetAllMocks();
+ jest.spyOn(useFilters, "useFilters").mockReturnValue({
+ filter: { status: undefined },
+ setUrlSearchParams: setUrlSearchParamsMock
+ });
+});
describe("StatisticsBreadcrumbs", () => {
it("should display statistics", () => {
@@ -60,4 +73,29 @@ describe("StatisticsBreadcrumbs", () => {
expect(screen.queryByText(")", { exact: false })).not.toBeInTheDocument();
}
);
+
+ it.each([
+ ["Successful", ScenarioStatusFilter.SUCCESS],
+ ["failed", ScenarioStatusFilter.FAILED],
+ ["pending", ScenarioStatusFilter.PENDING]
+ ])(
+ "Pressing %s link should filter for status %s",
+ (label: string, status: ScenarioStatusFilter) => {
+ const statistic = createReportStatistics();
+
+ render(
+
+
+ } />
+
+
+ );
+
+ userEvent.click(screen.getByText(label, { exact: false }));
+
+ expect(setUrlSearchParamsMock).toHaveBeenCalledWith({
+ status
+ });
+ }
+ );
});