diff --git a/.changeset/clever-clocks-drive.md b/.changeset/clever-clocks-drive.md
new file mode 100644
index 0000000000..0834133e36
--- /dev/null
+++ b/.changeset/clever-clocks-drive.md
@@ -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
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 33b9221a6a..dafb8797e2 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -1,5 +1,5 @@
{
- "mode": "pre",
+ "mode": "exit",
"tag": "next",
"initialVersions": {
"example-app": "0.2.75",
diff --git a/docs/assets/getting-started/portal.png b/docs/assets/getting-started/portal.png
index f16ad44757..b3133f2278 100644
Binary files a/docs/assets/getting-started/portal.png and b/docs/assets/getting-started/portal.png differ
diff --git a/docs/assets/getting-started/wizard.png b/docs/assets/getting-started/wizard.png
index 679878e252..8b6dce6131 100644
Binary files a/docs/assets/getting-started/wizard.png and b/docs/assets/getting-started/wizard.png differ
diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md
index ade73d3f68..477467bb88 100644
--- a/docs/getting-started/index.md
+++ b/docs/getting-started/index.md
@@ -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
-
+
### Run the Backstage app
diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md
index 99d18b9b12..9587a32ab5 100644
--- a/packages/core-components/api-report.md
+++ b/packages/core-components/api-report.md
@@ -242,6 +242,7 @@ export interface DependencyGraphProps
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;
diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx
index 9f447f80cc..5159f67d9e 100644
--- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx
+++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx
@@ -162,6 +162,14 @@ export interface DependencyGraphProps
* 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(
renderLabel,
defs,
zoom = 'enabled',
+ curve = 'curveMonotoneX',
...svgProps
} = props;
const theme: BackstageTheme = useTheme();
@@ -424,6 +433,7 @@ export function DependencyGraph(
setEdge={setEdge}
render={renderLabel}
edge={edge}
+ curve={curve}
/>
);
})}
diff --git a/packages/core-components/src/components/DependencyGraph/Edge.test.tsx b/packages/core-components/src/components/DependencyGraph/Edge.test.tsx
index db6fbe7552..21d677388e 100644
--- a/packages/core-components/src/components/DependencyGraph/Edge.test.tsx
+++ b/packages/core-components/src/components/DependencyGraph/Edge.test.tsx
@@ -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';
diff --git a/packages/core-components/src/components/DependencyGraph/Edge.tsx b/packages/core-components/src/components/DependencyGraph/Edge.tsx
index cc6a044beb..aae737b9ea 100644
--- a/packages/core-components/src/components/DependencyGraph/Edge.tsx
+++ b/packages/core-components/src/components/DependencyGraph/Edge.tsx
@@ -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 = {
id: dagre.Edge,
edge: DependencyEdge,
) => dagre.graphlib.Graph<{}>;
+ curve: 'curveStepBefore' | 'curveMonotoneX';
};
const renderDefault = (props: RenderLabelProps) => (
);
-const createPath = d3Shape
- .line()
- .x(d => d.x)
- .y(d => d.y)
- .curve(d3Shape.curveMonotoneX);
-
export function Edge({
render = renderDefault,
setEdge,
id,
edge,
+ curve,
}: EdgeComponentProps) {
const { x = 0, y = 0, width, height, points } = edge;
const labelProps: DependencyEdge = edge;
@@ -114,6 +110,16 @@ export function Edge({
let path: string = '';
+ const createPath = React.useMemo(
+ () =>
+ d3Shape
+ .line()
+ .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({
return (
<>
{path && (
-
+
)}
{labelProps.label ? (
JSX.Element;
@@ -120,6 +121,7 @@ export const EntityRelationsGraph: (props: {
renderLabel?:
| DependencyGraphTypes.RenderLabelFunction
| undefined;
+ curve?: 'curveStepBefore' | 'curveMonotoneX' | undefined;
}) => JSX.Element;
// @public
diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx
index 8e59c127d9..a6a6c2a711 100644
--- a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx
+++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx
@@ -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}
/>
+
diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx
new file mode 100644
index 0000000000..0be0fca66b
--- /dev/null
+++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx
@@ -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('', () => {
+ test('should display current curve label', () => {
+ const onChange = jest.fn();
+ const { getByText } = render(
+ ,
+ );
+
+ expect(getByText('Monotone X')).toBeInTheDocument();
+ });
+
+ test('should select an alternative curve factory', async () => {
+ const onChange = jest.fn();
+ const { getByText, getByTestId } = render(
+ ,
+ );
+
+ 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');
+ });
+ });
+});
diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.tsx
new file mode 100644
index 0000000000..6cda334cf1
--- /dev/null
+++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.tsx
@@ -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 = {
+ curveMonotoneX: 'Monotone X',
+ curveStepBefore: 'Step Before',
+};
+
+export type Props = {
+ value: Curve;
+ onChange: (value: 'curveStepBefore' | 'curveMonotoneX') => void;
+};
+
+const curves: Array = ['curveMonotoneX', 'curveStepBefore'];
+
+export const CurveFilter = ({ value, onChange }: Props) => {
+ const handleChange = useCallback(v => onChange(v as Curve), [onChange]);
+
+ return (
+
+
+ );
+};
diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts b/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts
index 308418f612..43da53c44a 100644
--- a/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts
+++ b/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts
@@ -46,6 +46,10 @@ export type CatalogGraphPageValue = {
setMergeRelations: Dispatch>;
direction: Direction;
setDirection: Dispatch>;
+ 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(() =>
typeof query.showFilters === 'string'
? query.showFilters === 'true'
@@ -249,6 +260,8 @@ export function useCatalogGraphPage({
setMergeRelations,
direction,
setDirection,
+ curve,
+ setCurve,
showFilters,
toggleShowFilters,
};
diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx
index 81c6a8f3c1..c0e2f610c1 100644
--- a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx
+++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx
@@ -82,6 +82,7 @@ export const EntityRelationsGraph = (props: {
zoom?: 'enabled' | 'disabled' | 'enable-on-click';
renderNode?: DependencyGraphTypes.RenderNodeFunction;
renderLabel?: DependencyGraphTypes.RenderLabelFunction;
+ 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}
/>
)}
diff --git a/plugins/techdocs-node/src/stages/publish/googleStorage.test.ts b/plugins/techdocs-node/src/stages/publish/googleStorage.test.ts
index 896e528644..4418e00c81 100644
--- a/plugins/techdocs-node/src/stages/publish/googleStorage.test.ts
+++ b/plugins/techdocs-node/src/stages/publish/googleStorage.test.ts
@@ -219,13 +219,13 @@ describe('GoogleGCSPublish', () => {
},
};
- beforeAll(() => {
+ beforeEach(() => {
mockFs({
[directory]: files,
});
});
- afterAll(() => {
+ afterEach(() => {
mockFs.restore();
});
diff --git a/yarn.lock b/yarn.lock
index 0f345a2ec2..b078398626 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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: