Merge pull request #7431 from szubster/dependency-graph-type-safety

Documentation and better type safety of DependencyGraph
This commit is contained in:
Fredrik Adelöw
2021-10-11 20:41:31 +02:00
committed by GitHub
16 changed files with 359 additions and 198 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/core-components': minor
'@backstage/plugin-catalog-graph': minor
---
Add documentation and more type safety around DependencyGraph
+52 -76
View File
@@ -17,7 +17,6 @@ import { ComponentProps } from 'react';
import { Context } from 'react';
import { default as CSS_2 } from 'csstype';
import { CSSProperties } from 'react';
import { default as dagre_2 } from 'dagre';
import { ElementType } from 'react';
import { ErrorInfo } from 'react';
import { IconComponent } from '@backstage/core-plugin-api';
@@ -53,15 +52,11 @@ export function AlertDisplay(_props: {}): JSX.Element | null;
// Warning: (ae-missing-release-tag) "Alignment" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
enum Alignment {
// (undocumented)
DOWN_LEFT = 'DL',
// (undocumented)
DOWN_RIGHT = 'DR',
// (undocumented)
UP_LEFT = 'UL',
// (undocumented)
UP_RIGHT = 'UR',
}
@@ -264,11 +259,10 @@ export type CustomProviderClassKey = 'form' | 'button';
// @public (undocumented)
export function DashboardIcon(props: IconComponentProps): JSX.Element;
// Warning: (ae-forgotten-export) The symbol "CustomType" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "DependencyEdge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
type DependencyEdge<T = CustomType> = T & {
// @public
type DependencyEdge<T = {}> = T & {
from: string;
to: string;
label?: string;
@@ -276,8 +270,10 @@ type DependencyEdge<T = CustomType> = T & {
// Warning: (ae-missing-release-tag) "DependencyGraph" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function DependencyGraph(props: DependencyGraphProps): JSX.Element;
// @public
export function DependencyGraph<NodeData, EdgeData>(
props: DependencyGraphProps<NodeData, EdgeData>,
): JSX.Element;
// Warning: (ae-missing-release-tag) "DependencyGraphDefaultLabelClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -301,40 +297,42 @@ export type DependencyGraphNodeClassKey = 'node';
// Warning: (ae-missing-release-tag) "DependencyGraphProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type DependencyGraphProps = React_2.SVGProps<SVGSVGElement> & {
edges: DependencyEdge[];
nodes: DependencyNode[];
direction?: Direction;
// @public
export interface DependencyGraphProps<NodeData, EdgeData>
extends React_2.SVGProps<SVGSVGElement> {
acyclicer?: 'greedy';
// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver
align?: Alignment;
nodeMargin?: number;
defs?: SVGDefsElement | SVGDefsElement[];
// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver
direction?: Direction;
edgeMargin?: number;
rankMargin?: number;
edgeRanks?: number;
edges: DependencyEdge<EdgeData>[];
edgeWeight?: number;
labelOffset?: number;
// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver
labelPosition?: LabelPosition;
nodeMargin?: number;
nodes: DependencyNode<NodeData>[];
paddingX?: number;
paddingY?: number;
acyclicer?: 'greedy';
// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver
ranker?: Ranker;
labelPosition?: LabelPosition;
labelOffset?: number;
edgeRanks?: number;
edgeWeight?: number;
renderNode?: RenderNodeFunction;
renderLabel?: RenderLabelFunction;
defs?: SVGDefsElement | SVGDefsElement[];
rankMargin?: number;
renderLabel?: RenderLabelFunction<EdgeData>;
renderNode?: RenderNodeFunction<NodeData>;
zoom?: 'enabled' | 'disabled' | 'enable-on-click';
};
}
declare namespace DependencyGraphTypes {
export {
DependencyEdge,
GraphEdge,
RenderLabelProps,
RenderLabelFunction,
DependencyNode,
GraphNode,
RenderNodeProps,
RenderNodeFunction,
EdgeProperties,
Direction,
Alignment,
Ranker,
@@ -345,22 +343,18 @@ export { DependencyGraphTypes };
// Warning: (ae-missing-release-tag) "DependencyNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
type DependencyNode<T = CustomType> = T & {
// @public
type DependencyNode<T = {}> = T & {
id: string;
};
// Warning: (ae-missing-release-tag) "Direction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
enum Direction {
// (undocumented)
BOTTOM_TOP = 'BT',
// (undocumented)
LEFT_RIGHT = 'LR',
// (undocumented)
RIGHT_LEFT = 'RL',
// (undocumented)
TOP_BOTTOM = 'TB',
}
@@ -387,20 +381,6 @@ export type DismissbleBannerClassKey =
// @public (undocumented)
export function DocsIcon(props: IconComponentProps): JSX.Element;
// Warning: (ae-missing-release-tag) "EdgeProperties" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
type EdgeProperties = {
label?: string;
width?: number;
height?: number;
labeloffset?: number;
labelpos?: LabelPosition;
minlen?: number;
weight?: number;
[customKey: string]: any;
};
// Warning: (ae-missing-release-tag) "EmailIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -520,18 +500,6 @@ export type GaugeClassKey = 'root' | 'overlay' | 'circle' | 'colorUnknown';
// @public (undocumented)
export function GitHubIcon(props: IconComponentProps): JSX.Element;
// Warning: (ae-missing-release-tag) "GraphEdge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
type GraphEdge<T = CustomType> = DependencyEdge<T> &
dagre_2.GraphEdge &
EdgeProperties;
// Warning: (ae-missing-release-tag) "GraphNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
type GraphNode<T = CustomType> = dagre_2.Node<DependencyNode<T>>;
// Warning: (ae-missing-release-tag) "GroupIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -738,7 +706,7 @@ export type ItemCardHeaderProps = Partial<WithStyles<typeof styles_2>> & {
// Warning: (ae-missing-release-tag) "LabelPosition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
enum LabelPosition {
// (undocumented)
CENTER = 'c',
@@ -888,37 +856,43 @@ export function Progress(
// Warning: (ae-missing-release-tag) "Ranker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
enum Ranker {
// (undocumented)
LONGEST_PATH = 'longest-path',
// (undocumented)
NETWORK_SIMPLEX = 'network-simplex',
// (undocumented)
TIGHT_TREE = 'tight-tree',
}
// Warning: (ae-missing-release-tag) "RenderLabelFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
type RenderLabelFunction = (props: RenderLabelProps<any>) => React.ReactNode;
// @public
type RenderLabelFunction<T = {}> = (
props: RenderLabelProps<T>,
) => React_2.ReactNode;
// Warning: (ae-missing-release-tag) "RenderLabelProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver
// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver
//
// @public (undocumented)
type RenderLabelProps<T = CustomType> = {
// @public
type RenderLabelProps<T = unknown> = {
edge: DependencyEdge<T>;
};
// Warning: (ae-missing-release-tag) "RenderNodeFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver
//
// @public (undocumented)
type RenderNodeFunction = (props: RenderNodeProps<any>) => React.ReactNode;
// @public
type RenderNodeFunction<T = {}> = (
props: RenderNodeProps<T>,
) => React_2.ReactNode;
// Warning: (ae-missing-release-tag) "RenderNodeProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver
// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver
//
// @public (undocumented)
type RenderNodeProps<T = CustomType> = {
// @public
type RenderNodeProps<T = unknown> = {
node: DependencyNode<T>;
};
@@ -2498,6 +2472,8 @@ export type WarningPanelClassKey =
// Warnings were encountered during analysis:
//
// src/components/DependencyGraph/types.d.ts:14:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode"
// src/components/DependencyGraph/types.d.ts:18:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode"
// src/components/TabbedLayout/RoutedTabs.d.ts:9:5 - (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts
// src/components/Table/Table.d.ts:19:5 - (ae-forgotten-export) The symbol "SelectedFilters" needs to be exported by the entry point index.d.ts
// src/layout/ErrorBoundary/ErrorBoundary.d.ts:7:5 - (ae-forgotten-export) The symbol "SlackChannel" needs to be exported by the entry point index.d.ts
+1 -1
View File
@@ -37,7 +37,6 @@
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"@types/dagre": "^0.7.44",
"@types/react": "*",
"@types/react-sparklines": "^1.7.0",
"@types/react-text-truncate": "^0.14.0",
@@ -79,6 +78,7 @@
"@types/d3-selection": "^2.0.0",
"@types/d3-shape": "^3.0.1",
"@types/d3-zoom": "^2.0.0",
"@types/dagre": "^0.7.44",
"@types/google-protobuf": "^3.7.2",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32",
@@ -29,39 +29,148 @@ import {
Ranker,
RenderNodeFunction,
RenderLabelFunction,
GraphEdge,
GraphNode,
LabelPosition,
} from './types';
import { Node } from './Node';
import { Edge } from './Edge';
import { Edge, GraphEdge } from './Edge';
import { ARROW_MARKER_ID } from './constants';
export type DependencyGraphProps = React.SVGProps<SVGSVGElement> & {
edges: DependencyEdge[];
nodes: DependencyNode[];
/**
* Properties of {@link DependencyGraph}
*
* @remarks
* <NodeData> and <EdgeData> are useful when rendering custom or edge labels
*/
export interface DependencyGraphProps<NodeData, EdgeData>
extends React.SVGProps<SVGSVGElement> {
/**
* Edges of graph
*/
edges: DependencyEdge<EdgeData>[];
/**
* Nodes of Graph
*/
nodes: DependencyNode<NodeData>[];
/**
* Graph {@link DependencyGraphTypes.Direction | direction}
*
* @remarks
*
* Default: `DependencyGraphTypes.Direction.TOP_BOTTOM`
*/
direction?: Direction;
/**
* Node {@link DependencyGraphTypes.Alignment | alignment}
*/
align?: Alignment;
/**
* Margin between nodes on each rank
*
* @remarks
*
* Default: 50
*/
nodeMargin?: number;
/**
* Margin between edges
*
* @remarks
*
* Default: 10
*/
edgeMargin?: number;
/**
* Margin between each rank
*
* @remarks
*
* Default: 50
*/
rankMargin?: number;
/**
* Margin on left and right of whole graph
*
* @remarks
*
* Default: 0
*/
paddingX?: number;
/**
* Margin on top and bottom of whole graph
*
* @remarks
*
* Default: 0
*/
paddingY?: number;
/**
* Heuristic used to find set of edges that will make graph acyclic
*/
acyclicer?: 'greedy';
/**
* {@link DependencyGraphTypes.Ranker | Algorithm} used to rank nodes
*
* @remarks
*
* Default: `DependencyGraphTypes.Ranker.NETWORK_SIMPLEX`
*/
ranker?: Ranker;
/**
* {@link DependencyGraphTypes.LabelPosition | Position} of label in relation to edge
*
* @remarks
*
* Default: `DependencyGraphTypes.LabelPosition.RIGHT`
*/
labelPosition?: LabelPosition;
/**
* How much to move label away from edge
*
* @remarks
*
* Applies only when {@link DependencyGraphProps.labelPosition} is `DependencyGraphTypes.LabelPosition.LEFT` or
* `DependencyGraphTypes.LabelPosition.RIGHT`
*/
labelOffset?: number;
/**
* Minimum number of ranks to keep between connected nodes
*/
edgeRanks?: number;
/**
* Weight applied to edges in graph
*/
edgeWeight?: number;
renderNode?: RenderNodeFunction;
renderLabel?: RenderLabelFunction;
/**
* Custom node rendering component
*/
renderNode?: RenderNodeFunction<NodeData>;
/**
* Custom label rendering component
*/
renderLabel?: RenderLabelFunction<EdgeData>;
/**
* {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs | Defs} shared by rendered SVG to be used by
* {@link DependencyGraphProps.renderNode} and/or {@link DependencyGraphProps.renderLabel}
*/
defs?: SVGDefsElement | SVGDefsElement[];
/**
* Controls zoom behavior of graph
*
* @remarks
*
* Default: `enabled`
*/
zoom?: 'enabled' | 'disabled' | 'enable-on-click';
};
}
const WORKSPACE_ID = 'workspace';
export function DependencyGraph(props: DependencyGraphProps) {
/**
* Graph component used to visualize relations between entities
*/
export function DependencyGraph<NodeData, EdgeData>(
props: DependencyGraphProps<NodeData, EdgeData>,
) {
const {
edges,
nodes,
@@ -88,7 +197,7 @@ export function DependencyGraph(props: DependencyGraphProps) {
const [containerWidth, setContainerWidth] = React.useState<number>(100);
const [containerHeight, setContainerHeight] = React.useState<number>(100);
const graph = React.useRef<dagre.graphlib.Graph<{}>>(
const graph = React.useRef<dagre.graphlib.Graph<DependencyNode<NodeData>>>(
new dagre.graphlib.Graph(),
);
const [graphWidth, setGraphWidth] = React.useState<number>(
@@ -256,13 +365,13 @@ export function DependencyGraph(props: DependencyGraphProps) {
updateGraph,
]);
function setNode(id: string, node: DependencyNode) {
function setNode(id: string, node: DependencyNode<NodeData>) {
graph.current.setNode(id, node);
updateGraph();
return graph.current;
}
function setEdge(id: dagre.Edge, edge: DependencyEdge) {
function setEdge(id: dagre.Edge, edge: DependencyEdge<EdgeData>) {
graph.current.setEdge(id, edge);
updateGraph();
return graph.current;
@@ -303,7 +412,7 @@ export function DependencyGraph(props: DependencyGraphProps) {
viewBox={`0 0 ${graphWidth} ${graphHeight}`}
>
{graphEdges.map(e => {
const edge = graph.current.edge(e) as GraphEdge;
const edge = graph.current.edge(e) as GraphEdge<EdgeData>;
if (!edge) return null;
return (
<Edge
@@ -316,7 +425,7 @@ export function DependencyGraph(props: DependencyGraphProps) {
);
})}
{graphNodes.map((id: string) => {
const node = graph.current.node(id) as GraphNode;
const node = graph.current.node(id);
if (!node) return null;
return (
<Node
@@ -23,6 +23,10 @@ const fromNode = 'node';
const toNode = 'other-node';
const edge = {
points: [
{ x: 10, y: 20 },
{ x: 20, y: 20 },
],
from: fromNode,
to: toNode,
};
@@ -38,10 +42,6 @@ const renderElement = jest.fn((props: RenderLabelProps) => (
));
const minProps = {
points: [
{ x: 10, y: 20 },
{ x: 20, y: 20 },
],
id,
setEdge,
renderElement,
@@ -20,13 +20,26 @@ import isFinite from 'lodash/isFinite';
import makeStyles from '@material-ui/core/styles/makeStyles';
import { BackstageTheme } from '@backstage/theme';
import {
GraphEdge,
RenderLabelProps,
RenderLabelFunction,
DependencyEdge,
LabelPosition,
} from './types';
import { ARROW_MARKER_ID, EDGE_TEST_ID, LABEL_TEST_ID } from './constants';
import { DefaultLabel } from './DefaultLabel';
import dagre from 'dagre';
/* Based on: https://github.com/dagrejs/dagre/wiki#configuring-the-layout */
export type EdgeProperties = {
label?: string;
width?: number;
height?: number;
labeloffset?: number;
labelpos?: LabelPosition;
minlen?: number;
weight?: number;
};
export type GraphEdge<T> = DependencyEdge<T> & dagre.GraphEdge & EdgeProperties;
export type DependencyGraphEdgeClassKey = 'path' | 'label';
@@ -47,14 +60,19 @@ const useStyles = makeStyles(
type EdgePoint = dagre.GraphEdge['points'][0];
export type EdgeComponentProps<T = any> = {
export type EdgeComponentProps<T = unknown> = {
id: dagre.Edge;
edge: GraphEdge<T>;
render?: RenderLabelFunction;
setEdge: (id: dagre.Edge, edge: DependencyEdge) => dagre.graphlib.Graph<{}>;
render?: RenderLabelFunction<T>;
setEdge: (
id: dagre.Edge,
edge: DependencyEdge<T>,
) => dagre.graphlib.Graph<{}>;
};
const renderDefault = (props: RenderLabelProps) => <DefaultLabel {...props} />;
const renderDefault = (props: RenderLabelProps<unknown>) => (
<DefaultLabel {...props} />
);
const createPath = d3Shape
.line<EdgePoint>()
@@ -62,13 +80,14 @@ const createPath = d3Shape
.y(d => d.y)
.curve(d3Shape.curveMonotoneX);
export function Edge({
export function Edge<EdgeData>({
render = renderDefault,
setEdge,
id,
edge,
}: EdgeComponentProps) {
const { x = 0, y = 0, width, height, points, ...labelProps } = edge;
}: EdgeComponentProps<EdgeData>) {
const { x = 0, y = 0, width, height, points } = edge;
const labelProps: DependencyEdge<EdgeData> = edge;
const classes = useStyles();
const labelRef = React.useRef<SVGGElement>(null);
@@ -20,21 +20,16 @@ import { render } from '@testing-library/react';
import { Node } from './Node';
import { RenderNodeProps } from './types';
const node = { id: 'abc' };
const node = { id: 'abc', x: 0, y: 0, width: 0, height: 0 };
const setNode = jest.fn(() => new dagre.graphlib.Graph());
const renderElement = jest.fn((props: RenderNodeProps) => (
<text>{props.node.id}</text>
));
const minProps = {
id: node.id,
node,
setNode,
render: renderElement,
x: 0,
y: 0,
width: 0,
height: 0,
};
describe('<Node />', () => {
@@ -50,7 +45,7 @@ describe('<Node />', () => {
it('renders the supplied element', () => {
const { getByText } = render(<Node {...minProps} />);
expect(getByText(minProps.id)).toBeInTheDocument();
expect(getByText(minProps.node.id)).toBeInTheDocument();
});
it('passes down node properties to the render method', () => {
@@ -62,13 +57,13 @@ describe('<Node />', () => {
it('calls setNode with node ID and actual size after rendering', () => {
const { getByText } = render(<Node {...minProps} />);
expect(getByText(minProps.id)).toBeInTheDocument();
expect(getByText(minProps.node.id)).toBeInTheDocument();
// Updates the node in the graph
expect(setNode).toHaveBeenCalledWith(node.id, {
...node,
height: 100,
width: 100,
...node,
});
// Does not pass down width/height to node
@@ -17,8 +17,9 @@
import React from 'react';
import makeStyles from '@material-ui/core/styles/makeStyles';
import { DefaultNode } from './DefaultNode';
import { RenderNodeFunction, RenderNodeProps, GraphNode } from './types';
import { RenderNodeFunction, RenderNodeProps, DependencyNode } from './types';
import { NODE_TEST_ID } from './constants';
import dagre from 'dagre';
export type DependencyGraphNodeClassKey = 'node';
@@ -31,20 +32,23 @@ const useStyles = makeStyles(
{ name: 'BackstageDependencyGraphNode' },
);
export type NodeComponentProps<T = any> = {
export type GraphNode<T> = dagre.Node<DependencyNode<T>>;
export type NodeComponentProps<T> = {
node: GraphNode<T>;
render?: RenderNodeFunction;
render?: RenderNodeFunction<T>;
setNode: dagre.graphlib.Graph['setNode'];
};
const renderDefault = (props: RenderNodeProps) => <DefaultNode {...props} />;
export function Node({
export function Node<T>({
render = renderDefault,
setNode,
node,
}: NodeComponentProps) {
const { width, height, x = 0, y = 0, ...nodeProps } = node;
}: NodeComponentProps<T>) {
const { width, height, x = 0, y = 0 } = node;
const nodeProps: DependencyNode<T> = node;
const classes = useStyles();
const nodeRef = React.useRef<SVGGElement | null>(null);
@@ -14,73 +14,132 @@
* limitations under the License.
*/
import dagre from 'dagre';
/**
* Types used to customize and provide data to {@link DependencyGraph}
*
* @packageDocumentation
*/
type CustomType = { [customKey: string]: any };
import React from 'react';
/* Edges */
export type DependencyEdge<T = CustomType> = T & {
/**
* Edge of {@link DependencyGraph}
*/
export type DependencyEdge<T = {}> = T & {
/**
* ID of {@link DependencyNode} from where the Edge start
*/
from: string;
/**
* ID of {@link DependencyNode} to where the Edge goes to
*/
to: string;
/**
* Label assigned and rendered with the Edge
*/
label?: string;
};
export type GraphEdge<T = CustomType> = DependencyEdge<T> &
dagre.GraphEdge &
EdgeProperties;
/**
* Properties of {@link DependencyGraphTypes.RenderLabelFunction} for {@link DependencyGraphTypes.DependencyEdge}
*/
export type RenderLabelProps<T = unknown> = { edge: DependencyEdge<T> };
export type RenderLabelProps<T = CustomType> = { edge: DependencyEdge<T> };
export type RenderLabelFunction = (
props: RenderLabelProps<any>,
/**
* Custom React component for edge labels
*/
export type RenderLabelFunction<T = {}> = (
props: RenderLabelProps<T>,
) => React.ReactNode;
/* Nodes */
export type DependencyNode<T = CustomType> = T & {
/**
* Node of {@link DependencyGraph}
*/
export type DependencyNode<T = {}> = T & {
id: string;
};
export type GraphNode<T = CustomType> = dagre.Node<DependencyNode<T>>;
/**
* Properties of {@link DependencyGraphTypes.RenderNodeFunction} for {@link DependencyGraphTypes.DependencyNode}
*/
export type RenderNodeProps<T = unknown> = { node: DependencyNode<T> };
export type RenderNodeProps<T = CustomType> = { node: DependencyNode<T> };
export type RenderNodeFunction = (
props: RenderNodeProps<any>,
/**
* Custom React component for graph {@link DependencyGraphTypes.DependencyNode}
*/
export type RenderNodeFunction<T = {}> = (
props: RenderNodeProps<T>,
) => React.ReactNode;
/* Based on: https://github.com/dagrejs/dagre/wiki#configuring-the-layout */
export type EdgeProperties = {
label?: string;
width?: number;
height?: number;
labeloffset?: number;
labelpos?: LabelPosition;
minlen?: number;
weight?: number;
[customKey: string]: any;
};
/**
* Graph direction
*/
export enum Direction {
/**
* Top to Bottom
*/
TOP_BOTTOM = 'TB',
/**
* Bottom to Top
*/
BOTTOM_TOP = 'BT',
/**
* Left to Right
*/
LEFT_RIGHT = 'LR',
/**
* Right to Left
*/
RIGHT_LEFT = 'RL',
}
/**
* Node alignment
*/
export enum Alignment {
/**
* Up Left
*/
UP_LEFT = 'UL',
/**
* Up Right
*/
UP_RIGHT = 'UR',
/**
* Down Left
*/
DOWN_LEFT = 'DL',
/**
* Down Right
*/
DOWN_RIGHT = 'DR',
}
/**
* Algorithm used to rand nodes in graph
*/
export enum Ranker {
/**
* {@link https://en.wikipedia.org/wiki/Network_simplex_algorithm | Network Simplex} algorithm
*/
NETWORK_SIMPLEX = 'network-simplex',
/**
* Tight Tree algorithm
*/
TIGHT_TREE = 'tight-tree',
/**
* Longest path algorithm
*
* @remarks
*
* Simplest and fastest
*/
LONGEST_PATH = 'longest-path',
}
/**
* Position of label in relation to the edge
*/
export enum LabelPosition {
LEFT = 'l',
RIGHT = 'r',
+14 -4
View File
@@ -93,13 +93,23 @@ export const EntityCatalogGraphCard: ({
}) => JSX.Element;
// @public
export type EntityEdge = DependencyGraphTypes.DependencyEdge<{
export type EntityEdge = DependencyGraphTypes.DependencyEdge<EntityEdgeData>;
// Warning: (ae-missing-release-tag) "EntityEdgeData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export type EntityEdgeData = {
relations: string[];
label: 'visible';
}>;
};
// @public
export type EntityNode = DependencyGraphTypes.DependencyNode<{
export type EntityNode = DependencyGraphTypes.DependencyNode<EntityNodeData>;
// Warning: (ae-missing-release-tag) "EntityNodeData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export type EntityNodeData = {
name: string;
kind?: string;
title?: string;
@@ -107,7 +117,7 @@ export type EntityNode = DependencyGraphTypes.DependencyNode<{
focused?: boolean;
color?: 'primary' | 'secondary' | 'default';
onClick?: MouseEventHandler<unknown>;
}>;
};
// @public
export const EntityRelationsGraph: ({
@@ -31,12 +31,6 @@ describe('<CustomLabel />', () => {
relations: [RELATION_PARENT_OF],
from: 'from-id',
to: 'to-id',
id: 'id',
x: 111,
y: 222,
width: 100,
height: 25,
points: [],
}}
/>
</svg>,
@@ -54,12 +48,6 @@ describe('<CustomLabel />', () => {
relations: [RELATION_PARENT_OF, RELATION_CHILD_OF],
from: 'from-id',
to: 'to-id',
id: 'id',
x: 111,
y: 222,
width: 100,
height: 25,
points: [],
}}
/>
</svg>,
@@ -17,7 +17,7 @@ import { DependencyGraphTypes } from '@backstage/core-components';
import { BackstageTheme } from '@backstage/theme';
import makeStyles from '@material-ui/core/styles/makeStyles';
import React from 'react';
import { GraphEdge } from './types';
import { EntityEdgeData } from './types';
import classNames from 'classnames';
const useStyles = makeStyles((theme: BackstageTheme) => ({
@@ -31,7 +31,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({
export function CustomLabel({
edge: { relations },
}: DependencyGraphTypes.RenderLabelProps<GraphEdge>) {
}: DependencyGraphTypes.RenderLabelProps<EntityEdgeData>) {
const classes = useStyles();
return (
<text className={classes.text} textAnchor="middle">
@@ -36,10 +36,6 @@ describe('<CustomNode />', () => {
name: 'name',
namespace: 'namespace',
id: 'kind:namespace/name',
x: 111,
y: 222,
width: 100,
height: 25,
color: 'primary',
}}
/>
@@ -59,10 +55,6 @@ describe('<CustomNode />', () => {
name: 'name',
namespace: 'default',
id: 'kind:default/name',
x: 111,
y: 222,
width: 100,
height: 25,
}}
/>
</svg>,
@@ -83,10 +75,6 @@ describe('<CustomNode />', () => {
namespace: 'namespace',
onClick,
id: 'kind:namespace/name',
x: 111,
y: 222,
width: 100,
height: 25,
}}
/>
</svg>,
@@ -108,10 +96,6 @@ describe('<CustomNode />', () => {
namespace: 'namespace',
title: 'Custom Title',
id: 'kind:namespace/name',
x: 111,
y: 222,
width: 100,
height: 25,
}}
/>
</svg>,
@@ -20,7 +20,7 @@ import { makeStyles } from '@material-ui/core/styles';
import classNames from 'classnames';
import React, { useLayoutEffect, useRef, useState } from 'react';
import { EntityKindIcon } from './EntityKindIcon';
import { GraphNode } from './types';
import { EntityNodeData } from './types';
const useStyles = makeStyles((theme: BackstageTheme) => ({
node: {
@@ -65,7 +65,7 @@ export function CustomNode({
title,
onClick,
},
}: DependencyGraphTypes.RenderNodeProps<GraphNode>) {
}: DependencyGraphTypes.RenderNodeProps<EntityNodeData>) {
const classes = useStyles();
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
@@ -17,4 +17,9 @@ export { EntityRelationsGraph } from './EntityRelationsGraph';
export { ALL_RELATION_PAIRS } from './relations';
export type { RelationPairs } from './relations';
export { Direction } from './types';
export type { EntityEdge, EntityNode } from './types';
export type {
EntityEdgeData,
EntityEdge,
EntityNodeData,
EntityNode,
} from './types';
@@ -17,11 +17,9 @@ import { DependencyGraphTypes } from '@backstage/core-components';
import { MouseEventHandler } from 'react';
/**
* Edge between two entities.
*
* @public
* Additional Data for entities
*/
export type EntityEdge = DependencyGraphTypes.DependencyEdge<{
export type EntityEdgeData = {
/**
* Up to two relations that are connecting an entity.
*/
@@ -31,14 +29,19 @@ export type EntityEdge = DependencyGraphTypes.DependencyEdge<{
*/
// Not used, but has to be non empty to draw a label at all!
label: 'visible';
}>;
};
/**
* Node representing an entity.
* Edge between two entities.
*
* @public
*/
export type EntityNode = DependencyGraphTypes.DependencyNode<{
export type EntityEdge = DependencyGraphTypes.DependencyEdge<EntityEdgeData>;
/**
* Additional data for Entity Node
*/
export type EntityNodeData = {
/**
* Name of the entity.
*/
@@ -68,11 +71,14 @@ export type EntityNode = DependencyGraphTypes.DependencyNode<{
* Optional click handler.
*/
onClick?: MouseEventHandler<unknown>;
}>;
};
export type GraphEdge = DependencyGraphTypes.GraphEdge<EntityEdge>;
export type GraphNode = DependencyGraphTypes.GraphNode<EntityNode>;
/**
* Node representing an entity.
*
* @public
*/
export type EntityNode = DependencyGraphTypes.DependencyNode<EntityNodeData>;
/**
* Render direction of the graph.