Skip to content

Commit

Permalink
Breadcrumb setup (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisJamesC authored Aug 17, 2023
1 parent 856d3a7 commit ac9b32d
Show file tree
Hide file tree
Showing 8 changed files with 377 additions and 1,502 deletions.
2 changes: 1 addition & 1 deletion reports/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Report examples
public/monitoring-tests-reports
public/report_uspace.json

# Logs
logs
Expand Down
12 changes: 3 additions & 9 deletions reports/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"test": "jest"
"test": "vitest"
},
"dependencies": {
"jsonpath": "^1.1.1",
Expand All @@ -19,7 +19,6 @@
"react-router-dom": "^6.15.0"
},
"devDependencies": {
"@types/jest": "^29.5.3",
"@types/jsonpath": "^0.2.0",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
Expand All @@ -29,14 +28,9 @@
"eslint": "^8.45.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"jest": "^29.6.2",
"ts-jest": "^29.1.1",
"typescript": "^5.0.2",
"vite": "^4.4.5",
"vite-plugin-singlefile": "^0.13.5"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node"
"vite-plugin-singlefile": "^0.13.5",
"vitest": "^0.34.2"
}
}
4 changes: 1 addition & 3 deletions reports/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ function App() {
return <div>Report not found</div>;
}
const router = createHashRouter(nav);
return <>
<RouterProvider router={router} />
</>;
return <RouterProvider router={router} />;
}

export default App;
57 changes: 46 additions & 11 deletions reports/src/capability/CapabilityTable.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import {useNavigate} from "react-router-dom";
import {Capability, Check, Requirement} from "./capabilityTypes";
import {useEffect, useState} from "react";
import { Link } from "react-router-dom";
import { Capability, Check, Requirement } from "./capabilityTypes";
import { useEffect, useState } from "react";
import { useMatches } from "react-router-dom";
import { ReactNode } from "react";

type CapabilityTableProps = {
capability: Capability;
};

export const CheckLabel = ({name, docUrl}: {name: string; docUrl?: string}) => {
export const CheckLabel = ({
name,
docUrl,
}: {
name: string;
docUrl?: string;
}) => {
return docUrl ? <a href={docUrl}>{name}</a> : <>{name}</>;
};

export const CheckRow = ({check}: {check: Check}) => {
export const CheckRow = ({ check }: { check: Check }) => {
const [showDetails, setShowDetails] = useState(false);
const separator = " :: ";
const checkSource = (
Expand Down Expand Up @@ -82,13 +90,12 @@ export const ChildCapabilityRow = ({
capability: Capability;
path: string;
}) => {
const navigate = useNavigate();
return (
<tr>
<td colSpan={2}>
<a onClick={() => navigate(path, {relative: "path"})}>
<Link to={path}>
{capability.name} ({capability.participant_id})
</a>
</Link>
</td>
<td
className={capability.result === "pass" ? "pass" : "fail"}
Expand All @@ -109,7 +116,7 @@ const ChildCapabilityHeader = () => {
);
};

export const CapabilityRows = ({capability}: {capability: Capability}) => {
export const CapabilityRows = ({ capability }: { capability: Capability }) => {
const requirements = capability.requirements
.flatMap((r) => requirementRow(r))
.flat();
Expand All @@ -131,7 +138,7 @@ export const CapabilityRows = ({capability}: {capability: Capability}) => {
];
};

export const CapabilityDebug = ({capability}: {capability: Capability}) => {
export const CapabilityDebug = ({ capability }: { capability: Capability }) => {
// return <code><pre style={{textAlign:"left"}}>{JSON.stringify(capability, null, 2)}</pre></code>;
useEffect(
() => console.info("Displayed Capability: ", capability),
Expand All @@ -140,9 +147,37 @@ export const CapabilityDebug = ({capability}: {capability: Capability}) => {
return <></>;
};

export const CapabilityTable = ({capability}: CapabilityTableProps) => {
type CrumbHandle = { crumb: () => ReactNode };
function Breadcrumbs() {
const matches = useMatches();
const crumbs = matches
// first get rid of any matches that don't have handle and crumb
.filter((match) =>
Boolean((match.handle as CrumbHandle | undefined)?.crumb)
)
.slice(0, -1) // Skip last crumb
.map((match) => (match.handle as CrumbHandle).crumb())
.reverse();

return (
<div>
{crumbs.map((crumb, index) => (
<span key={index}>
{crumb}
{index === crumbs.length - 1 ? "" : " <= "}
</span>
))}
</div>
);
}

export const CapabilityTable = ({ capability }: CapabilityTableProps) => {
if (!capability) {
return <span>Capability not found</span>;
}
return (
<>
<Breadcrumbs />
<table>
<thead>
<tr>
Expand Down
15 changes: 15 additions & 0 deletions reports/src/capability/reportNavigation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { test, expect } from "vitest";
import { joinRoutes } from "./reportNavigation";

test("Routes are correctly joined", () => {
expect(joinRoutes("/hello/", "/world/")).toBe("/hello/world/");
expect(joinRoutes("hello", "world")).toBe("hello/world");
expect(joinRoutes("", "world")).toBe("world");
expect(joinRoutes("hello", "")).toBe("hello");
expect(joinRoutes("/", "world")).toBe("/world");
});

test("Routes are correctly joined with numbers", () => {
expect(joinRoutes("/", "0")).toBe("/0");
expect(joinRoutes("/1", "0")).toBe("/1/0");
});
34 changes: 29 additions & 5 deletions reports/src/capability/reportNavigation.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
import { RouteObject } from "react-router-dom";
import { Link, RouteObject } from "react-router-dom";
import { Capability } from "./capabilityTypes";
import { CapabilityTable } from "./CapabilityTable";

export const joinRoutes = (root: string, child: string): string => {
const cleanChild = child.replace(/^\/+/g, ""); // remove leading slash
if (root === "/") {
return root + cleanChild;
}
const cleanRoot = root.replace(/\/$/, ""); // remove trailing slash
if (!cleanRoot) {
return cleanChild;
} else if (!cleanChild) {
return cleanRoot;
} else {
return `${cleanRoot}/${cleanChild}`;
}
};

export const getNavFromCapability = (
capability: Capability,
path: string = ""
path: string = "/",
fullPath: string = "/"
): RouteObject[] => {
const children =
capability.childCapabilities.flatMap((c, i) =>
getNavFromCapability(c, `${path}/${i}`)
getNavFromCapability(c, `${i}`, joinRoutes(fullPath, i.toString()))
) || [];
return [
{
path: path,
element: <CapabilityTable capability={capability} />,
handle: {
crumb: () => <Link to={fullPath}>{capability.name}</Link>,
},
children: [
{
index: true,
element: <CapabilityTable capability={capability} />,
},
...children,
],
},
...children,
];
};
34 changes: 20 additions & 14 deletions reports/src/capability/useReport.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import {useState, useEffect, useMemo} from "react";
import {
ReportsReportTestRunReport,
} from "../types/TestRunReport";
import {RouteObject} from "react-router-dom";
import {parseReport} from "./reportParser";
import {getNavFromCapability} from "./reportNavigation";
import { useState, useEffect, useMemo } from "react";
import { ReportsReportTestRunReport } from "../types/TestRunReport";
import { RouteObject } from "react-router-dom";
import { parseReport } from "./reportParser";
import { getNavFromCapability } from "./reportNavigation";

const reportUrl = "/report_uspace.json";

type UseReportProps = {
report?: ReportsReportTestRunReport
}
report?: ReportsReportTestRunReport;
};

type UseReportReturn = {
loading: boolean,
loading: boolean;
report?: ReportsReportTestRunReport;
nav: RouteObject[];
};

export const useReport = ({report: _report}: UseReportProps): UseReportReturn => {
export const useReport = ({
report: _report,
}: UseReportProps): UseReportReturn => {
const [loading, setLoading] = useState<boolean>(true);
const [report, setReport] = useState<ReportsReportTestRunReport>();

Expand All @@ -38,7 +38,13 @@ export const useReport = ({report: _report}: UseReportProps): UseReportReturn =>
fetchReport();
}, []);

const parsedReport = useMemo(() => report ? parseReport(report) : undefined, [report]);
const nav = useMemo(() => parsedReport ? getNavFromCapability(parsedReport.capability) : [], [parsedReport]);
return {loading, report, nav};
const parsedReport = useMemo(
() => (report ? parseReport(report) : undefined),
[report]
);
const nav = useMemo(
() => (parsedReport ? getNavFromCapability(parsedReport.capability) : []),
[parsedReport]
);
return { loading, report, nav };
};
Loading

0 comments on commit ac9b32d

Please sign in to comment.