Merge branch 'backstage:master' into master

This commit is contained in:
Joe Patterson
2022-10-14 14:43:47 +10:00
committed by GitHub
17 changed files with 194 additions and 31 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/core-components': patch
'@backstage/plugin-catalog-graph': patch
---
Added `curve` prop to the `DependencyGraph` component to select the type of layout
+1 -1
View File
@@ -1,5 +1,5 @@
{
"mode": "pre",
"mode": "exit",
"tag": "next",
"initialVersions": {
"example-app": "0.2.75",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 40 KiB

+2 -6
View File
@@ -51,14 +51,10 @@ create a subdirectory inside your current working directory.
npx @backstage/create-app
```
The wizard will ask you
- The name of the app, which will also be the name of the directory
- The database type to use for the backend. For this guide, you'll be using the
SQLite option.
The wizard will ask you for the name of the app, which will also be the name of the directory
<p align='center'>
<img src='../assets/getting-started/wizard.png' alt='Screenshot of the wizard asking for a name for the app, and a selection menu for the database.' />
<img src='../assets/getting-started/wizard.png' alt='Screenshot of the wizard asking for a name for the app.' />
</p>
### Run the Backstage app
+1
View File
@@ -242,6 +242,7 @@ export interface DependencyGraphProps<NodeData, EdgeData>
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;
curve?: 'curveStepBefore' | 'curveMonotoneX';
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;
@@ -162,6 +162,14 @@ export interface DependencyGraphProps<NodeData, EdgeData>
* Default: `enabled`
*/
zoom?: 'enabled' | 'disabled' | 'enable-on-click';
/**
* A factory for curve generators addressing both lines and areas.
*
* @remarks
*
* Default: 'curveMonotoneX'
*/
curve?: 'curveStepBefore' | 'curveMonotoneX';
}
const WORKSPACE_ID = 'workspace';
@@ -194,6 +202,7 @@ export function DependencyGraph<NodeData, EdgeData>(
renderLabel,
defs,
zoom = 'enabled',
curve = 'curveMonotoneX',
...svgProps
} = props;
const theme: BackstageTheme = useTheme();
@@ -424,6 +433,7 @@ export function DependencyGraph<NodeData, EdgeData>(
setEdge={setEdge}
render={renderLabel}
edge={edge}
curve={curve}
/>
);
})}
@@ -21,6 +21,7 @@ import { RenderLabelProps } from './types';
const fromNode = 'node';
const toNode = 'other-node';
const curve: 'curveStepBefore' | 'curveMonotoneX' = 'curveMonotoneX';
const edge = {
points: [
@@ -46,6 +47,7 @@ const minProps = {
setEdge,
renderElement,
edge,
curve,
};
const label = 'label';
@@ -25,7 +25,7 @@ import {
DependencyEdge,
LabelPosition,
} from './types';
import { ARROW_MARKER_ID, EDGE_TEST_ID, LABEL_TEST_ID } from './constants';
import { EDGE_TEST_ID, LABEL_TEST_ID } from './constants';
import { DefaultLabel } from './DefaultLabel';
import dagre from 'dagre';
@@ -47,7 +47,7 @@ export type DependencyGraphEdgeClassKey = 'path' | 'label';
const useStyles = makeStyles(
(theme: BackstageTheme) => ({
path: {
strokeWidth: 2,
strokeWidth: 1,
stroke: theme.palette.textSubtle,
fill: 'none',
transition: `${theme.transitions.duration.shortest}ms`,
@@ -70,23 +70,19 @@ export type EdgeComponentProps<T = unknown> = {
id: dagre.Edge,
edge: DependencyEdge<T>,
) => dagre.graphlib.Graph<{}>;
curve: 'curveStepBefore' | 'curveMonotoneX';
};
const renderDefault = (props: RenderLabelProps<unknown>) => (
<DefaultLabel {...props} />
);
const createPath = d3Shape
.line<EdgePoint>()
.x(d => d.x)
.y(d => d.y)
.curve(d3Shape.curveMonotoneX);
export function Edge<EdgeData>({
render = renderDefault,
setEdge,
id,
edge,
curve,
}: EdgeComponentProps<EdgeData>) {
const { x = 0, y = 0, width, height, points } = edge;
const labelProps: DependencyEdge<EdgeData> = edge;
@@ -114,6 +110,16 @@ export function Edge<EdgeData>({
let path: string = '';
const createPath = React.useMemo(
() =>
d3Shape
.line<EdgePoint>()
.x(d => d.x)
.y(d => d.y)
.curve(d3Shape[curve]),
[curve],
);
if (points) {
const finitePoints = points.filter(
(point: EdgePoint) => isFinite(point.x) && isFinite(point.y),
@@ -124,12 +130,7 @@ export function Edge<EdgeData>({
return (
<>
{path && (
<path
data-testid={EDGE_TEST_ID}
className={classes.path}
markerEnd={`url(#${ARROW_MARKER_ID})`}
d={path}
/>
<path data-testid={EDGE_TEST_ID} className={classes.path} d={path} />
)}
{labelProps.label ? (
<g
+2
View File
@@ -30,6 +30,7 @@ export const CatalogGraphPage: (props: {
mergeRelations?: boolean | undefined;
direction?: Direction | undefined;
showFilters?: boolean | undefined;
curve?: 'curveStepBefore' | 'curveMonotoneX' | undefined;
}
| undefined;
}) => JSX.Element;
@@ -120,6 +121,7 @@ export const EntityRelationsGraph: (props: {
renderLabel?:
| DependencyGraphTypes.RenderLabelFunction<EntityEdge>
| undefined;
curve?: 'curveStepBefore' | 'curveMonotoneX' | undefined;
}) => JSX.Element;
// @public
@@ -40,6 +40,7 @@ import {
EntityRelationsGraph,
RelationPairs,
} from '../EntityRelationsGraph';
import { CurveFilter } from './CurveFilter';
import { DirectionFilter } from './DirectionFilter';
import { MaxDepthFilter } from './MaxDepthFilter';
import { SelectedKindsFilter } from './SelectedKindsFilter';
@@ -110,6 +111,7 @@ export const CatalogGraphPage = (props: {
mergeRelations?: boolean;
direction?: Direction;
showFilters?: boolean;
curve?: 'curveStepBefore' | 'curveMonotoneX';
};
}) => {
const { relationPairs = ALL_RELATION_PAIRS, initialState } = props;
@@ -130,6 +132,8 @@ export const CatalogGraphPage = (props: {
setMergeRelations,
direction,
setDirection,
curve,
setCurve,
rootEntityNames,
setRootEntityNames,
showFilters,
@@ -201,6 +205,7 @@ export const CatalogGraphPage = (props: {
relationPairs={relationPairs}
/>
<DirectionFilter value={direction} onChange={setDirection} />
<CurveFilter value={curve} onChange={setCurve} />
<SwitchFilter
value={unidirectional}
onChange={setUnidirectional}
@@ -245,6 +250,7 @@ export const CatalogGraphPage = (props: {
relationPairs={relationPairs}
className={classes.graph}
zoom="enabled"
curve={curve}
/>
</Paper>
</Grid>
@@ -0,0 +1,47 @@
/*
* Copyright 2021 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 { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { CurveFilter } from './CurveFilter';
describe('<CurveFilter/>', () => {
test('should display current curve label', () => {
const onChange = jest.fn();
const { getByText } = render(
<CurveFilter value="curveMonotoneX" onChange={onChange} />,
);
expect(getByText('Monotone X')).toBeInTheDocument();
});
test('should select an alternative curve factory', async () => {
const onChange = jest.fn();
const { getByText, getByTestId } = render(
<CurveFilter value="curveStepBefore" onChange={onChange} />,
);
expect(getByText('Step Before')).toBeInTheDocument();
await userEvent.click(getByTestId('select'));
await userEvent.click(getByText('Monotone X'));
await waitFor(() => {
expect(getByText('Monotone X')).toBeInTheDocument();
expect(onChange).toHaveBeenCalledWith('curveMonotoneX');
});
});
});
@@ -0,0 +1,49 @@
/*
* Copyright 2021 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 { Select } from '@backstage/core-components';
import { Box } from '@material-ui/core';
import React, { useCallback } from 'react';
type Curve = 'curveStepBefore' | 'curveMonotoneX';
const CURVE_DISPLAY_NAMES: Record<Curve, string> = {
curveMonotoneX: 'Monotone X',
curveStepBefore: 'Step Before',
};
export type Props = {
value: Curve;
onChange: (value: 'curveStepBefore' | 'curveMonotoneX') => void;
};
const curves: Array<Curve> = ['curveMonotoneX', 'curveStepBefore'];
export const CurveFilter = ({ value, onChange }: Props) => {
const handleChange = useCallback(v => onChange(v as Curve), [onChange]);
return (
<Box pb={1} pt={1}>
<Select
label="Curve"
selected={value}
items={curves.map(v => ({
label: CURVE_DISPLAY_NAMES[v],
value: v,
}))}
onChange={handleChange}
/>
</Box>
);
};
@@ -46,6 +46,10 @@ export type CatalogGraphPageValue = {
setMergeRelations: Dispatch<React.SetStateAction<boolean>>;
direction: Direction;
setDirection: Dispatch<React.SetStateAction<Direction>>;
curve: 'curveStepBefore' | 'curveMonotoneX';
setCurve: Dispatch<
React.SetStateAction<'curveStepBefore' | 'curveMonotoneX'>
>;
showFilters: boolean;
toggleShowFilters: DispatchWithoutAction;
};
@@ -62,6 +66,7 @@ export function useCatalogGraphPage({
mergeRelations?: boolean;
direction?: Direction;
showFilters?: boolean;
curve?: 'curveStepBefore' | 'curveMonotoneX';
};
}): CatalogGraphPageValue {
const location = useLocation();
@@ -77,6 +82,7 @@ export function useCatalogGraphPage({
mergeRelations?: string[] | string;
direction?: string[] | Direction;
showFilters?: string[] | string;
curve?: string[] | 'curveStepBefore' | 'curveMonotoneX';
},
[location.search],
);
@@ -122,6 +128,11 @@ export function useCatalogGraphPage({
? query.direction
: initialState?.direction ?? Direction.LEFT_RIGHT,
);
const [curve, setCurve] = useState<'curveStepBefore' | 'curveMonotoneX'>(() =>
typeof query.curve === 'string'
? query.curve
: initialState?.curve ?? 'curveMonotoneX',
);
const [showFilters, setShowFilters] = useState<boolean>(() =>
typeof query.showFilters === 'string'
? query.showFilters === 'true'
@@ -249,6 +260,8 @@ export function useCatalogGraphPage({
setMergeRelations,
direction,
setDirection,
curve,
setCurve,
showFilters,
toggleShowFilters,
};
@@ -82,6 +82,7 @@ export const EntityRelationsGraph = (props: {
zoom?: 'enabled' | 'disabled' | 'enable-on-click';
renderNode?: DependencyGraphTypes.RenderNodeFunction<EntityNode>;
renderLabel?: DependencyGraphTypes.RenderLabelFunction<EntityEdge>;
curve?: 'curveStepBefore' | 'curveMonotoneX';
}) => {
const {
rootEntityNames,
@@ -97,6 +98,7 @@ export const EntityRelationsGraph = (props: {
zoom = 'enabled',
renderNode,
renderLabel,
curve,
} = props;
const theme = useTheme();
@@ -143,6 +145,7 @@ export const EntityRelationsGraph = (props: {
labelPosition={DependencyGraphTypes.LabelPosition.RIGHT}
labelOffset={theme.spacing(1)}
zoom={zoom}
curve={curve}
/>
)}
</div>
@@ -219,13 +219,13 @@ describe('GoogleGCSPublish', () => {
},
};
beforeAll(() => {
beforeEach(() => {
mockFs({
[directory]: files,
});
});
afterAll(() => {
afterEach(() => {
mockFs.restore();
});
+35 -8
View File
@@ -11178,20 +11178,20 @@ __metadata:
linkType: hard
"@octokit/auth-app@npm:^4.0.0":
version: 4.0.6
resolution: "@octokit/auth-app@npm:4.0.6"
version: 4.0.7
resolution: "@octokit/auth-app@npm:4.0.7"
dependencies:
"@octokit/auth-oauth-app": ^5.0.0
"@octokit/auth-oauth-user": ^2.0.0
"@octokit/request": ^6.0.0
"@octokit/request-error": ^3.0.0
"@octokit/types": ^7.0.0
"@octokit/types": ^8.0.0
"@types/lru-cache": ^5.1.0
deprecation: ^2.3.1
lru-cache: ^6.0.0
universal-github-app-jwt: ^1.0.1
universal-user-agent: ^6.0.0
checksum: 342fece6db4470ee489e710af8aa14ffd3a89e666815a15fadbabe3a382932a714d5bece24375f0ca39f3310f9d10bd28bc348210282cbc6701d27dd9b2ff7ed
checksum: 880de7341f47c5a48822612f50ae453fb0990a188b29afdb2f80a1f7e703c88e068d05a4edc1aa9a8204c730b6f70cf2772fbd5da032fee4986a5266ebdda4c7
languageName: node
linkType: hard
@@ -11446,6 +11446,13 @@ __metadata:
languageName: node
linkType: hard
"@octokit/openapi-types@npm:^14.0.0":
version: 14.0.0
resolution: "@octokit/openapi-types@npm:14.0.0"
checksum: 0a1f8f3be998cd82c5a640e9166d43fd183b33d5d36f5e1a9b81608e94d0da87c01ec46c9988f69cd26585d4e2ffc4d3ec99ee4f75e5fe997fc86dad0aa8293c
languageName: node
linkType: hard
"@octokit/openapi-types@npm:^7.3.2":
version: 7.4.0
resolution: "@octokit/openapi-types@npm:7.4.0"
@@ -11669,6 +11676,15 @@ __metadata:
languageName: node
linkType: hard
"@octokit/types@npm:^8.0.0":
version: 8.0.0
resolution: "@octokit/types@npm:8.0.0"
dependencies:
"@octokit/openapi-types": ^14.0.0
checksum: 1a0197b2c4c522ac90f145e02b3f8cb048a47f71c2c6bdbf021a03db7dd30ca92a899c0186acb401337f218efe44e60d33cc1cc68715b622bb75bc1a4e79515d
languageName: node
linkType: hard
"@octokit/webhooks-methods@npm:^3.0.0":
version: 3.0.0
resolution: "@octokit/webhooks-methods@npm:3.0.0"
@@ -21774,8 +21790,8 @@ __metadata:
linkType: hard
"eslint-plugin-react@npm:^7.28.0":
version: 7.31.8
resolution: "eslint-plugin-react@npm:7.31.8"
version: 7.31.10
resolution: "eslint-plugin-react@npm:7.31.10"
dependencies:
array-includes: ^3.1.5
array.prototype.flatmap: ^1.3.0
@@ -21793,7 +21809,7 @@ __metadata:
string.prototype.matchall: ^4.0.7
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
checksum: 0683e2a624a4df6f08264a3f6bc614a81e8f961c83173bdf2d8d3523f84ed5d234cddc976dbc6815913e007c5984df742ba61be0c0592b27c3daabe0f68165a3
checksum: f013669c296483559a760648fa06425f161b1aff93c668f14c4561c933d22a7836b745b88a795c53cab929c71513d5fd1f2ffdddff915709f01b77ac25f5b71b
languageName: node
linkType: hard
@@ -32624,7 +32640,18 @@ __metadata:
languageName: node
linkType: hard
"postcss@npm:^8.1.0, postcss@npm:^8.4.7":
"postcss@npm:^8.1.0":
version: 8.4.17
resolution: "postcss@npm:8.4.17"
dependencies:
nanoid: ^3.3.4
picocolors: ^1.0.0
source-map-js: ^1.0.2
checksum: a6d9096dd711e17f7b1d18ff5dcb4fdedf3941d5a3dc8b0e4ea873b8f31972d57f73d6da9a8aed7ff389eb52190ed34f6a94f299a7f5ddc68b08a24a48f77eb9
languageName: node
linkType: hard
"postcss@npm:^8.4.7":
version: 8.4.16
resolution: "postcss@npm:8.4.16"
dependencies: