diff --git a/packages/demo-app-ts/src/utils/useDemoPipelineNodes.tsx b/packages/demo-app-ts/src/utils/useDemoPipelineNodes.tsx
index 667c7c69..507247e2 100644
--- a/packages/demo-app-ts/src/utils/useDemoPipelineNodes.tsx
+++ b/packages/demo-app-ts/src/utils/useDemoPipelineNodes.tsx
@@ -8,7 +8,8 @@ import {
RunStatus,
WhenStatus,
} from '@patternfly/react-topology';
-
+import { CubeIcon } from '@patternfly/react-icons/dist/esm/icons/cube-icon';
+import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon';
import { logos } from './logos';
export const NODE_PADDING_VERTICAL = 45;
@@ -209,5 +210,69 @@ export const useDemoPipelineNodes = (
tasks.push(...taskGroups);
}
+ const iconTask1: PipelineNodeModel = {
+ id: `task-icon-1`,
+ type: DEFAULT_TASK_NODE_TYPE,
+ label: `Lead icon task`,
+ width: DEFAULT_TASK_WIDTH + (showContextMenu ? 10 : 0) + (showBadges ? 40 : 0),
+ height: DEFAULT_TASK_HEIGHT,
+ style: {
+ padding: [NODE_PADDING_VERTICAL, NODE_PADDING_HORIZONTAL + (showIcons ? 25 : 0)]
+ },
+ runAfterTasks: []
+ };
+
+ // put options in data, our DEMO task node will pass them along to the TaskNode
+ iconTask1.data = {
+ status: RunStatus.Failed,
+ badge: showBadges ? '3/4' : undefined,
+ badgeTooltips,
+ taskIconClass: showIcons ? logos.get('icon-java') : undefined,
+ taskIconTooltip: showIcons ? 'Environment' : undefined,
+ showContextMenu,
+ columnGroup: TASK_STATUSES.length % STATUS_PER_ROW + 1,
+ leadIcon: ,
+ };
+
+ if (!layout) {
+ const row = Math.ceil((TASK_STATUSES.length + 1) / STATUS_PER_ROW) - 1;
+ const columnWidth = COLUMN_WIDTH + (showIcons ? 15 : 0) + (showBadges ? 32 : 0) + (showContextMenu ? 20 : 0);
+ iconTask1.x = (showIcons ? 28 : 0) + columnWidth;
+ iconTask1.y = GRAPH_MARGIN_TOP + row * ROW_HEIGHT;
+ }
+ tasks.push(iconTask1);
+
+ const iconTask2: PipelineNodeModel = {
+ id: `task-icon-2`,
+ type: DEFAULT_TASK_NODE_TYPE,
+ label: `Lead icon task`,
+ width: DEFAULT_TASK_WIDTH + (showContextMenu ? 10 : 0) + (showBadges ? 40 : 0),
+ height: DEFAULT_TASK_HEIGHT,
+ style: {
+ padding: [NODE_PADDING_VERTICAL, NODE_PADDING_HORIZONTAL + (showIcons ? 25 : 0)]
+ },
+ runAfterTasks: [iconTask1.id]
+ };
+
+ // put options in data, our DEMO task node will pass them along to the TaskNode
+ iconTask2.data = {
+ badge: showBadges ? '3/4' : undefined,
+ badgeTooltips,
+ taskIconClass: showIcons ? logos.get('icon-java') : undefined,
+ taskIconTooltip: showIcons ? 'Environment' : undefined,
+ showContextMenu,
+ columnGroup: TASK_STATUSES.length % STATUS_PER_ROW + 1,
+ showStatusState: false,
+ leadIcon: ,
+ };
+
+ if (!layout) {
+ const row = Math.ceil((TASK_STATUSES.length + 1) / STATUS_PER_ROW) - 1;
+ const columnWidth = COLUMN_WIDTH + (showIcons ? 15 : 0) + (showBadges ? 32 : 0) + (showContextMenu ? 20 : 0);
+ iconTask2.x = (showIcons ? 28 : 0) + 2 * columnWidth;
+ iconTask2.y = GRAPH_MARGIN_TOP + row * ROW_HEIGHT;
+ }
+ tasks.push(iconTask2);
+
return [...tasks, ...finallyNodes, finallyGroup];
}, [badgeTooltips, layout, showBadges, showContextMenu, showGroups, showIcons]);
diff --git a/packages/module/src/pipelines/components/nodes/TaskNode.tsx b/packages/module/src/pipelines/components/nodes/TaskNode.tsx
index 4d37125b..27e1ebfe 100644
--- a/packages/module/src/pipelines/components/nodes/TaskNode.tsx
+++ b/packages/module/src/pipelines/components/nodes/TaskNode.tsx
@@ -52,6 +52,8 @@ export interface TaskNodeProps {
hideDetailsAtMedium?: boolean;
/** Statuses to show at when details are hidden */
hiddenDetailsShownStatuses?: RunStatus[];
+ /** Additional icon to be shown before the task label*/
+ leadIcon?: React.ReactNode;
/** Text for the label's badge */
badge?: string;
/** Color to use for the label's badge background */
@@ -121,6 +123,7 @@ const TaskNodeInner: React.FC = observer(({
scaleNode,
hideDetailsAtMedium,
hiddenDetailsShownStatuses = [RunStatus.Failed, RunStatus.FailedToStart, RunStatus.Cancelled],
+ leadIcon,
badge,
badgeColor,
badgeTextColor,
@@ -160,6 +163,7 @@ const TaskNodeInner: React.FC = observer(({
const nameLabelTriggerRef = React.useRef();
const nameLabelRef = useCombineRefs(textRef, nameLabelTriggerRef)
const [statusSize, statusRef] = useSize([status, showStatusState, statusIconSize]);
+ const [leadSize, leadIconRef] = useSize([leadIcon]);
const [badgeSize, badgeRef] = useSize([badge]);
const badgeLabelTriggerRef = React.useRef();
const [actionSize, actionRef] = useSize([actionIcon, paddingX]);
@@ -201,7 +205,8 @@ const TaskNodeInner: React.FC = observer(({
pillWidth,
badgeStartX,
iconWidth,
- iconStartX
+ iconStartX,
+ leadIconStartX
} = React.useMemo(() => {
if (!textSize) {
return {
@@ -213,7 +218,8 @@ const TaskNodeInner: React.FC = observer(({
pillWidth: 0,
badgeStartX: 0,
iconWidth: 0,
- iconStartX: 0
+ iconStartX: 0,
+ leadIconStartX: 0
};
}
const height: number = textHeight + 2 * paddingY;
@@ -225,7 +231,10 @@ const TaskNodeInner: React.FC = observer(({
const statusStartX = startX - statusIconSize / 4; // Adjust for icon padding
const statusSpace = status && showStatusState && statusSize ? statusSize.width + paddingX : 0;
- const textStartX = startX + statusSpace;
+ const leadIconStartX = startX + statusSpace;
+ const leadIconSpace = leadIcon ? leadSize.width + paddingX : 0;
+
+ const textStartX = leadIconStartX + leadIconSpace;
const textSpace = textWidth + paddingX;
const badgeStartX = textStartX + textSpace;
@@ -248,6 +257,7 @@ const TaskNodeInner: React.FC = observer(({
badgeStartX,
iconWidth,
iconStartX,
+ leadIconStartX,
pillWidth
};
}, [
@@ -262,6 +272,8 @@ const TaskNodeInner: React.FC = observer(({
statusIconSize,
status,
showStatusState,
+ leadSize,
+ leadIcon,
statusSize,
badgeSize,
badge,
@@ -423,6 +435,11 @@ const TaskNodeInner: React.FC = observer(({
)}
+ {leadIcon && (
+
+ {leadIcon}
+
+ )}
{taskIconComponent &&
(taskIconTooltip ? {taskIconComponent} : taskIconComponent)}
{badgeComponent}