diff --git a/frontend/src/app/App.scss b/frontend/src/app/App.scss
index 60c55f9db1..3e4dd89d26 100644
--- a/frontend/src/app/App.scss
+++ b/frontend/src/app/App.scss
@@ -41,6 +41,12 @@ body,
inset-inline-start: 12px;
}
}
+ .pf-topology-container {
+ overflow-y: hidden;
+ .pf-v5-c-drawer__panel.pf-m-resizable {
+ min-width: 350px;
+ }
+ }
}
// specificity targeting form elements to override --pf-v5-global--FontSize--md
diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx
index 915fcdec68..a96c62d4ec 100644
--- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx
+++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineDetails.tsx
@@ -3,11 +3,9 @@ import { useNavigate, useParams } from 'react-router-dom';
import {
Breadcrumb,
BreadcrumbItem,
- Drawer,
- DrawerContent,
- DrawerContentBody,
Flex,
FlexItem,
+ PageSection,
Tab,
TabContent,
TabContentBody,
@@ -84,90 +82,87 @@ const PipelineDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath }) =
return (
<>
-
- setSelectedId(null)}
- />
- }
- >
-
-
- {breadcrumbPath()}
-
- {/* TODO: Remove the custom className after upgrading to PFv6 */}
-
-
-
- {/* TODO: Remove the custom className after upgrading to PFv6 */}
-
-
-
- }
- title={
-
+ {breadcrumbPath()}
+
+ {/* TODO: Remove the custom className after upgrading to PFv6 */}
+
+
+
+ {/* TODO: Remove the custom className after upgrading to PFv6 */}
+
+
+
+ }
+ title={
+
+ }
+ {...(pipelineVersion && {
+ description: (
+
+ ),
+ })}
+ empty={false}
+ loaded={isLoaded}
+ headerAction={
+ isPipelineVersionLoaded && (
+
+
+
+ navigate(
+ routePipelineDetailsNamespace(
+ namespace,
+ version.pipeline_id,
+ version.pipeline_version_id,
+ ),
+ )
+ }
/>
- }
- {...(pipelineVersion && {
- description: (
-
+
+ {isLoaded && (
+ setDeletionOpen(true)}
+ pipeline={pipeline}
+ pipelineVersion={pipelineVersion}
/>
- ),
- })}
- empty={false}
- loaded={isLoaded}
- headerAction={
- isPipelineVersionLoaded && (
-
-
-
- navigate(
- routePipelineDetailsNamespace(
- namespace,
- version.pipeline_id,
- version.pipeline_version_id,
- ),
- )
- }
- />
-
-
- {isLoaded && (
- setDeletionOpen(true)}
- pipeline={pipeline}
- pipelineVersion={pipelineVersion}
- />
- )}
-
-
- )
- }
- >
+ )}
+
+
+ )
+ }
+ >
+
+
+
{
setActiveTabKey(tabIndex);
@@ -182,7 +177,6 @@ const PipelineDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath }) =
aria-label="Pipeline Graph Tab"
tabContentId={`tabContent-${PipelineDetailsTab.GRAPH}`}
/>
-
Summary}
@@ -192,7 +186,6 @@ const PipelineDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath }) =
-
Pipeline spec}
@@ -201,54 +194,55 @@ const PipelineDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath }) =
tabContentId={`tabContent-${PipelineDetailsTab.YAML}`}
/>
-
-
- {nodes.length === 0 ? (
-
- ) : (
- {
- const firstId = ids[0];
- if (ids.length === 0) {
- setSelectedId(null);
- } else {
- setSelectedId(firstId);
- }
- }}
- />
- )}
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ {nodes.length === 0 ? (
+
+ ) : (
+ {
+ setSelectedId(ids.length ? ids[0] : null);
+ }}
+ sidePanel={
+ setSelectedId(null)}
+ />
+ }
+ />
+ )}
+
+
+
+
+
+
+
+
+
+
{pipeline && (
= ({ t
}
return (
-
+
{task.name} {task.type === 'artifact' ? 'Artifact details' : ''}
diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx
index ebd7bb8098..6cb2b98be4 100644
--- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx
+++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx
@@ -2,8 +2,6 @@ import * as React from 'react';
import {
Breadcrumb,
BreadcrumbItem,
- Drawer,
- DrawerContent,
EmptyState,
EmptyStateIcon,
EmptyStateVariant,
@@ -96,99 +94,87 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath,
const runType =
run?.storage_state === StorageStateKF.ARCHIVED ? PipelineRunType.ARCHIVED : undefined;
+ const panelContent = selectedNode ? (
+ setSelectedId(null)}
+ executions={executions}
+ />
+ ) : null;
+
return (
<>
-
- setSelectedId(null)}
- executions={executions}
+ : 'Error loading run'
+ }
+ subtext={
+ run && (
+
- }
- >
-
- ) : (
- 'Error loading run'
- )
- }
- subtext={
- run && (
-
- )
- }
- description={
- run?.description ? : ''
- }
- loaded={loaded}
- loadError={error}
- breadcrumb={
-
- {breadcrumbPath(runType)}
-
- {version ? (
-
- {/* TODO: Remove the custom className after upgrading to PFv6 */}
-
-
- ) : (
- 'Loading...'
+ )
+ }
+ description={
+ run?.description ? : ''
+ }
+ loaded={loaded}
+ loadError={error}
+ breadcrumb={
+
+ {breadcrumbPath(runType)}
+
+ {version ? (
+
-
+ >
{/* TODO: Remove the custom className after upgrading to PFv6 */}
-
-
-
- }
- headerAction={
- setDeleting(true)}
- onArchive={() => setArchiving(true)}
+
+
+ ) : (
+ 'Loading...'
+ )}
+
+
+ {/* TODO: Remove the custom className after upgrading to PFv6 */}
+
- }
- empty={false}
- >
- {
- const firstId = ids[0];
- if (ids.length === 0) {
- setSelectedId(null);
- } else if (nodes.find((node) => node.id === firstId)) {
- setSelectedId(firstId);
- }
- }}
- />
- }
+
+
+ }
+ headerAction={
+ setDeleting(true)}
+ onArchive={() => setArchiving(true)}
+ />
+ }
+ empty={false}
+ >
+ {
+ setSelectedId(ids.length ? ids[0] : null);
+ }}
+ sidePanel={panelContent}
/>
-
-
-
+ }
+ />
+
= ({
const isJob = run && isPipelineRunJob(run);
return (
- <>
- setActiveKey(eventKey)}
- aria-label="Pipeline run details tabs"
+
+
- Graph}
- aria-label="Run graph tab"
- data-testid="pipeline-run-tab-graph"
- />
-
- Details}
- aria-label="Run details tab"
- data-testid="pipeline-run-tab-details"
- >
-
-
-
-
-
- {!isJob && pipelineSpec && (
-
+ setActiveKey(eventKey)}
+ aria-label="Pipeline run details tabs"
+ >
+ Graph}
+ aria-label="Run graph tab"
+ data-testid="pipeline-run-tab-graph"
+ />
+ Details}
+ aria-label="Run details tab"
+ data-testid="pipeline-run-tab-details"
+ >
+
+
+
+
+ {!isJob && pipelineSpec && (
+ Pipeline spec}
+ aria-label="Run spec tab"
+ data-testid="pipeline-run-tab-spec"
+ />
+ )}
+
+
+
+
+ {graphContent}
+
+ Pipeline spec}
- aria-label="Run spec tab"
- data-testid="pipeline-run-tab-spec"
- />
- )}
-
-
-
-
- {graphContent}
-
-
-
-
-
-
-
-
- >
+ hidden={activeKey !== DetailsTabKey.Spec}
+ className="pf-v5-u-h-100"
+ style={{ flex: 1 }}
+ >
+
+
+
+
+
+
+
);
};
diff --git a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDrawerRightContent.tsx b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDrawerRightContent.tsx
index cc9b916457..87fb500b60 100644
--- a/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDrawerRightContent.tsx
+++ b/frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDrawerRightContent.tsx
@@ -33,10 +33,8 @@ const PipelineRunDrawerRightContent: React.FC
{task.type === 'artifact' ? (
nodes.find((n) => n.id === firstId)?.data?.pipelineTask;
-
const loaded = versionLoaded && jobLoaded;
const error = versionError || jobError;
@@ -76,76 +72,67 @@ const PipelineRunJobDetails: PipelineCoreDetailsPageComponent = ({
);
}
+ const panelContent = selectedNode ? (
+ setSelectedId(null)}
+ />
+ ) : null;
+
return (
<>
-
- setSelectedId(null)}
- />
- }
- >
- : ''}
- loaded={loaded}
- loadError={error}
- breadcrumb={
-
- {breadcrumbPath(PipelineRunType.SCHEDULED)}
-
- {version ? (
-
- {version.display_name}
-
- ) : (
- 'Loading...'
+ : ''}
+ loaded={loaded}
+ loadError={error}
+ breadcrumb={
+
+ {breadcrumbPath(PipelineRunType.SCHEDULED)}
+
+ {version ? (
+
- {job?.display_name ?? 'Loading...'}
-
- }
- headerAction={
- loaded && (
- setDeleting(true)}
- />
- )
- }
- empty={false}
- >
- {
- const firstId = ids[0];
- if (ids.length === 0) {
- setSelectedId(null);
- } else if (getFirstNode(firstId)) {
- setSelectedId(firstId);
- }
- }}
- />
- }
+ >
+ {version.display_name}
+
+ ) : (
+ 'Loading...'
+ )}
+
+ {job?.display_name ?? 'Loading...'}
+
+ }
+ headerAction={
+ loaded && (
+ setDeleting(true)}
/>
-
-
-
-
+ )
+ }
+ empty={false}
+ >
+ {
+ setSelectedId(ids.length ? ids[0] : null);
+ }}
+ sidePanel={panelContent}
+ />
+ }
+ />
+
void;
nodes: PipelineNodeModel[];
+ sidePanel?: React.ReactElement | null;
};
const PipelineTopology: React.FC = ({
nodes,
selectedIds,
onSelectionChange,
+ sidePanel,
}) => {
const controller = useTopologyController('g1');
@@ -51,7 +53,7 @@ const PipelineTopology: React.FC = ({
return (
-
+
);
};
diff --git a/frontend/src/concepts/topology/PipelineVisualizationSurface.tsx b/frontend/src/concepts/topology/PipelineVisualizationSurface.tsx
index f05528ecbc..a338fb6c44 100644
--- a/frontend/src/concepts/topology/PipelineVisualizationSurface.tsx
+++ b/frontend/src/concepts/topology/PipelineVisualizationSurface.tsx
@@ -12,6 +12,7 @@ import {
addSpacerNodes,
DEFAULT_SPACER_NODE_TYPE,
DEFAULT_EDGE_TYPE,
+ TopologySideBar,
} from '@patternfly/react-topology';
import {
EmptyState,
@@ -25,14 +26,43 @@ import { NODE_HEIGHT, NODE_WIDTH } from './const';
type PipelineVisualizationSurfaceProps = {
nodes: PipelineNodeModel[];
selectedIds?: string[];
+ sidePanel?: React.ReactElement | null;
};
const PipelineVisualizationSurface: React.FC = ({
nodes,
selectedIds,
+ sidePanel,
}) => {
const controller = useVisualizationController();
const [error, setError] = React.useState();
+
+ const selectedNode = React.useMemo(() => {
+ if (selectedIds?.[0]) {
+ const node = controller.getNodeById(selectedIds[0]);
+ if (node) {
+ return node;
+ }
+ }
+ return null;
+ }, [selectedIds, controller]);
+
+ React.useEffect(() => {
+ let resizeTimeout: NodeJS.Timeout | null;
+ if (selectedNode) {
+ // Use a timeout in order to allow the side panel to be shown and window size recomputed
+ resizeTimeout = setTimeout(() => {
+ controller.getGraph().panIntoView(selectedNode, { offset: 20, minimumVisible: 100 });
+ resizeTimeout = null;
+ }, 500);
+ }
+ return () => {
+ if (resizeTimeout) {
+ clearTimeout(resizeTimeout);
+ }
+ };
+ }, [selectedIds, controller, selectedNode]);
+
React.useEffect(() => {
const currentModel = controller.toModel();
const updateNodes = nodes.map((node) => {
@@ -158,6 +188,9 @@ const PipelineVisualizationSurface: React.FC
})}
/>
}
+ sideBarOpen={!!selectedNode}
+ sideBarResizable
+ sideBar={{sidePanel}}
>