diff --git a/frontend/packages/core/src/AppProvider/index.tsx b/frontend/packages/core/src/AppProvider/index.tsx index 9d7c984d55..c3c4a5162d 100644 --- a/frontend/packages/core/src/AppProvider/index.tsx +++ b/frontend/packages/core/src/AppProvider/index.tsx @@ -218,10 +218,9 @@ const ClutchApp = ({ // We define these props in order to avoid UI changes before refactoring const workflowLayoutProps: LayoutProps = { - variant: "custom", - hideHeader: true, - heading, ...route.layoutProps, + heading: route.layoutProps?.heading || heading, + workflow, }; const workflowRouteComponent = ( diff --git a/frontend/packages/core/src/WorkflowLayout/index.tsx b/frontend/packages/core/src/WorkflowLayout/index.tsx index a7927a4db9..97733a9a14 100644 --- a/frontend/packages/core/src/WorkflowLayout/index.tsx +++ b/frontend/packages/core/src/WorkflowLayout/index.tsx @@ -1,14 +1,20 @@ import React from "react"; +import { matchPath } from "react-router"; import type { Interpolation } from "@emotion/styled"; import type { CSSObject, Theme } from "@mui/material"; +import type { Workflow } from "../AppProvider/workflow"; +import Breadcrumbs from "../Breadcrumbs"; +import { useLocation } from "../navigation"; import styled from "../styled"; import { Typography } from "../typography"; +import { generateBreadcrumbsEntries } from "../utils"; export type LayoutVariant = "standard" | "wizard" | "custom"; export type LayoutProps = { - variant: LayoutVariant; + workflow: Workflow; + variant?: LayoutVariant; heading?: string | React.ReactElement; hideHeader?: boolean; }; @@ -61,15 +67,27 @@ const HeaderTitle = styled(Typography)({ }); const WorkflowLayout = ({ - variant, - heading, + workflow, + variant = "standard", + heading = null, hideHeader = false, children, }: React.PropsWithChildren) => { + const location = useLocation(); + const workflowPaths = workflow.routes.map(({ path }) => `/${workflow.path}/${path}`); + const breadcrumbsEntries = generateBreadcrumbsEntries( + location, + (url: string) => + `/${workflow.path}` !== url && + !workflowPaths.includes(url) && + !workflowPaths.find(path => !!matchPath({ path }, url)) + ); + return ( {!hideHeader && ( + {heading && ( <> {React.isValidElement(heading) ? ( diff --git a/frontend/packages/core/src/utils/generateBreadcrumbsEntries.tsx b/frontend/packages/core/src/utils/generateBreadcrumbsEntries.tsx new file mode 100644 index 0000000000..029deed285 --- /dev/null +++ b/frontend/packages/core/src/utils/generateBreadcrumbsEntries.tsx @@ -0,0 +1,28 @@ +import type { Location } from "history"; + +import type { BreadcrumbEntry } from "../Breadcrumbs"; + +const generateBreadcrumbsEntries = (location: Location, validateUrl: (url: string) => boolean) => { + const labels = location.pathname + .split("/") + .slice(1, location.pathname.endsWith("/") ? -1 : undefined); + + const entries: Array = [{ label: "Home", url: "/" }].concat( + labels.map((label, index) => { + let url = `/${labels.slice(0, index + 1).join("/")}`; + + if (validateUrl(url)) { + url = undefined; + } + + return { + label, + url, + }; + }) + ); + + return entries; +}; + +export default generateBreadcrumbsEntries; diff --git a/frontend/packages/core/src/utils/index.ts b/frontend/packages/core/src/utils/index.ts index 4aa7bbf944..e8eab9b075 100644 --- a/frontend/packages/core/src/utils/index.ts +++ b/frontend/packages/core/src/utils/index.ts @@ -1,2 +1,3 @@ export { default as getDisplayName } from "./getDisplayName"; export { default as findPathMatchList } from "./pathMatching"; +export { default as generateBreadcrumbsEntries } from "./generateBreadcrumbsEntries";