[Kiali] Component library (#3987)
Signed-off-by: Alberto Gutierrez <aljesusg@gmail.com>
This commit is contained in:
parent
a1078e6793
commit
cdad6af6f5
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@backstage-community/plugin-kiali': minor
|
||||
---
|
||||
|
||||
Create kiali-react component library
|
||||
|
|
@ -46,7 +46,7 @@ metadata:
|
|||
- core
|
||||
- servicemesh
|
||||
annotations:
|
||||
kiali.io/provider: kubernetes
|
||||
kiali.io/provider: Kubernetes
|
||||
kiali.io/namespace: bookinfo
|
||||
spec:
|
||||
type: service
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
"pluginPackages": [
|
||||
"@backstage-community/plugin-kiali",
|
||||
"@backstage-community/plugin-kiali-backend",
|
||||
"@backstage-community/plugin-kiali-common"
|
||||
"@backstage-community/plugin-kiali-common",
|
||||
"@backstage-community/plugin-kiali-react"
|
||||
]
|
||||
},
|
||||
"exports": {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@
|
|||
"pluginPackages": [
|
||||
"@backstage-community/plugin-kiali",
|
||||
"@backstage-community/plugin-kiali-backend",
|
||||
"@backstage-community/plugin-kiali-common"
|
||||
"@backstage-community/plugin-kiali-common",
|
||||
"@backstage-community/plugin-kiali-react"
|
||||
]
|
||||
},
|
||||
"sideEffects": false,
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# @backstage/plugin-kiali-react
|
||||
|
||||
> Shared web components for the Kiali plugin in Backstage
|
||||
|
||||
This package provides a reusable set of UI components, utilities, and hooks designed to support the development of the [Kiali plugin for Backstage](https://github.com/backstage/community-plugins/blob/main/workspaces/kiali). It aims to keep the plugin codebase clean and modular, while enabling faster iteration and better maintainability.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- Reusable UI components tailored to Kiali data and workflows
|
||||
- Shared logic for interfacing with Istio/Kiali APIs
|
||||
- Designed to integrate seamlessly within Backstage plugins
|
||||
- Focused on developer experience and visual consistency
|
||||
|
||||
## 🧱 Usage
|
||||
|
||||
Import and use the shared components within your Kiali Backstage plugin:
|
||||
|
||||
```tsx
|
||||
import { TrafficGraph } from '@backstage/plugin-kiali-react';
|
||||
|
||||
<TrafficGraph model={} />;
|
||||
```
|
||||
|
||||
📁 Project Structure
|
||||
|
||||
``bash
|
||||
src/
|
||||
├── components/ # Shared React components
|
||||
├── hooks/ # Reusable custom hooks
|
||||
└── utils/ # Utility functions
|
||||
|
||||
```
|
||||
|
||||
|
||||
🤝 Contributing
|
||||
Contributions, issues, and feature requests are welcome! Please open a PR or issue in the main plugin repo if it relates to this shared library.
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { Model } from '@patternfly/react-topology';
|
||||
import { getAllThemes } from '@redhat-developer/red-hat-developer-hub-theme';
|
||||
import { TrafficGraph } from '../src';
|
||||
import graphModel from './__data__/bookinfoGraphModel.json';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin()
|
||||
.addThemes(getAllThemes())
|
||||
.addPage({
|
||||
element: <TrafficGraph model={graphModel as Model} />,
|
||||
title: 'TrafficGraph',
|
||||
path: '/kiali/trafficGraph',
|
||||
});
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"name": "@backstage-community/plugin-kiali-react",
|
||||
"version": "0.1.0",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"description": "Web library for the kiali plugin",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/community-plugins",
|
||||
"directory": "workspaces/kiali/plugins/kiali-react"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "web-library",
|
||||
"pluginId": "kiali",
|
||||
"pluginPackages": [
|
||||
"@backstage-community/plugin-kiali",
|
||||
"@backstage-community/plugin-kiali-backend",
|
||||
"@backstage-community/plugin-kiali-common",
|
||||
"@backstage-community/plugin-kiali-react"
|
||||
]
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
"build": "backstage-cli package build",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"clean": "backstage-cli package clean",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage-community/plugin-kiali-common": "workspace:^",
|
||||
"@backstage/core-plugin-api": "^1.10.6",
|
||||
"@material-ui/core": "^4.9.13",
|
||||
"@patternfly/react-core": "^5.1.1",
|
||||
"@patternfly/react-icons": "^5.1.1",
|
||||
"@patternfly/react-topology": "5.4.1",
|
||||
"typestyle": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.32.0",
|
||||
"@backstage/dev-utils": "^1.1.9",
|
||||
"@backstage/test-utils": "^1.7.7",
|
||||
"@redhat-developer/red-hat-developer-hub-theme": "0.4.0",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
## API Report File for "@backstage-community/plugin-kiali-react"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { JSX as JSX_2 } from 'react/jsx-runtime';
|
||||
import { Model } from '@patternfly/react-topology';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "TrafficGraph" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const TrafficGraph: (props: { model: Model }) => JSX_2.Element;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Badge, Tooltip, TooltipPosition } from '@patternfly/react-core';
|
||||
import { CSSProperties, default as React } from 'react';
|
||||
import { style } from 'typestyle';
|
||||
import { PFColors } from './PfColors';
|
||||
|
||||
export type PFBadgeType = {
|
||||
badge: string;
|
||||
tt?: React.ReactFragment;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
|
||||
// PF Badges used by Kiali, keep alphabetized
|
||||
// avoid duplicate badge letters, especially if they may appear on the same page
|
||||
export const PFBadges: { [key: string]: PFBadgeType } = Object.freeze({
|
||||
App: {
|
||||
badge: 'A',
|
||||
tt: 'Application',
|
||||
style: { backgroundColor: PFColors.Green500 },
|
||||
} as PFBadgeType,
|
||||
Adapter: { badge: 'A', tt: 'Adapter' } as PFBadgeType,
|
||||
AttributeManifest: { badge: 'AM', tt: 'Attribute Manifest' } as PFBadgeType,
|
||||
AuthorizationPolicy: {
|
||||
badge: 'AP',
|
||||
tt: 'Authorization Policy',
|
||||
} as PFBadgeType,
|
||||
Cluster: {
|
||||
badge: 'C',
|
||||
tt: 'Cluster',
|
||||
style: { backgroundColor: PFColors.Blue300 },
|
||||
} as PFBadgeType,
|
||||
ClusterRBACConfig: {
|
||||
badge: 'CRC',
|
||||
tt: 'Cluster RBAC Configuration',
|
||||
} as PFBadgeType,
|
||||
Container: {
|
||||
badge: 'C',
|
||||
tt: 'Container',
|
||||
style: { backgroundColor: PFColors.Blue300 },
|
||||
} as PFBadgeType,
|
||||
DestinationRule: { badge: 'DR', tt: 'Destination Rule' } as PFBadgeType,
|
||||
EnvoyFilter: { badge: 'EF', tt: 'Envoy Filter' } as PFBadgeType,
|
||||
ExternalService: { badge: 'ES', tt: 'External Service' } as PFBadgeType,
|
||||
FaultInjectionAbort: {
|
||||
badge: 'FI',
|
||||
tt: 'Fault Injection: Abort',
|
||||
style: { backgroundColor: PFColors.Purple500 },
|
||||
} as PFBadgeType,
|
||||
FaultInjectionDelay: {
|
||||
badge: 'FI',
|
||||
tt: 'Fault Injection: Delay',
|
||||
style: { backgroundColor: PFColors.Purple500 },
|
||||
} as PFBadgeType,
|
||||
FederatedService: { badge: 'FS', tt: 'Federated Service' } as PFBadgeType,
|
||||
Gateway: { badge: 'G', tt: 'Gateway' } as PFBadgeType,
|
||||
HTTPRoute: { badge: 'HTTP', tt: 'HTTPRoute' } as PFBadgeType,
|
||||
K8sGateway: { badge: 'G', tt: 'Gateway (K8s)' } as PFBadgeType,
|
||||
K8sHTTPRoute: { badge: 'HTTP', tt: 'HTTPRoute (K8s)' } as PFBadgeType,
|
||||
Handler: { badge: 'H', tt: 'Handler' },
|
||||
Host: { badge: 'H', tt: 'Host' },
|
||||
Instance: { badge: 'I', tt: 'Instance' },
|
||||
MeshPolicy: { badge: 'MP', tt: 'Mesh Policy' } as PFBadgeType,
|
||||
MirroredWorkload: {
|
||||
badge: 'MI',
|
||||
tt: 'Mirrored Workload',
|
||||
style: { backgroundColor: PFColors.Purple500 },
|
||||
} as PFBadgeType,
|
||||
Namespace: {
|
||||
badge: 'NS',
|
||||
tt: 'Namespace',
|
||||
style: { backgroundColor: PFColors.Green600 },
|
||||
} as PFBadgeType,
|
||||
Operation: { badge: 'O', tt: 'Operation' } as PFBadgeType,
|
||||
PeerAuthentication: { badge: 'PA', tt: 'Peer Authentication' } as PFBadgeType,
|
||||
Pod: {
|
||||
badge: 'P',
|
||||
tt: 'Pod',
|
||||
style: { backgroundColor: PFColors.Cyan300 },
|
||||
} as PFBadgeType,
|
||||
Policy: { badge: 'P', tt: 'Policy' } as PFBadgeType,
|
||||
RBACConfig: { badge: 'RC', tt: 'RBAC Configuration' } as PFBadgeType,
|
||||
RequestAuthentication: {
|
||||
badge: 'RA',
|
||||
tt: 'Request Authentication',
|
||||
} as PFBadgeType,
|
||||
RequestRetry: {
|
||||
badge: 'RR',
|
||||
tt: 'Request Retry',
|
||||
style: { backgroundColor: PFColors.Purple500 },
|
||||
} as PFBadgeType,
|
||||
RequestTimeout: {
|
||||
badge: 'RT',
|
||||
tt: 'Request Timeout',
|
||||
style: { backgroundColor: PFColors.Purple500 },
|
||||
} as PFBadgeType,
|
||||
Rule: { badge: 'R', tt: 'Rule' } as PFBadgeType,
|
||||
Service: {
|
||||
badge: 'S',
|
||||
tt: 'Service',
|
||||
style: { backgroundColor: PFColors.LightGreen500 },
|
||||
} as PFBadgeType,
|
||||
ServiceEntry: { badge: 'SE', tt: 'Service Entry' } as PFBadgeType,
|
||||
ServiceRole: { badge: 'SR', tt: 'Service Role' } as PFBadgeType,
|
||||
ServiceRoleBinding: {
|
||||
badge: 'SRB',
|
||||
tt: 'Service Role Binding',
|
||||
} as PFBadgeType,
|
||||
Sidecar: { badge: 'SC', tt: 'Istio Sidecar Proxy' } as PFBadgeType,
|
||||
WasmPlugin: { badge: 'WP', tt: 'Istio Wasm Plugin' } as PFBadgeType,
|
||||
Telemetry: { badge: 'TM', tt: 'Istio Telemetry' } as PFBadgeType,
|
||||
Template: { badge: 'T', tt: 'Template' } as PFBadgeType,
|
||||
Unknown: { badge: 'U', tt: 'Unknown' } as PFBadgeType,
|
||||
VirtualService: { badge: 'VS', tt: 'Virtual Service' } as PFBadgeType,
|
||||
Waypoint: { badge: 'W', tt: 'Waypoint proxy' } as PFBadgeType,
|
||||
Workload: {
|
||||
badge: 'W',
|
||||
tt: 'Workload',
|
||||
style: { backgroundColor: PFColors.Blue500 },
|
||||
} as PFBadgeType,
|
||||
WorkloadEntry: { badge: 'WE', tt: 'Workload Entry' } as PFBadgeType,
|
||||
WorkloadGroup: { badge: 'WG', tt: 'Workload Group' } as PFBadgeType,
|
||||
});
|
||||
|
||||
// This is styled for consistency with OpenShift Console. See console: public/components/_resource.scss
|
||||
export const kialiBadge = style({
|
||||
backgroundColor: PFColors.Badge,
|
||||
color: PFColors.White,
|
||||
borderRadius: '20px',
|
||||
flexShrink: 0,
|
||||
fontFamily: 'var(--pf-v5-global--FontFamily--text)',
|
||||
fontSize: 'var(--kiali-global--font-size)',
|
||||
lineHeight: '16px',
|
||||
marginRight: '4px',
|
||||
minWidth: '1.5em',
|
||||
padding: '1px 4px',
|
||||
textAlign: 'center',
|
||||
whiteSpace: 'nowrap',
|
||||
});
|
||||
|
||||
export const kialiBadgeSmall = style({
|
||||
backgroundColor: PFColors.Badge,
|
||||
color: PFColors.White,
|
||||
borderRadius: '20px',
|
||||
flexShrink: 0,
|
||||
fontFamily: 'var(--pf-v5-global--FontFamily--text)',
|
||||
fontSize: '12px',
|
||||
lineHeight: '13px',
|
||||
marginRight: '5px',
|
||||
minWidth: '1.3em',
|
||||
padding: '1px 3px',
|
||||
textAlign: 'center',
|
||||
whiteSpace: 'nowrap',
|
||||
});
|
||||
|
||||
type PFBadgeProps = {
|
||||
badge: PFBadgeType;
|
||||
isRead?: boolean;
|
||||
keyValue?: string;
|
||||
position?: string; // default=auto
|
||||
size?: 'global' | 'sm';
|
||||
style?: CSSProperties;
|
||||
tooltip?: React.ReactFragment;
|
||||
};
|
||||
|
||||
export class PFBadge extends React.PureComponent<PFBadgeProps> {
|
||||
render() {
|
||||
const key = this.props.keyValue || `pfbadge-${this.props.badge.badge}`;
|
||||
const ttKey = `tt-${key}`;
|
||||
const BadgeStyle = { ...this.props.badge.style, ...this.props.style };
|
||||
const tooltip = this.props.tooltip || this.props.badge.tt;
|
||||
const className = this.props.size === 'sm' ? kialiBadgeSmall : kialiBadge;
|
||||
|
||||
const badge = (
|
||||
<Badge
|
||||
className={className}
|
||||
id={key}
|
||||
isRead={this.props.isRead || false}
|
||||
key={key}
|
||||
style={BadgeStyle}
|
||||
>
|
||||
{this.props.badge.badge}
|
||||
</Badge>
|
||||
);
|
||||
|
||||
return !tooltip ? (
|
||||
badge
|
||||
) : (
|
||||
<Tooltip
|
||||
content={<>{tooltip}</>}
|
||||
id={ttKey}
|
||||
key={ttKey}
|
||||
position={
|
||||
(this.props.position as TooltipPosition) || TooltipPosition.auto
|
||||
}
|
||||
>
|
||||
{badge}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// PF colors, and moreover, use the defined color variables such that any changes made by PF are
|
||||
// picked up when the PF version is updated. The preferred, standard way, is in CSS styling. In
|
||||
// those cases we can directly let CSS resolve the PF var. So, whenever possible use the PFColors
|
||||
// enum below. In certain cases (like in cytoscape), we need the explicit hex value. In that case
|
||||
// we must actually get the computed value. We do this as soon as we get an initial document (in
|
||||
// StartupInitializer.tsx). In those cases use PFColorVals. Note that those values are not
|
||||
// available until they can be computed, so don't use them in constants or before they are
|
||||
// available.
|
||||
|
||||
// Colors used by Kiali for CSS styling
|
||||
export enum PFColors {
|
||||
Black100 = 'var(--pf-v5-global--palette--black-100)',
|
||||
Black150 = 'var(--pf-v5-global--palette--black-150)',
|
||||
Black200 = 'var(--pf-v5-global--palette--black-200)',
|
||||
Black300 = 'var(--pf-v5-global--palette--black-300)',
|
||||
Black400 = 'var(--pf-v5-global--palette--black-400)',
|
||||
Black500 = 'var(--pf-v5-global--palette--black-500)',
|
||||
Black600 = 'var(--pf-v5-global--palette--black-600)',
|
||||
Black700 = 'var(--pf-v5-global--palette--black-700)',
|
||||
Black800 = 'var(--pf-v5-global--palette--black-800)',
|
||||
Black900 = 'var(--pf-v5-global--palette--black-900)',
|
||||
Black1000 = 'var(--pf-v5-global--palette--black-1000)',
|
||||
Blue50 = 'var(--pf-v5-global--palette--blue-50)',
|
||||
Blue200 = 'var(--pf-v5-global--palette--blue-200)',
|
||||
Blue300 = 'var(--pf-v5-global--palette--blue-300)',
|
||||
Blue400 = 'var(--pf-v5-global--palette--blue-400)',
|
||||
Blue500 = 'var(--pf-v5-global--palette--blue-500)',
|
||||
Blue600 = 'var(--pf-v5-global--palette--blue-600)',
|
||||
Cyan300 = 'var(--pf-v5-global--palette--cyan-300)',
|
||||
Gold400 = 'var(--pf-v5-global--palette--gold-400)',
|
||||
Green300 = 'var(--pf-v5-global--palette--green-300)',
|
||||
Green400 = 'var(--pf-v5-global--palette--green-400)',
|
||||
Green500 = 'var(--pf-v5-global--palette--green-500)',
|
||||
Green600 = 'var(--pf-v5-global--palette--green-600)',
|
||||
LightBlue400 = 'var(--pf-v5-global--palette--light-blue-400)',
|
||||
LightGreen400 = 'var(--pf-v5-global--palette--light-green-400)',
|
||||
LightGreen500 = 'var(--pf-v5-global--palette--light-green-500)',
|
||||
Orange50 = 'var(--pf-v5-global--palette--orange-50)',
|
||||
Orange400 = 'var(--pf-v5-global--palette--orange-400)',
|
||||
Purple100 = 'var(--pf-v5-global--palette--purple-100)',
|
||||
Purple200 = 'var(--pf-v5-global--palette--purple-200)',
|
||||
Purple500 = 'var(--pf-v5-global--palette--purple-500)',
|
||||
Red50 = 'var(--pf-v5-global--palette--red-50)',
|
||||
Red100 = 'var(--pf-v5-global--palette--red-100)',
|
||||
Red200 = 'var(--pf-v5-global--palette--red-200)',
|
||||
Red500 = 'var(--pf-v5-global--palette--red-500)',
|
||||
White = 'var(--pf-v5-global--palette--white)',
|
||||
|
||||
// semantic kiali colors
|
||||
Active = 'var(--pf-v5-global--active-color--400)',
|
||||
ActiveText = 'var(--pf-v5-global--primary-color--200)',
|
||||
Badge = 'var(--pf-v5-global--palette--blue-300)',
|
||||
Replay = 'var(--pf-v5-global--active-color--300)',
|
||||
Link = 'var(--pf-v5-global--link--Color)',
|
||||
|
||||
// Health/Alert colors https://www.patternfly.org/v4/design-guidelines/styles/colors
|
||||
Danger = 'var(--pf-v5-global--danger-color--100)',
|
||||
Info = 'var(--pf-v5-global--info-color--100)',
|
||||
InfoBackground = 'var(--pf-v5-global--info-color--200)',
|
||||
Success = 'var(--pf-v5-global--success-color--100)',
|
||||
SuccessBackground = 'var(--pf-v5-global--success-color--200)',
|
||||
Warning = 'var(--pf-v5-global--warning-color--100)',
|
||||
|
||||
// chart-specific color values, for rates charts where 4xx is really Danger not Warning
|
||||
ChartDanger = 'var(--pf-v5-global--danger-color--300)',
|
||||
ChartOther = 'var(--pf-v5-global--palette-black-1000)',
|
||||
ChartWarning = 'var(--pf-v5-global--danger-color--100)',
|
||||
|
||||
// PF background colors (compatible with dark mode)
|
||||
BackgroundColor100 = 'var(--pf-v5-global--BackgroundColor--100)',
|
||||
BackgroundColor150 = 'var(--pf-v5-global--BackgroundColor--150)',
|
||||
BackgroundColor200 = 'var(--pf-v5-global--BackgroundColor--200)',
|
||||
|
||||
// PF standard colors (compatible with dark mode)
|
||||
Color100 = 'var(--pf-v5-global--Color--100)',
|
||||
Color200 = 'var(--pf-v5-global--Color--200)',
|
||||
ColorLight100 = 'var(--pf-v5-global--Color--light-100)',
|
||||
ColorLight200 = 'var(--pf-v5-global--Color--light-200)',
|
||||
ColorLight300 = 'var(--pf-v5-global--Color--light-300)',
|
||||
|
||||
// PF border colors (compatible with dark mode)
|
||||
BorderColor100 = 'var(--pf-v5-global--BorderColor--100)',
|
||||
BorderColor200 = 'var(--pf-v5-global--BorderColor--200)',
|
||||
BorderColor300 = 'var(--pf-v5-global--BorderColor--300)',
|
||||
BorderColorLight100 = 'var(--pf-v5-global--BorderColor--light-100)',
|
||||
}
|
||||
|
||||
// The hex string value of the PF CSS variable
|
||||
export type PFColorVal = string;
|
||||
|
||||
// Color values used by Kiali outside of CSS (i.e. when we must have the actual hex value)
|
||||
export type PFColorValues = {
|
||||
Black100: PFColorVal;
|
||||
Black150: PFColorVal;
|
||||
Black200: PFColorVal;
|
||||
Black300: PFColorVal;
|
||||
Black400: PFColorVal;
|
||||
Black500: PFColorVal;
|
||||
Black600: PFColorVal;
|
||||
Black700: PFColorVal;
|
||||
Black1000: PFColorVal;
|
||||
Blue50: PFColorVal;
|
||||
Blue300: PFColorVal;
|
||||
Blue600: PFColorVal;
|
||||
Red50: PFColorVal;
|
||||
Orange50: PFColorVal;
|
||||
Gold400: PFColorVal;
|
||||
Green400: PFColorVal;
|
||||
Purple200: PFColorVal;
|
||||
White: PFColorVal;
|
||||
|
||||
// Health/Alert colors https://www.patternfly.org/v4/design-guidelines/styles/colors
|
||||
Danger: PFColorVal;
|
||||
Success: PFColorVal;
|
||||
Warning: PFColorVal;
|
||||
|
||||
// PF colors (compatible with dark mode)
|
||||
BackgroundColor100: PFColorVal;
|
||||
BackgroundColor200: PFColorVal;
|
||||
|
||||
Color100: PFColorVal;
|
||||
Color200: PFColorVal;
|
||||
|
||||
BorderColor100: PFColorVal;
|
||||
BorderColor200: PFColorVal;
|
||||
BorderColor300: PFColorVal;
|
||||
};
|
||||
|
||||
export let PFColorVals: PFColorValues;
|
||||
|
||||
/*
|
||||
Extract color from var
|
||||
Input : var(--pf-v5-global--palette--black-100)
|
||||
Output: --pf-v5-global--palette--black-100
|
||||
|
||||
- In case there is not var then return the same input
|
||||
*/
|
||||
const getColor = (val: string) => {
|
||||
return val.indexOf('var(') === 0 ? val.split('(').pop()!.split(')')[0] : val;
|
||||
};
|
||||
|
||||
export const setPFColorVals = (element: Element) => {
|
||||
PFColorVals = {
|
||||
// color values used by kiali
|
||||
Black100: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Black100)),
|
||||
Black150: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Black150)),
|
||||
Black200: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Black200)),
|
||||
Black300: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Black300)),
|
||||
Black400: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Black400)),
|
||||
Black500: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Black500)),
|
||||
Black600: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Black600)),
|
||||
Black700: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Black700)),
|
||||
Black1000: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Black1000)),
|
||||
Blue50: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Blue50)),
|
||||
Blue300: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Blue300)),
|
||||
Blue600: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Blue600)),
|
||||
Red50: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Red50)),
|
||||
Orange50: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Orange50)),
|
||||
Gold400: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Gold400)),
|
||||
Green400: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Green400)),
|
||||
Purple200: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Purple200)),
|
||||
White: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.White)),
|
||||
|
||||
// status color values used by kiali
|
||||
Danger: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Danger)),
|
||||
Success: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Success)),
|
||||
Warning: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Warning)),
|
||||
|
||||
// PF colors (compatible with dark mode)
|
||||
BackgroundColor100: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.BackgroundColor100)),
|
||||
BackgroundColor200: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.BackgroundColor200)),
|
||||
|
||||
Color100: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Color100)),
|
||||
Color200: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.Color200)),
|
||||
|
||||
BorderColor100: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.BorderColor100)),
|
||||
BorderColor200: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.BorderColor200)),
|
||||
BorderColor300: window
|
||||
.getComputedStyle(element)
|
||||
.getPropertyValue(getColor(PFColors.BorderColor300)),
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
ColaLayout,
|
||||
Graph,
|
||||
Layout,
|
||||
LayoutFactory,
|
||||
} from '@patternfly/react-topology';
|
||||
import { KialiDagreLayout } from './layouts/KialiDagreLayout';
|
||||
|
||||
export const KialiLayoutFactory: LayoutFactory = (
|
||||
type: string,
|
||||
graph: Graph,
|
||||
): Layout => {
|
||||
switch (type) {
|
||||
case 'Dagre':
|
||||
return new KialiDagreLayout(graph, {
|
||||
linkDistance: 40,
|
||||
nodeDistance: 25,
|
||||
marginx: undefined,
|
||||
marginy: undefined,
|
||||
ranker: 'network-simplex',
|
||||
rankdir: 'LR',
|
||||
});
|
||||
default:
|
||||
return new ColaLayout(graph, { layoutOnDrag: false });
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
action,
|
||||
createTopologyControlButtons,
|
||||
defaultControlButtonsOptions,
|
||||
Model,
|
||||
TopologyControlBar,
|
||||
TopologyView,
|
||||
Visualization,
|
||||
VisualizationProvider,
|
||||
VisualizationSurface,
|
||||
} from '@patternfly/react-topology';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { KialiComponentFactory } from './factories/KialiComponentFactory';
|
||||
import { KialiLayoutFactory } from './factories/KialiLayoutFactory';
|
||||
|
||||
const getVisualization = (): Visualization => {
|
||||
const vis = new Visualization();
|
||||
|
||||
vis.registerLayoutFactory(KialiLayoutFactory);
|
||||
vis.registerComponentFactory(KialiComponentFactory);
|
||||
vis.setFitToScreenOnLayout(true);
|
||||
|
||||
return vis;
|
||||
};
|
||||
|
||||
export const TrafficGraph = (props: { model: Model }) => {
|
||||
const [controller] = useState(getVisualization());
|
||||
useEffect(() => {
|
||||
controller.fromModel(props.model, false);
|
||||
}, [props.model, controller]);
|
||||
|
||||
return (
|
||||
<TopologyView
|
||||
controlBar={
|
||||
<TopologyControlBar
|
||||
controlButtons={createTopologyControlButtons({
|
||||
...defaultControlButtonsOptions,
|
||||
zoomInCallback: action(() => {
|
||||
controller.getGraph().scaleBy(4 / 3);
|
||||
}),
|
||||
zoomOutCallback: action(() => {
|
||||
controller.getGraph().scaleBy(0.75);
|
||||
}),
|
||||
fitToScreenCallback: action(() => {
|
||||
controller.getGraph().fit(80);
|
||||
}),
|
||||
resetViewCallback: action(() => {
|
||||
controller.getGraph().reset();
|
||||
controller.getGraph().layout();
|
||||
}),
|
||||
legend: false,
|
||||
zoomInAriaLabel: '',
|
||||
zoomOutAriaLabel: '',
|
||||
fitToScreenAriaLabel: '',
|
||||
resetViewAriaLabel: '',
|
||||
zoomInTip: '',
|
||||
zoomOutTip: '',
|
||||
fitToScreenTip: '',
|
||||
resetViewTip: '',
|
||||
})}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<VisualizationProvider controller={controller}>
|
||||
<VisualizationSurface state={props.model} />
|
||||
</VisualizationProvider>
|
||||
</TopologyView>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
ComponentFactory,
|
||||
DefaultGroup,
|
||||
GraphComponent,
|
||||
ModelKind,
|
||||
withPanZoom,
|
||||
withSelection,
|
||||
} from '@patternfly/react-topology';
|
||||
import { KialiEdge } from '../styles/KialiEdge';
|
||||
import { KialiNode } from '../styles/KialiNode';
|
||||
|
||||
export const KialiComponentFactory: ComponentFactory = (
|
||||
kind: ModelKind,
|
||||
type: string,
|
||||
) => {
|
||||
switch (type) {
|
||||
case 'group':
|
||||
return DefaultGroup;
|
||||
default:
|
||||
switch (kind) {
|
||||
case ModelKind.graph:
|
||||
return withPanZoom()(GraphComponent);
|
||||
case ModelKind.node:
|
||||
return KialiNode as any;
|
||||
case ModelKind.edge:
|
||||
return withSelection({ multiSelect: false, controlled: false })(
|
||||
KialiEdge as any,
|
||||
);
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
ColaLayout,
|
||||
Graph,
|
||||
Layout,
|
||||
LayoutFactory,
|
||||
} from '@patternfly/react-topology';
|
||||
import { KialiDagreLayout } from '../layouts/KialiDagreLayout';
|
||||
|
||||
export const KialiLayoutFactory: LayoutFactory = (
|
||||
type: string,
|
||||
graph: Graph,
|
||||
): Layout => {
|
||||
switch (type) {
|
||||
case 'Dagre':
|
||||
return new KialiDagreLayout(graph, {
|
||||
linkDistance: 40,
|
||||
nodeDistance: 25,
|
||||
marginx: undefined,
|
||||
marginy: undefined,
|
||||
ranker: 'network-simplex',
|
||||
rankdir: 'LR',
|
||||
});
|
||||
default:
|
||||
return new ColaLayout(graph, { layoutOnDrag: false });
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './TrafficGraph';
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { DagreLayout } from '@patternfly/react-topology';
|
||||
|
||||
export class KialiDagreLayout extends DagreLayout {
|
||||
override updateEdgeBendpoints() {
|
||||
// Your implementation here
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
DefaultEdge,
|
||||
Edge,
|
||||
observer,
|
||||
ScaleDetailsLevel,
|
||||
WithSelectionProps,
|
||||
} from '@patternfly/react-topology';
|
||||
import useDetailsLevel from '@patternfly/react-topology/dist/esm/hooks/useDetailsLevel';
|
||||
import { default as React } from 'react';
|
||||
import { classes, style } from 'typestyle';
|
||||
import { PFColors } from '../../../components/Pf/PfColors';
|
||||
|
||||
// This is our styled edge component registered in stylesComponentFactory.tsx. It is responsible for adding customizations that then get passed down to DefaultEdge. The current customizations:
|
||||
// data.pathStyle?: React.CSSProperties // additional CSS stylings for the edge/path (not the endpoint).
|
||||
// data.isFind?: boolean // adds graph-find overlay
|
||||
// data.isUnhighlighted?: boolean // adds unhighlight effects
|
||||
// data.hasSpans?: Span[] // adds trace overlay
|
||||
// add showTag prop and show scaled tag on hover (when showTag is false)
|
||||
// support [lock] icons on edge tags
|
||||
|
||||
const ColorFind = PFColors.Gold400;
|
||||
const ColorSpan = PFColors.Purple200;
|
||||
const OverlayOpacity = 0.3;
|
||||
const OverlayWidth = 30;
|
||||
|
||||
type StyleEdgeProps = {
|
||||
element: Edge;
|
||||
} & WithSelectionProps;
|
||||
|
||||
const tagClass = style({
|
||||
fontFamily: 'Verdana,Arial,Helvetica,sans-serif,pficon',
|
||||
});
|
||||
|
||||
const StyleEdgeComponent: React.FC<StyleEdgeProps> = ({ element, ...rest }) => {
|
||||
const data = element.getData();
|
||||
const detailsLevel = useDetailsLevel();
|
||||
|
||||
const cssClasses: string[] = [];
|
||||
|
||||
// Change edge color according to the pathStyle
|
||||
const edgeClass = style({
|
||||
$nest: {
|
||||
'& .pf-topology__edge__link': data.pathStyle,
|
||||
},
|
||||
});
|
||||
cssClasses.push(edgeClass);
|
||||
|
||||
const edgeHoverClass = style({
|
||||
$nest: {
|
||||
'& .pf-topology__edge.pf-m-hover': {
|
||||
$nest: {
|
||||
'& .pf-topology__edge__link, & .pf-topology-connector-arrow':
|
||||
data.pathStyle,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
cssClasses.push(edgeHoverClass);
|
||||
|
||||
// Change connector color according to the pathStyle
|
||||
const connectorClass = style({
|
||||
$nest: {
|
||||
'& .pf-topology-connector-arrow': {
|
||||
stroke: data.pathStyle.stroke,
|
||||
fill: data.pathStyle.stroke,
|
||||
},
|
||||
},
|
||||
});
|
||||
cssClasses.push(connectorClass);
|
||||
|
||||
const edgeConnectorArrowHoverStyles = style({
|
||||
$nest: {
|
||||
'& .pf-topology__edge.pf-m-hover': {
|
||||
$nest: {
|
||||
'& .pf-topology-connector-arrow': {
|
||||
stroke: data.pathStyle.stroke,
|
||||
fill: data.pathStyle.stroke,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
cssClasses.push(edgeConnectorArrowHoverStyles);
|
||||
|
||||
// If has spans, add the span overlay
|
||||
if (data.hasSpans) {
|
||||
const spansClass = style({
|
||||
$nest: {
|
||||
'& .pf-topology__edge__background': {
|
||||
strokeWidth: OverlayWidth,
|
||||
stroke: ColorSpan,
|
||||
strokeOpacity: OverlayOpacity,
|
||||
},
|
||||
},
|
||||
});
|
||||
cssClasses.push(spansClass);
|
||||
// If isHighlighted, add the highlight overlay
|
||||
} else if (data.isFind) {
|
||||
const findClass = style({
|
||||
$nest: {
|
||||
'& .pf-topology__edge__background': {
|
||||
strokeWidth: OverlayWidth,
|
||||
stroke: ColorFind,
|
||||
strokeOpacity: OverlayOpacity,
|
||||
},
|
||||
},
|
||||
});
|
||||
cssClasses.push(findClass);
|
||||
}
|
||||
|
||||
// Set animation duration velocity
|
||||
if (data.animationDuration) {
|
||||
const animationClass = style({
|
||||
$nest: {
|
||||
'& .pf-topology__edge__link': {
|
||||
animationDuration: `${data.animationDuration}s`,
|
||||
},
|
||||
},
|
||||
});
|
||||
cssClasses.push(animationClass);
|
||||
}
|
||||
|
||||
// Set the path style when unhighlighted (opacity)
|
||||
let opacity = 1;
|
||||
if (data.isUnhighlighted) {
|
||||
opacity = 0.1;
|
||||
}
|
||||
|
||||
const passedData = React.useMemo(() => {
|
||||
const newData = { ...data };
|
||||
if (detailsLevel !== ScaleDetailsLevel.high) {
|
||||
newData.showTag = false;
|
||||
}
|
||||
Object.keys(newData).forEach(key => {
|
||||
if (newData[key] === undefined) {
|
||||
delete newData[key];
|
||||
}
|
||||
});
|
||||
return newData;
|
||||
}, [data, detailsLevel]);
|
||||
|
||||
return (
|
||||
<g style={{ opacity: opacity }}>
|
||||
<DefaultEdge
|
||||
className={classes(...cssClasses)}
|
||||
element={element}
|
||||
tagClass={tagClass}
|
||||
{...rest}
|
||||
{...passedData}
|
||||
/>
|
||||
</g>
|
||||
);
|
||||
};
|
||||
|
||||
export const KialiEdge = observer(StyleEdgeComponent);
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { CubesIcon } from '@patternfly/react-icons';
|
||||
import {
|
||||
DefaultGroup,
|
||||
Node,
|
||||
ScaleDetailsLevel,
|
||||
ShapeProps,
|
||||
WithSelectionProps,
|
||||
} from '@patternfly/react-topology';
|
||||
import useDetailsLevel from '@patternfly/react-topology/dist/esm/hooks/useDetailsLevel';
|
||||
import { default as React } from 'react';
|
||||
import { PFColors } from '../../../components/Pf/PfColors';
|
||||
|
||||
const ICON_PADDING = 20;
|
||||
|
||||
export enum DataTypes {
|
||||
Default,
|
||||
}
|
||||
|
||||
type StyleGroupProps = {
|
||||
element: Node;
|
||||
collapsible: boolean;
|
||||
collapsedWidth?: number;
|
||||
collapsedHeight?: number;
|
||||
onCollapseChange?: (group: Node, collapsed: boolean) => void;
|
||||
getCollapsedShape?: (node: Node) => React.FC<ShapeProps>;
|
||||
collapsedShadowOffset?: number; // defaults to 10
|
||||
} & WithSelectionProps;
|
||||
|
||||
export function KialiGroup({
|
||||
element,
|
||||
collapsedWidth = 75,
|
||||
collapsedHeight = 75,
|
||||
...rest
|
||||
}: StyleGroupProps) {
|
||||
const data = element.getData();
|
||||
const detailsLevel = useDetailsLevel();
|
||||
|
||||
const passedData = React.useMemo(() => {
|
||||
const newData = { ...data };
|
||||
Object.keys(newData).forEach(key => {
|
||||
if (newData[key] === undefined) {
|
||||
delete newData[key];
|
||||
}
|
||||
});
|
||||
return newData;
|
||||
}, [data]);
|
||||
|
||||
if (data.isFocused) {
|
||||
element.setData({ ...data, isFocused: false });
|
||||
}
|
||||
|
||||
const renderIcon = (): React.ReactNode => {
|
||||
const iconSize =
|
||||
Math.min(collapsedWidth, collapsedHeight) - ICON_PADDING * 2;
|
||||
const Component = CubesIcon;
|
||||
|
||||
return (
|
||||
<g
|
||||
transform={`translate(${(collapsedWidth - iconSize) / 2}, ${
|
||||
(collapsedHeight - iconSize) / 2
|
||||
})`}
|
||||
>
|
||||
<Component
|
||||
style={{ color: PFColors.Color200 }}
|
||||
width={iconSize}
|
||||
height={iconSize}
|
||||
/>
|
||||
</g>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<g>
|
||||
<DefaultGroup
|
||||
element={element}
|
||||
collapsedWidth={collapsedWidth}
|
||||
collapsedHeight={collapsedHeight}
|
||||
showLabel={detailsLevel === ScaleDetailsLevel.high}
|
||||
{...rest}
|
||||
{...passedData}
|
||||
>
|
||||
{element.isCollapsed() ? renderIcon() : null}
|
||||
</DefaultGroup>
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { KeyIcon, TopologyIcon } from '@patternfly/react-icons';
|
||||
import {
|
||||
DefaultNode,
|
||||
getShapeComponent,
|
||||
Node,
|
||||
NodeShape,
|
||||
observer,
|
||||
ScaleDetailsLevel,
|
||||
useHover,
|
||||
WithSelectionProps,
|
||||
} from '@patternfly/react-topology';
|
||||
import useDetailsLevel from '@patternfly/react-topology/dist/esm/hooks/useDetailsLevel';
|
||||
import { default as React } from 'react';
|
||||
import { style } from 'typestyle';
|
||||
import { PFColors } from '../../../components/Pf/PfColors';
|
||||
|
||||
// This is the registered Node component override that utilizes our customized Node.tsx component.
|
||||
|
||||
type StyleNodeProps = {
|
||||
element: Node;
|
||||
} & WithSelectionProps;
|
||||
|
||||
const renderIcon = (element: Node): React.ReactNode => {
|
||||
let Component: React.ComponentClass<React.ComponentProps<any>> | undefined;
|
||||
const data = element.getData();
|
||||
const isInaccessible = data.isInaccessible;
|
||||
const isServiceEntry = data.isServiceEntry;
|
||||
const isBox = data.isBox;
|
||||
if (isInaccessible && !isServiceEntry && !isBox) {
|
||||
Component = KeyIcon;
|
||||
}
|
||||
const isOutside = data.isOutside;
|
||||
if (isOutside && !isBox) {
|
||||
Component = TopologyIcon;
|
||||
}
|
||||
|
||||
// this blurb taken from PFT demo StyleNode.tsx, not sure if it's required
|
||||
// vv
|
||||
const { width, height } = element.getDimensions();
|
||||
const shape = element.getNodeShape();
|
||||
const iconSize =
|
||||
(shape === NodeShape.trapezoid ? width : Math.min(width, height)) -
|
||||
(shape === NodeShape.stadium ? 5 : 20) * 2;
|
||||
// ^^
|
||||
|
||||
return Component ? (
|
||||
<g
|
||||
transform={`translate(${(width - iconSize) / 2}, ${
|
||||
(height - iconSize) / 2
|
||||
})`}
|
||||
>
|
||||
<Component width={iconSize} height={iconSize} />
|
||||
</g>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
|
||||
const StyleNodeComponent: React.FC<StyleNodeProps> = ({ element, ...rest }) => {
|
||||
const data = element.getData();
|
||||
const detailsLevel = useDetailsLevel();
|
||||
const [hover, hoverRef] = useHover();
|
||||
const ShapeComponent = getShapeComponent(element);
|
||||
|
||||
const ColorFind = PFColors.Gold400;
|
||||
const ColorSpan = PFColors.Purple200;
|
||||
const OverlayOpacity = 0.3;
|
||||
const OverlayWidth = 40;
|
||||
|
||||
const traceOverlayStyle = style({
|
||||
strokeWidth: OverlayWidth,
|
||||
stroke: ColorSpan,
|
||||
strokeOpacity: OverlayOpacity,
|
||||
});
|
||||
|
||||
const findOverlayStyle = style({
|
||||
strokeWidth: OverlayWidth,
|
||||
stroke: ColorFind,
|
||||
strokeOpacity: OverlayOpacity,
|
||||
});
|
||||
|
||||
// Set the path style when unhighlighted (opacity)
|
||||
let opacity = 1;
|
||||
if (data.isUnhighlighted) {
|
||||
opacity = 0.1;
|
||||
}
|
||||
|
||||
const passedData = React.useMemo(() => {
|
||||
const newData = { ...data };
|
||||
if (detailsLevel !== ScaleDetailsLevel.high) {
|
||||
newData.tag = undefined;
|
||||
}
|
||||
Object.keys(newData).forEach(key => {
|
||||
if (newData[key] === undefined) {
|
||||
delete newData[key];
|
||||
}
|
||||
});
|
||||
return newData;
|
||||
}, [data, detailsLevel]);
|
||||
|
||||
const { width, height } = element.getDimensions();
|
||||
|
||||
return (
|
||||
<g
|
||||
data-test={`node-${data.getLabel}`}
|
||||
style={{ opacity: opacity }}
|
||||
ref={hoverRef as any}
|
||||
>
|
||||
{data.hasSpans && (
|
||||
<ShapeComponent
|
||||
className={traceOverlayStyle}
|
||||
width={width}
|
||||
height={height}
|
||||
element={element}
|
||||
/>
|
||||
)}
|
||||
{data.isFind && (
|
||||
<ShapeComponent
|
||||
className={findOverlayStyle}
|
||||
width={width}
|
||||
height={height}
|
||||
element={element}
|
||||
/>
|
||||
)}
|
||||
<DefaultNode
|
||||
element={element}
|
||||
{...rest}
|
||||
{...passedData}
|
||||
attachments={
|
||||
hover || detailsLevel === ScaleDetailsLevel.high
|
||||
? data.attachments
|
||||
: undefined
|
||||
}
|
||||
scaleLabel={hover && detailsLevel !== ScaleDetailsLevel.high}
|
||||
// scaleNode={hover && detailsLevel === ScaleDetailsLevel.low}
|
||||
showLabel={hover || detailsLevel === ScaleDetailsLevel.high}
|
||||
showStatusBackground={detailsLevel === ScaleDetailsLevel.low}
|
||||
>
|
||||
{(hover || detailsLevel !== ScaleDetailsLevel.low) &&
|
||||
renderIcon(element)}
|
||||
</DefaultNode>
|
||||
</g>
|
||||
);
|
||||
};
|
||||
|
||||
export const KialiNode = observer(StyleNodeComponent);
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
DecoratedGraphEdgeData,
|
||||
Span,
|
||||
} from '@backstage-community/plugin-kiali-common/types';
|
||||
import {
|
||||
EdgeTerminalType,
|
||||
GraphElement,
|
||||
NodeStatus,
|
||||
} from '@patternfly/react-topology';
|
||||
|
||||
export type EdgeData = DecoratedGraphEdgeData & {
|
||||
endTerminalType: EdgeTerminalType;
|
||||
hasSpans?: Span[];
|
||||
isFind?: boolean;
|
||||
isHighlighted?: boolean;
|
||||
isSelected?: boolean;
|
||||
isUnhighlighted?: boolean;
|
||||
onHover?: (element: GraphElement, isMouseIn: boolean) => void;
|
||||
pathStyle?: React.CSSProperties;
|
||||
tag?: string;
|
||||
tagStatus?: NodeStatus;
|
||||
};
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import type { Namespace } from '@backstage-community/plugin-kiali-common/types';
|
||||
import {
|
||||
EdgeLabelMode,
|
||||
GraphType,
|
||||
TrafficRate,
|
||||
} from '@backstage-community/plugin-kiali-common/types';
|
||||
|
||||
export type GraphPFSettings = {
|
||||
activeNamespaces: Namespace[];
|
||||
edgeLabels: EdgeLabelMode[];
|
||||
graphType: GraphType;
|
||||
showOutOfMesh: boolean;
|
||||
showSecurity: boolean;
|
||||
showVirtualServices: boolean;
|
||||
trafficRates: TrafficRate[];
|
||||
};
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
BoxByType,
|
||||
DecoratedGraphNodeData,
|
||||
DEGRADED,
|
||||
FAILURE,
|
||||
NodeType,
|
||||
} from '@backstage-community/plugin-kiali-common/types';
|
||||
import {
|
||||
BadgeLocation,
|
||||
GraphElement,
|
||||
LabelPosition,
|
||||
NodeShape,
|
||||
NodeStatus,
|
||||
} from '@patternfly/react-topology';
|
||||
|
||||
export type NodeData = DecoratedGraphNodeData & {
|
||||
// These are node.data fields that have an impact on the PFT rendering of the node.
|
||||
// TODO: Is there an actual type defined for these in PFT?
|
||||
attachments?: React.ReactNode; // ie. decorators
|
||||
badge?: string;
|
||||
badgeBorderColor?: string;
|
||||
badgeClassName?: string;
|
||||
badgeColor?: string;
|
||||
badgeLocation?: BadgeLocation;
|
||||
badgeTextColor?: string;
|
||||
column?: number;
|
||||
component?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
isFind?: boolean;
|
||||
isHighlighted?: boolean;
|
||||
isSelected?: boolean;
|
||||
isUnhighlighted?: boolean;
|
||||
labelIcon?: React.ReactNode;
|
||||
labelIconClass?: string;
|
||||
labelIconPadding?: number;
|
||||
labelPosition?: LabelPosition;
|
||||
marginX?: number;
|
||||
onHover?: (element: GraphElement, isMouseIn: boolean) => void;
|
||||
row?: number;
|
||||
secondaryLabel?: string;
|
||||
setLocation?: boolean;
|
||||
showContextMenu?: boolean;
|
||||
showStatusDecorator?: boolean;
|
||||
statusDecoratorTooltip?: React.ReactNode;
|
||||
truncateLength?: number;
|
||||
x?: number;
|
||||
y?: number;
|
||||
};
|
||||
|
||||
export const getNodeStatus = (data: NodeData): NodeStatus => {
|
||||
if ((data.isBox && data.isBox !== BoxByType.APP) || data.isIdle) {
|
||||
return NodeStatus.default;
|
||||
}
|
||||
|
||||
switch (data.healthStatus) {
|
||||
case DEGRADED.name:
|
||||
return NodeStatus.warning;
|
||||
case FAILURE.name:
|
||||
return NodeStatus.danger;
|
||||
default:
|
||||
return NodeStatus.success;
|
||||
}
|
||||
};
|
||||
|
||||
export const getNodeShape = (data: NodeData): NodeShape => {
|
||||
switch (data.nodeType) {
|
||||
case NodeType.AGGREGATE:
|
||||
return NodeShape.hexagon;
|
||||
case NodeType.APP:
|
||||
return NodeShape.rect;
|
||||
case NodeType.SERVICE:
|
||||
return data.isServiceEntry ? NodeShape.trapezoid : NodeShape.rhombus;
|
||||
case NodeType.WORKLOAD:
|
||||
return NodeShape.circle;
|
||||
default:
|
||||
return NodeShape.ellipse;
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { NodeModel } from '@patternfly/react-topology';
|
||||
|
||||
export type NodeMap = Map<string, NodeModel>;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// The index file in ./components/ is typically responsible for selecting
|
||||
// which components are public API and should be exported from the package.
|
||||
|
||||
export * from './TrafficGraph';
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './components';
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { style } from 'typestyle';
|
||||
import { NestedCSSProperties } from 'typestyle/lib/types';
|
||||
import { PFColors } from '../components/Pf/PfColors';
|
||||
|
||||
/*
|
||||
* 70px is the height of the bottom toolbar (save, reload and cancel buttons)
|
||||
* 100px is the top margin of the yaml editor (Adjusted with RenderComponentScroll).
|
||||
* So, substracting 170px from the tab content height.
|
||||
*/
|
||||
export const istioAceEditorStyle = style({
|
||||
'--kiali-yaml-editor-height':
|
||||
'calc(var(--kiali-details-pages-tab-content-height) - 170px)',
|
||||
position: 'relative',
|
||||
minHeight: '200px',
|
||||
border: `1px solid ${PFColors.BorderColor200}`,
|
||||
fontSize: 'var(--kiali-global--font-size) !important',
|
||||
$nest: {
|
||||
'& div.ace_gutter-cell.ace_info': {
|
||||
backgroundImage: 'none',
|
||||
$nest: {
|
||||
'&::before': {
|
||||
content: `'\\E92b'`,
|
||||
fontFamily: 'pficon',
|
||||
left: '5px',
|
||||
position: 'absolute',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as NestedCSSProperties);
|
||||
|
||||
export const istioValidationErrorStyle = style({
|
||||
position: 'absolute',
|
||||
background: 'rgba(204, 0, 0, 0.5)',
|
||||
});
|
||||
|
||||
export const istioValidationWarningStyle = style({
|
||||
position: 'absolute',
|
||||
background: 'rgba(236, 122, 8, 0.5)',
|
||||
});
|
||||
|
||||
export const istioValidationInfoStyle = style({
|
||||
position: 'absolute',
|
||||
background: PFColors.ColorLight300,
|
||||
});
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { style } from 'typestyle';
|
||||
import { NestedCSSProperties } from 'typestyle/lib/types';
|
||||
import { PFColors } from '../components/Pf/PfColors';
|
||||
|
||||
export const containerStyle = style({
|
||||
overflow: 'auto',
|
||||
});
|
||||
|
||||
// this emulates Select component .pf-v5-c-select__menu
|
||||
export const menuStyle = style({
|
||||
fontSize: 'var(--kiali-global--font-size)',
|
||||
});
|
||||
|
||||
// this emulates Select component .pf-v5-c-select__menu but w/o cursor manipulation
|
||||
export const menuEntryStyle = style({
|
||||
display: 'inline-block',
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
// this emulates Select component .pf-v5-c-select__menu-group-title but with less bottom padding to conserve space
|
||||
export const titleStyle = style({
|
||||
padding: '0.5rem 1rem 0 1rem',
|
||||
fontWeight: 700,
|
||||
color: PFColors.Color200,
|
||||
});
|
||||
|
||||
const itemStyle: NestedCSSProperties = {
|
||||
alignItems: 'center',
|
||||
whiteSpace: 'nowrap',
|
||||
margin: 0,
|
||||
padding: '0.375rem 1rem',
|
||||
display: 'inline-block',
|
||||
};
|
||||
|
||||
// this emulates Select component .pf-v5-c-select__menu-item but with less vertical padding to conserve space
|
||||
export const itemStyleWithoutInfo = style(itemStyle);
|
||||
|
||||
// this emulates Select component .pf-v5-c-select__menu-item but with less vertical padding to conserve space
|
||||
export const itemStyleWithInfo = style({
|
||||
...itemStyle,
|
||||
padding: '0.375rem 0 0.375rem 1rem',
|
||||
});
|
||||
|
||||
export const infoStyle = style({
|
||||
marginLeft: '0.375rem',
|
||||
});
|
||||
|
||||
export const groupMenuStyle = style({
|
||||
textAlign: 'left',
|
||||
});
|
||||
|
||||
export const kebabToggleStyle = style({
|
||||
paddingLeft: '0.5rem',
|
||||
paddingRight: '0.5rem',
|
||||
});
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { style } from 'typestyle';
|
||||
import { PFColors } from '../components/Pf/PfColors';
|
||||
|
||||
export const healthIndicatorStyle = style({
|
||||
$nest: {
|
||||
'& .pf-v5-c-tooltip__content': {
|
||||
borderWidth: '1px',
|
||||
textAlign: 'left',
|
||||
},
|
||||
|
||||
'& .pf-v5-c-content ul': {
|
||||
marginBottom: 'var(--pf-v5-c-content--ul--MarginTop)',
|
||||
marginTop: 0,
|
||||
color: PFColors.Color100,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { style } from 'typestyle';
|
||||
import { PFColors } from '../components/Pf/PfColors';
|
||||
|
||||
export const basicTabStyle = style({
|
||||
$nest: {
|
||||
'& .pf-v5-c-tabs__list': {
|
||||
marginLeft: '20px',
|
||||
},
|
||||
|
||||
'& .pf-v5-c-tab-content': {
|
||||
overflowY: 'auto',
|
||||
height: '600px',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const traceTabStyle = style({
|
||||
$nest: {
|
||||
'& .pf-v5-c-tabs__list': {
|
||||
backgroundColor: PFColors.BackgroundColor100,
|
||||
borderBottom: `1px solid ${PFColors.BorderColor100}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -16,7 +16,8 @@
|
|||
"pluginPackages": [
|
||||
"@backstage-community/plugin-kiali",
|
||||
"@backstage-community/plugin-kiali-backend",
|
||||
"@backstage-community/plugin-kiali-common"
|
||||
"@backstage-community/plugin-kiali-common",
|
||||
"@backstage-community/plugin-kiali-react"
|
||||
]
|
||||
},
|
||||
"sideEffects": false,
|
||||
|
|
@ -31,6 +32,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@backstage-community/plugin-kiali-common": "workspace:^",
|
||||
"@backstage-community/plugin-kiali-react": "workspace:^",
|
||||
"@backstage/catalog-model": "^1.7.3",
|
||||
"@backstage/core-components": "^0.17.1",
|
||||
"@backstage/core-plugin-api": "^1.10.6",
|
||||
|
|
@ -90,6 +92,7 @@
|
|||
"canvas": "^3.0.0",
|
||||
"cross-fetch": "4.1.0",
|
||||
"jest-canvas-mock": "2.5.2",
|
||||
"msw": "1.3.5",
|
||||
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-router-dom": "^6.0.0",
|
||||
|
|
|
|||
|
|
@ -21,21 +21,12 @@ import {
|
|||
GraphType,
|
||||
TrafficRate,
|
||||
} from '@backstage-community/plugin-kiali-common/types';
|
||||
import { TrafficGraph } from '@backstage-community/plugin-kiali-react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Content, InfoCard } from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { CircularProgress, useTheme } from '@material-ui/core';
|
||||
import {
|
||||
action,
|
||||
createTopologyControlButtons,
|
||||
defaultControlButtonsOptions,
|
||||
Model,
|
||||
TopologyControlBar,
|
||||
TopologyView,
|
||||
Visualization,
|
||||
VisualizationProvider,
|
||||
VisualizationSurface,
|
||||
} from '@patternfly/react-topology';
|
||||
import { Model, Visualization } from '@patternfly/react-topology';
|
||||
import { default as React, useRef, useState } from 'react';
|
||||
import { useAsyncFn, useDebounce } from 'react-use';
|
||||
import { DefaultSecondaryMasthead } from '../../components/DefaultSecondaryMasthead/DefaultSecondaryMasthead';
|
||||
|
|
@ -241,41 +232,7 @@ function TrafficGraphPage(props: { view?: string; entity?: Entity }) {
|
|||
onRefresh={refresh}
|
||||
/>
|
||||
)}
|
||||
<TopologyView
|
||||
controlBar={
|
||||
<TopologyControlBar
|
||||
controlButtons={createTopologyControlButtons({
|
||||
...defaultControlButtonsOptions,
|
||||
zoomInCallback: action(() => {
|
||||
controller.getGraph().scaleBy(4 / 3);
|
||||
}),
|
||||
zoomOutCallback: action(() => {
|
||||
controller.getGraph().scaleBy(0.75);
|
||||
}),
|
||||
fitToScreenCallback: action(() => {
|
||||
controller.getGraph().fit(80);
|
||||
}),
|
||||
resetViewCallback: action(() => {
|
||||
controller.getGraph().reset();
|
||||
controller.getGraph().layout();
|
||||
}),
|
||||
legend: false,
|
||||
zoomInAriaLabel: '',
|
||||
zoomOutAriaLabel: '',
|
||||
fitToScreenAriaLabel: '',
|
||||
resetViewAriaLabel: '',
|
||||
zoomInTip: '',
|
||||
zoomOutTip: '',
|
||||
fitToScreenTip: '',
|
||||
resetViewTip: '',
|
||||
})}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<VisualizationProvider controller={controller}>
|
||||
<VisualizationSurface state={model} />
|
||||
</VisualizationProvider>
|
||||
</TopologyView>
|
||||
<TrafficGraph model={model} />
|
||||
</>
|
||||
)}
|
||||
</Content>
|
||||
|
|
|
|||
|
|
@ -2790,11 +2790,35 @@ __metadata:
|
|||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage-community/plugin-kiali-react@workspace:^, @backstage-community/plugin-kiali-react@workspace:plugins/kiali-react":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage-community/plugin-kiali-react@workspace:plugins/kiali-react"
|
||||
dependencies:
|
||||
"@backstage-community/plugin-kiali-common": "workspace:^"
|
||||
"@backstage/cli": "npm:^0.32.0"
|
||||
"@backstage/core-plugin-api": "npm:^1.10.6"
|
||||
"@backstage/dev-utils": "npm:^1.1.9"
|
||||
"@backstage/test-utils": "npm:^1.7.7"
|
||||
"@material-ui/core": "npm:^4.9.13"
|
||||
"@patternfly/react-core": "npm:^5.1.1"
|
||||
"@patternfly/react-icons": "npm:^5.1.1"
|
||||
"@patternfly/react-topology": "npm:5.4.1"
|
||||
"@redhat-developer/red-hat-developer-hub-theme": "npm:0.4.0"
|
||||
"@testing-library/jest-dom": "npm:^6.0.0"
|
||||
"@testing-library/react": "npm:^14.0.0"
|
||||
react: "npm:^16.13.1 || ^17.0.0 || ^18.0.0"
|
||||
typestyle: "npm:^2.4.0"
|
||||
peerDependencies:
|
||||
react: ^16.13.1 || ^17.0.0 || ^18.0.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage-community/plugin-kiali@workspace:^, @backstage-community/plugin-kiali@workspace:plugins/kiali":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage-community/plugin-kiali@workspace:plugins/kiali"
|
||||
dependencies:
|
||||
"@backstage-community/plugin-kiali-common": "workspace:^"
|
||||
"@backstage-community/plugin-kiali-react": "workspace:^"
|
||||
"@backstage/catalog-model": "npm:^1.7.3"
|
||||
"@backstage/cli": "npm:^0.32.0"
|
||||
"@backstage/core-components": "npm:^0.17.1"
|
||||
|
|
@ -2836,6 +2860,7 @@ __metadata:
|
|||
lodash: "npm:^4.17.21"
|
||||
micro-memoize: "npm:4.1.3"
|
||||
moment: "npm:^2.29.4"
|
||||
msw: "npm:1.3.5"
|
||||
prop-types: "npm:^15.8.1"
|
||||
react: "npm:^16.13.1 || ^17.0.0 || ^18.0.0"
|
||||
react-ace: "npm:9.5.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue