Skip to content

Commit

Permalink
Add pipeline groups (#8278)
Browse files Browse the repository at this point in the history
* Add pipeline groups

* Breakout topology package options for readability
  • Loading branch information
jeff-phillips-18 authored Oct 26, 2022
1 parent 873cff7 commit ee9b15c
Show file tree
Hide file tree
Showing 20 changed files with 915 additions and 918 deletions.
Original file line number Diff line number Diff line change
@@ -1,96 +1,121 @@
import React from 'react';
import {
Model,
ModelKind,
withPanZoom,
GraphComponent,
useComponentFactory,
useModel,
ComponentFactory,
useLayoutFactory,
Graph,
Layout,
PipelineDagreLayout,
PipelineNodeModel,
Visualization,
VisualizationProvider,
useEventListener,
SelectionEventListener,
SELECTION_EVENT,
TopologyView,
VisualizationSurface,
useVisualizationController,
NODE_SEPARATION_HORIZONTAL,
GRAPH_LAYOUT_END_EVENT,
getSpacerNodes,
getEdgesFromNodes,
useVisualizationController
DEFAULT_EDGE_TYPE
} from '@patternfly/react-topology';
import '@patternfly/react-styles/css/components/Topology/topology-components.css';
import withTopologySetup from './utils/withTopologySetup';
import pipelineComponentFactory from './components/pipelineComponentFactory';
import { createFinallyTasks, createParallelTasks, createStatusTasks, setWhenStatus } from './utils/pipelineUtils';
import pipelineComponentFactory, { GROUPED_EDGE_TYPE } from './components/pipelineComponentFactory';
import { usePipelineOptions } from './usePipelineOptions';
import { useDemoPipelineNodes } from './useDemoPipelineNodes';
import { GROUPED_PIPELINE_NODE_SEPARATION_HORIZONTAL } from './components/DemoTaskGroupEdge';

export const PIPELINE_NODE_SEPARATION_VERTICAL = 65;

export const LAYOUT_TITLE = 'Layout';

const getModel = (layout: string): Model => {
const tasks: PipelineNodeModel[] = createStatusTasks('task', 4, undefined, false, true, true, true);
const PIPELINE_LAYOUT = 'PipelineLayout';
const GROUPED_PIPELINE_LAYOUT = 'GroupedPipelineLayout';

const whenTasks = tasks.reduce((acc, task, index) => {
if (index % (Math.floor(tasks.length / 3) + 1) !== 0) {
acc.push(task);
}
return acc;
}, []);
setWhenStatus(whenTasks);
const TopologyPipelineLayout: React.FC = () => {
const [selectedIds, setSelectedIds] = React.useState<string[]>();

for (let i = 0; i < tasks.length; i++) {
tasks[i + 1].runAfterTasks.push(tasks[i].id);
i++;
if (i + 1 < tasks.length) {
tasks[i + 1].runAfterTasks.push(tasks[i].id);
}
i++;
if (i + 1 < tasks.length) {
tasks[i + 1].runAfterTasks.push(tasks[i].id);
}
i++;
}
const controller = useVisualizationController();
const { contextToolbar, showContextMenu, showBadges, showIcons, showGroups, badgeTooltips } = usePipelineOptions(
true
);
const pipelineNodes = useDemoPipelineNodes(
showContextMenu,
showBadges,
showIcons,
badgeTooltips,
'PipelineDagreLayout',
showGroups
);

const parallelTasks = createParallelTasks('parallelTasks', tasks[9].id, 3, 2, true, true);
tasks.push(...parallelTasks);
React.useEffect(() => {
const spacerNodes = getSpacerNodes(pipelineNodes);
const nodes = [...pipelineNodes, ...spacerNodes];
const edges = getEdgesFromNodes(
nodes.filter(n => !n.group),
showGroups ? GROUPED_EDGE_TYPE : DEFAULT_EDGE_TYPE
);

const finallyNodes = createFinallyTasks('finally', 2, tasks, true);
const finallyGroup = {
id: 'finally-group',
type: 'finally-group',
children: finallyNodes.map(n => n.id),
group: true,
style: { padding: [35, 17] }
};
controller.fromModel(
{
graph: {
id: 'g1',
type: 'graph',
x: 25,
y: 25,
layout: showGroups ? GROUPED_PIPELINE_LAYOUT : PIPELINE_LAYOUT
},
nodes,
edges
},
true
);
controller.getGraph().layout();
}, [controller, pipelineNodes, showGroups]);

const spacerNodes = getSpacerNodes([...tasks, ...finallyNodes]);
const edges = getEdgesFromNodes([...tasks, ...finallyNodes, ...spacerNodes]);
useEventListener<SelectionEventListener>(SELECTION_EVENT, ids => {
setSelectedIds(ids);
});

return {
graph: {
id: 'g1',
type: 'graph',
x: 25,
y: 25,
layout
},
nodes: [...tasks, ...finallyNodes, ...spacerNodes, finallyGroup],
edges
};
return (
<TopologyView contextToolbar={contextToolbar}>
<VisualizationSurface state={{ selectedIds }} />
</TopologyView>
);
};

export const PipelineLayout = withTopologySetup(() => {
useLayoutFactory((type: string, graph: Graph): Layout | undefined => new PipelineDagreLayout(graph, { nodesep: 95 }));
useComponentFactory(pipelineComponentFactory);
const controller = useVisualizationController();
controller.setFitToScreenOnLayout(true);
TopologyPipelineLayout.displayName = 'TopologyPipelineLayout';

// support pan zoom and drag
useComponentFactory(
React.useCallback<ComponentFactory>(kind => {
if (kind === ModelKind.graph) {
return withPanZoom()(GraphComponent);
export const PipelineLayout = React.memo(() => {
const controller = new Visualization();
controller.setFitToScreenOnLayout(true);
controller.registerComponentFactory(pipelineComponentFactory);
controller.registerLayoutFactory(
(type: string, graph: Graph): Layout | undefined =>
new PipelineDagreLayout(graph, {
nodesep: PIPELINE_NODE_SEPARATION_VERTICAL,
ranksep:
type === GROUPED_PIPELINE_LAYOUT ? GROUPED_PIPELINE_NODE_SEPARATION_HORIZONTAL : NODE_SEPARATION_HORIZONTAL,
ignoreGroups: true
})
);
controller.fromModel(
{
graph: {
id: 'g1',
type: 'graph',
x: 25,
y: 25,
layout: PIPELINE_LAYOUT
}
return undefined;
}, [])
},
false
);
controller.addEventListener(GRAPH_LAYOUT_END_EVENT, () => {
controller.getGraph().fit(75);
});

useModel(getModel('PipelineDagreLayout'));
return null;
return (
<VisualizationProvider controller={controller}>
<TopologyPipelineLayout />
</VisualizationProvider>
);
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';
import { Checkbox, Flex, FlexItem, Radio, ToolbarItem } from '@patternfly/react-core';
import {
TopologyView,
Visualization,
Expand All @@ -12,150 +11,44 @@ import {
} from '@patternfly/react-topology';
import '@patternfly/react-styles/css/components/Topology/topology-components.css';
import pipelineComponentFactory from './components/pipelineComponentFactory';
import { createFinallyTasks, createStatusTasks, setWhenStatus } from './utils/pipelineUtils';
import { usePipelineOptions } from './usePipelineOptions';
import { useDemoPipelineNodes } from './useDemoPipelineNodes';

export const TASKS_TITLE = 'Tasks';

export const PipelineTasks: React.FC = () => {
const [showContext, setShowContext] = React.useState<boolean>(false);
const [showBadges, setShowBadges] = React.useState<boolean>(false);
const [showIcons, setShowIcons] = React.useState<boolean>(false);
const [selectedIds, setSelectedIds] = React.useState<string[]>();
const [badgeTooltips, setBadgeTooltips] = React.useState<boolean>(false);

const controller = useVisualizationController();
const { contextToolbar, showContextMenu, showBadges, showIcons, badgeTooltips } = usePipelineOptions();
const pipelineNodes = useDemoPipelineNodes(showContextMenu, showBadges, showIcons, badgeTooltips);

React.useEffect(() => {
const tasks = createStatusTasks(
'task',
4,
undefined,
false,
false,
showContext,
showBadges,
showIcons,
badgeTooltips
);
setWhenStatus(tasks);
const finallyNodes = createFinallyTasks('finally', 2, tasks);
const finallyGroup = {
id: 'finally-group',
type: 'finally-group',
children: finallyNodes.map(n => n.id),
group: true,
style: { padding: 30 }
};
const model = {
graph: {
id: 'g1',
type: 'graph',
x: 25,
y: 25
controller.fromModel(
{
graph: {
id: 'g1',
type: 'graph',
x: 25,
y: 25
},
nodes: pipelineNodes
},
nodes: [...tasks, ...finallyNodes, finallyGroup]
};
controller.fromModel(model, false);
}, [badgeTooltips, controller, showBadges, showContext, showIcons]);
false
);
}, [controller, pipelineNodes]);

useEventListener<SelectionEventListener>(SELECTION_EVENT, ids => {
setSelectedIds(ids);
});

const contextToolbar = (
<>
<ToolbarItem>
<Flex alignItems={{ default: 'alignItemsCenter' }}>
<FlexItem>Icons:</FlexItem>
<FlexItem>
<Radio
id="show-icons"
aria-label="Show icons"
label="Show"
name="show-icons"
isChecked={showIcons}
onChange={checked => checked && setShowIcons(true)}
/>
</FlexItem>
<FlexItem>
<Radio
id="hide-icons"
aria-label="Hide icons"
label="Hide"
name="hide-icons"
isChecked={!showIcons}
onChange={checked => checked && setShowIcons(false)}
/>
</FlexItem>
</Flex>
</ToolbarItem>
<ToolbarItem>
<Flex alignItems={{ default: 'alignItemsCenter' }}>
<FlexItem>Badges:</FlexItem>
<FlexItem>
<Radio
id="show-badges"
aria-label="Show badges"
label="Show"
name="show-badges"
isChecked={showBadges}
onChange={checked => checked && setShowBadges(true)}
/>
</FlexItem>
<FlexItem>
<Radio
id="hide-badges"
aria-label="Hide badges"
label="Hide"
name="hide-badges"
isChecked={!showBadges}
onChange={checked => checked && setShowBadges(false)}
/>
</FlexItem>
</Flex>
</ToolbarItem>
<ToolbarItem>
<Flex alignItems={{ default: 'alignItemsCenter' }}>
<FlexItem>Context menus:</FlexItem>
<FlexItem>
<Radio
id="show-menus"
aria-label="Show context menus"
label="Show"
name="show-menus"
isChecked={showContext}
onChange={checked => checked && setShowContext(true)}
/>
</FlexItem>
<FlexItem>
<Radio
id="hide-context"
aria-label="Hide context menus"
label="Hide"
name="hide-context"
isChecked={!showContext}
onChange={checked => checked && setShowContext(false)}
/>
</FlexItem>
<FlexItem className="pf-u-ml-2xl">
<Checkbox
id="badge-tips-checkbox"
label="Use tooltips for badges"
isChecked={badgeTooltips}
onChange={setBadgeTooltips}
/>
</FlexItem>
</Flex>
</ToolbarItem>
</>
);

return (
<TopologyView contextToolbar={contextToolbar}>
<VisualizationSurface state={{ selectedIds }} />
</TopologyView>
);
};

PipelineTasks.displayName = 'PipelineTasks';

export const TopologyPipelineTasks = React.memo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@
.pf-ri__topology-demo .pf-c-toolbar__item .pf-l-flex {
--pf-l-flex--FlexWrap: no-wrap;
}
.pf-ri__topology-demo .pf-c-toolbar__item .pf-l-flex > * {
--pf-l-flex--spacer: 8px;
}

.pf-ri__topology-demo .pf-c-toolbar__item .pf-c-form-control {
width: 85px;
}
Expand Down
Loading

0 comments on commit ee9b15c

Please sign in to comment.