Skip to content

Commit

Permalink
Merge main into wl-header-content
Browse files Browse the repository at this point in the history
  • Loading branch information
septum committed Nov 18, 2024
2 parents 5474537 + edd2f15 commit a4c0890
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 27 deletions.
4 changes: 2 additions & 2 deletions frontend/packages/core/src/AppProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,8 @@ const ClutchApp = ({
: workflow.displayName;

const workflowLayoutProps: LayoutProps = {
workflow,
title: heading,
workflowsInPath: workflows.filter(w => w.path === workflow.path),
title: route.displayName || workflow.displayName,
subtitle: route.description,
variant:
route.layoutProps?.variant === null ||
Expand Down
4 changes: 2 additions & 2 deletions frontend/packages/core/src/AppProvider/workflow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ interface WorkflowLayoutConfiguration {
/**
* (Optional) property to pass the defined layout properties to all of its defined routes
*/
defaultLayoutProps?: Omit<LayoutProps, "workflow" | "title" | "subtitle">;
defaultLayoutProps?: Omit<LayoutProps, "workflowsInPath" | "title" | "subtitle">;
}

export interface Workflow
Expand Down Expand Up @@ -105,7 +105,7 @@ export interface Route {
/**
* (Optional) property to define layout properties for a single route
*/
layoutProps?: Omit<LayoutProps, "workflow" | "title" | "subtitle">;
layoutProps?: Omit<LayoutProps, "workflowsInPath" | "title" | "subtitle">;
}

export interface ConfiguredRoute extends Route {
Expand Down
15 changes: 5 additions & 10 deletions frontend/packages/core/src/WorkflowLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from "react";
import { matchPath } from "react-router-dom";
import type { Interpolation } from "@emotion/styled";
import type { CSSObject, Theme } from "@mui/material";
import { alpha } from "@mui/material";
Expand All @@ -17,7 +16,7 @@ import { useWorkflowLayoutContext } from "./context";
export type LayoutVariant = "standard" | "wizard";

export type LayoutProps = {
workflow: Workflow;
workflowsInPath: Array<Workflow>;
variant?: LayoutVariant | null;
title?: string;
subtitle?: string;
Expand Down Expand Up @@ -101,7 +100,7 @@ const Subtitle = styled(Typography)(({ theme }: { theme: Theme }) => ({
}));

const WorkflowLayout = ({
workflow,
workflowsInPath,
variant = null,
title = null,
subtitle = null,
Expand All @@ -124,22 +123,18 @@ const WorkflowLayout = ({
}
}, [context]);

const entries = generateBreadcrumbsEntries(workflowsInPath, location);

if (variant === null) {
return <>{children}</>;
}

const workflowPaths = workflow.routes.map(({ path }) => `/${workflow.path}/${path}`);
const breadcrumbsEntries = generateBreadcrumbsEntries(
location,
url => !!workflowPaths.find(path => !!matchPath({ path }, url))
);

return (
<LayoutContainer $variant={variant}>
{!hideHeader && (
<PageHeader $variant={variant}>
<PageHeaderBreadcrumbsWrapper>
<Breadcrumbs entries={breadcrumbsEntries} />
<Breadcrumbs entries={entries} />
</PageHeaderBreadcrumbsWrapper>
{(headerTitle || headerSubtitle) && (
<PageHeaderMainContainer>
Expand Down
57 changes: 45 additions & 12 deletions frontend/packages/core/src/utils/generateBreadcrumbsEntries.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,56 @@
import type { Location } from "react-router-dom";
import { Location, matchPath } from "react-router-dom";

import type { Workflow } from "../AppProvider/workflow";
import type { BreadcrumbEntry } from "../Breadcrumbs";

const generateBreadcrumbsEntries = (location: Location, validateUrl: (url: string) => boolean) => {
const labels = decodeURIComponent(location.pathname)
const HOME_ENTRY = { label: "Home", url: "/" };

const generateBreadcrumbsEntries = (workflowsInPath: Array<Workflow>, location: Location) => {
// The first workflow in the will contain
// the same path and displayName as the others
const firstWorkflow = workflowsInPath[0];

if (!firstWorkflow) {
return [HOME_ENTRY];
}

// Get a single level list of the routes available
const allRoutes = workflowsInPath.flatMap(w => w.routes);

// Add to every item in the routes list the workflow path prefix
const fullPaths = allRoutes.map(({ path }) => `/${firstWorkflow.path}/${path}`);

// Generate a list of path segments from the location
const pathSegments = decodeURIComponent(location.pathname)
.split("/")
.slice(1, location.pathname.endsWith("/") ? -1 : undefined);
.slice(1, location.pathname.endsWith("/") ? -1 : undefined); // in case of a trailing `/`

const entries: Array<BreadcrumbEntry> = [HOME_ENTRY].concat(
pathSegments.map((segment, index) => {
const nextIndex = index + 1;
const url = `/${pathSegments.slice(0, nextIndex).join("/")}`;

const entries: Array<BreadcrumbEntry> = [{ label: "Home", url: "/" }].concat(
labels.map((label, index) => {
let url = `/${labels.slice(0, index + 1).join("/")}`;
const path = fullPaths.find(p => !!matchPath(p, url));

if (!validateUrl(url)) {
url = undefined;
}
// If there is a matched path, it's used to find the route that contains its displayName
const route = path
? allRoutes.find(r =>
r.path.startsWith("/")
? r.path
: // Done in case of an empty path or missing a leading `/`
`/${r.path}` === `/${path.split("/").slice(2).join("/")}`
)
: null;

return {
label,
url,
// For the label:
// - Prioritize the display name
// - Handle the case of a single route with an unusual long name
// - Default to the path segment
label:
route?.displayName || (allRoutes.length === 1 && firstWorkflow.displayName) || segment,
// Set a null url if there is no path or for the last segment
url: !!path && pathSegments.length !== nextIndex ? url : null,
};
})
);
Expand Down
4 changes: 3 additions & 1 deletion frontend/packages/wizard/src/wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ const Header = styled(Grid)<{ $orientation: MuiStepperProps["orientation"] }>(

const Container = styled(MuiContainer)<{ $width: ContainerProps["width"] }>(
({ theme }: { theme: Theme }) => ({
padding: theme.clutch.layout.gutter,
padding: theme.clutch.useWorkflowLayout
? theme.spacing("none", "md")
: theme.clutch.layout.gutter,
height: "100%",
}),
props => ({
Expand Down

0 comments on commit a4c0890

Please sign in to comment.