Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(pf5): upgrade Topology View to Patternfly 5 #1308

Merged
merged 14 commits into from
Jul 31, 2024
Merged
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,14 @@
"webpack-merge": "^5.8.0"
},
"dependencies": {
"@patternfly/quickstarts": "^5.0.0",
"@patternfly/quickstarts": "^5.3.0",
"@patternfly/react-catalog-view-extension": "^5.0.0",
"@patternfly/react-charts": "^7.1.1",
"@patternfly/react-core": "^5.1.1",
"@patternfly/react-icons": "^5.1.1",
"@patternfly/react-styles": "^5.1.1",
"@patternfly/react-table": "^5.1.1",
"@patternfly/react-topology": "^5.1.0",
"@patternfly/react-charts": "^7.3.0",
"@patternfly/react-core": "^5.3.4",
"@patternfly/react-icons": "^5.3.2",
"@patternfly/react-styles": "^5.3.1",
"@patternfly/react-table": "^5.3.4",
"@patternfly/react-topology": "^5.2.0",
"@reduxjs/toolkit": "^1.9.3",
"@types/lodash": "^4.14.202",
"@types/react": "^17.0.69",
Expand Down
1 change: 0 additions & 1 deletion src/app/Shared/Services/api.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import _ from 'lodash';
import { Observable } from 'rxjs';

export type ApiVersion = 'v1' | 'v2' | 'v2.1' | 'v2.2' | 'v2.3' | 'v2.4' | 'v3' | 'beta';

// ======================================
// Common Resources
// ======================================
Expand Down
2 changes: 2 additions & 0 deletions src/app/Topology/Actions/NodeActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ export const ActionDropdown: React.FC<ActionDropdownProps> = ({
onClick={(e) => e.stopPropagation()}
toggle={toggle}
popperProps={popperProps}
onOpenChange={setActionOpen}
onOpenChangeKeys={['Escape']}
>
<DropdownList>{actions}</DropdownList>
</Dropdown>
Expand Down
3 changes: 1 addition & 2 deletions src/app/Topology/Actions/QuickSearchPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,6 @@ export const QuickSearchModal: React.FC<QuickSearchModalProps> = ({ isOpen, onCl
return (
<span>
For quickstarts on how to create these entities, visit <Link to={'/quickstarts'}>Quick Starts</Link>.
<Link to={'/quickstarts'}>Quick Starts</Link>.
</span>
);
}, []);
Expand All @@ -233,7 +232,7 @@ export const QuickSearchModal: React.FC<QuickSearchModalProps> = ({ isOpen, onCl
title={'Topology entity catalog'}
className={'topology__quick-search-modal'}
id={'topology-quick-search-modal'}
description={{ description }}
description={description}
>
<QuickSearchPanel />
</Modal>
Expand Down
4 changes: 2 additions & 2 deletions src/app/Topology/Entity/EntityAnnotations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const EntityAnnotations: React.FC<{ annotations?: Annotations; maxDisplay
return annotations
? Object.keys(annotations).map((groupK) => ({
groupLabel: groupK,
annotations: annotations[groupK].map((kv) => keyValueToString(kv)),
annotations: annotations[groupK].map(keyValueToString),
}))
: [];
}, [annotations]);
Expand All @@ -39,7 +39,7 @@ export const EntityAnnotations: React.FC<{ annotations?: Annotations; maxDisplay
<div className="entity-overview__displayed-annotations" key={group.groupLabel}>
<LabelGroup numLabels={maxDisplay} categoryName={group.groupLabel}>
{group.annotations.map((a) => (
<Label color="blue" key={a}>
<Label color="blue" key={a} textMaxWidth={'20ch'}>
{a}
</Label>
))}
Expand Down
2 changes: 1 addition & 1 deletion src/app/Topology/Entity/EntityKeyValues.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const EntityKeyValues: React.FC<EntityKeyValuesProps> = ({
<div className="entity-overview__displayed-keyvalues-wrapper" {...props}>
<LabelGroup numLabels={maxDisplay}>
{_transformedKv.map((l) => (
<Label color="blue" key={l}>
<Label color="blue" key={l} textMaxWidth={'20ch'}>
{l}
</Label>
))}
Expand Down
2 changes: 1 addition & 1 deletion src/app/Topology/GraphView/CustomGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const renderGroupIcon = (width: number, height: number): React.ReactNode

return (
<>
<circle cx={cx} cy={cy} r={contentSize / 2} fill="var(--pf-global--palette--white)" />
<circle cx={cx} cy={cy} r={contentSize / 2} fill="var(--pf-v5-global--palette--white)" />
<image
x={cx - mainContentSize / 2}
y={cy - mainContentSize / 2}
Expand Down
4 changes: 2 additions & 2 deletions src/app/Topology/GraphView/CustomNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const renderNodeIcon = (graphic: string, _data: TargetNode, element: Node

return (
<>
<circle cx={cx} cy={cy} r={contentSize / 2} fill="var(--pf-global--palette--white)" />
<circle cx={cx} cy={cy} r={contentSize / 2} fill="var(--pf-v5-global--palette--white)" />
{useAlt ? (
<g transform={`translate(${trueCx}, ${trueCy})`}>
<ContainerNodeIcon width={mainContentSize} height={mainContentSize} />
Expand Down Expand Up @@ -127,7 +127,7 @@ const CustomNode: React.FC<CustomNodeProps> = ({
scaleNode={(hover || contextMenuOpen) && detailsLevel !== ScaleDetailsLevel.high}
badge={showBadge ? nodeTypeToAbbr(data.nodeType) : undefined}
badgeColor={NODE_BADGE_COLOR}
badgeClassName={'topology__node-badge'}
badgeClassName={css('topology__node-badge')}
nodeStatus={showStatus ? nodeStatus : undefined}
showStatusBackground={!hover && detailsLevel === ScaleDetailsLevel.low}
truncateLength={RESOURCE_NAME_TRUNCATE_LENGTH}
Expand Down
18 changes: 15 additions & 3 deletions src/app/Topology/GraphView/NodeDecorator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ interface DecoratorProps {

export const ActiveRecordingDecorator: React.FC<DecoratorProps> = ({ element, quadrant, ...props }) => {
const data: TargetNode = element.getData();
const decoratorRef = React.useRef<SVGGElement>(null);
const { x, y } = getDefaultShapeDecoratorCenter(quadrant, element);
const { resources: recordings, error, loading } = useResources<ActiveRecording>(data, 'activeRecordings');

Expand All @@ -71,19 +72,30 @@ export const ActiveRecordingDecorator: React.FC<DecoratorProps> = ({ element, qu
}, [error, loading, runningRecs]);

return iconConfig ? (
<Tooltip {...props} content={iconConfig.tooltip} appendTo={portalRoot}>
<Decorator x={x} y={y} radius={DEFAULT_DECORATOR_RADIUS} showBackground icon={iconConfig.icon} />
<Tooltip content={iconConfig.tooltip} triggerRef={decoratorRef} appendTo={portalRoot}>
<Decorator
innerRef={decoratorRef}
{...props}
x={x}
y={y}
radius={DEFAULT_DECORATOR_RADIUS}
showBackground
icon={iconConfig.icon}
/>
</Tooltip>
) : null;
};

export const StatusDecorator: React.FC<DecoratorProps> = ({ element, quadrant, ...props }) => {
const decoratorRef = React.useRef<SVGGElement>(null);
const data: TargetNode = element.getData();
const [nodeStatus, extra] = getStatusTargetNode(data);
const { x, y } = getDefaultShapeDecoratorCenter(quadrant, element);
return nodeStatus ? (
<Tooltip {...props} content={extra?.title} appendTo={portalRoot}>
<Tooltip content={extra?.title} triggerRef={decoratorRef} appendTo={portalRoot}>
<Decorator
{...props}
innerRef={decoratorRef}
x={x}
y={y}
radius={DEFAULT_DECORATOR_RADIUS}
Expand Down
62 changes: 23 additions & 39 deletions src/app/Topology/GraphView/TopologyControlBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { css } from '@patternfly/react-styles';
import {
action,
createTopologyControlButtons,
Expand All @@ -27,43 +26,32 @@ import { CollapseIcon } from '../Shared/Components/CollapseIcon';
export interface TopologyControlBarProps {
visualization: Visualization;
noCollapse?: boolean;
className?: string;
}

export const TopologyControlBar: React.FC<TopologyControlBarProps> = ({
visualization,
noCollapse,
className,
...props
}) => {
export const TopologyControlBar: React.FC<TopologyControlBarProps> = ({ visualization, noCollapse, ...props }) => {
const buttonConfigs = React.useMemo(() => {
const base = [
...createTopologyControlButtons({
...defaultControlButtonsOptions,
zoomInCallback: action(() => {
visualization.getGraph().scaleBy(4 / 3);
}),
zoomInTip: 'Zoom in',
zoomInAriaLabel: 'Zoom in',
zoomOutCallback: action(() => {
visualization.getGraph().scaleBy(3 / 4);
}),
zoomOutTip: 'Zoom out',
zoomOutAriaLabel: 'Zoom out',
fitToScreenCallback: action(() => {
visualization.getGraph().fit(120);
}),
fitToScreenTip: 'Fit to screen',
fitToScreenAriaLabel: 'Fit to screen',
resetViewCallback: action(() => {
visualization.getGraph().reset();
visualization.getGraph().layout();
}),
resetViewTip: 'Reset view',
resetViewAriaLabel: 'Reset view',
legend: false,
const base = createTopologyControlButtons({
...defaultControlButtonsOptions,
zoomInCallback: action(() => {
// Zoom in by 4 / 3
visualization.getGraph().scaleBy(4 / 3);
}),
zoomOutCallback: action(() => {
// Zoom out by 3 / 4
visualization.getGraph().scaleBy(3 / 4);
}),
fitToScreenCallback: action(() => {
// Fit entire graph in the viewable area with an 120px margin
visualization.getGraph().fit(120);
}),
resetViewCallback: action(() => {
// Scale back to 1, and re-center the graph
visualization.getGraph().reset();
// Reset layout
visualization.getGraph().layout();
}),
];
legend: false,
});

if (!noCollapse) {
base.push({
Expand All @@ -82,9 +70,5 @@ export const TopologyControlBar: React.FC<TopologyControlBarProps> = ({
return base;
}, [visualization, noCollapse]);

return (
<div className={css('topology-control-bar', className)}>
<PFTopologyControlBar {...props} controlButtons={buttonConfigs} />
</div>
);
return <PFTopologyControlBar {...props} controlButtons={buttonConfigs} />;
};
18 changes: 13 additions & 5 deletions src/app/Topology/GraphView/TopologyGraphView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,27 +207,35 @@ export const TopologyGraphView: React.FC<TopologyGraphViewProps> = ({ transformC
contextMenu.style.display = 'none';
}
};

const showMenu = (e: MouseEvent) => {
e.preventDefault();

const contextMenu = document.getElementById('topology-context-menu');
if (contextMenu) {
// FIXME: This is a magic workaround.
// Position of context menu should be absolute to the document. Currently, it is relative to the container.
contextMenu.style.top = `${e.offsetY + 80}px`;
contextMenu.style.left = `${e.offsetX + 15}px`;
contextMenu.style.display = 'block';
contextMenu.style.top = `${e.clientY}px`;
contextMenu.style.left = `${e.clientX}px`;
}
};

document.addEventListener('click', hideMenu);

// Visualize surface needs time to intialize.
// Workaround: find drawer body which is already ready and tightly wraps the surface.
const container: HTMLElement | null = document.querySelector(
'#topology__visualization-container .pf-c-drawer__content',
'#topology__visualization-container .pf-v5-c-drawer__content',
);
if (container) {
container.addEventListener('contextmenu', showMenu);
}
document.addEventListener('click', hideMenu);
return () => document.removeEventListener('click', hideMenu);

return () => {
document.removeEventListener('click', hideMenu);
container?.removeEventListener('contextmenu', showMenu);
};
}, []);

const sidebar = React.useMemo(
Expand Down
2 changes: 1 addition & 1 deletion src/app/Topology/GraphView/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const DEFAULT_GROUP_PADDINGS = [

export const RESOURCE_NAME_TRUNCATE_LENGTH = 20;

export const NODE_BADGE_COLOR = 'var(--pf-global--palette--blue-500)';
export const NODE_BADGE_COLOR = 'var(--pf-v5-global--palette--blue-500)';

export const NODE_ICON_PADDING = 5;

Expand Down
Loading
Loading