+
{ring.name}
{entries.length === 0 ? (
(empty)
) : (
- {entries.map(entry => {
- let node = {entry.title};
-
- if (entry.url) {
- node = (
-
- {node}
-
- );
- }
-
- return (
- - onEntryMouseEnter(entry))
- }
- onMouseLeave={
- onEntryMouseLeave && (() => onEntryMouseLeave(entry))
- }
- >
- {node}
-
- );
- })}
+ {entries.map(entry => (
+ - onEntryMouseEnter(entry))
+ }
+ onMouseLeave={
+ onEntryMouseLeave && (() => onEntryMouseLeave(entry))
+ }
+ >
+
+ {entry.title}
+
+
+ ))}
)}
);
};
- const _renderQuadrant = (
- segments: Segments,
- quadrant: Quadrant,
- rings: Ring[],
- onEntryMouseEnter: Props['onEntryMouseEnter'],
- onEntryMouseLeave: Props['onEntryMouseLeave'],
- ) => {
+ type RadarLegendQuadrantProps = {
+ segments: Segments;
+ quadrant: Quadrant;
+ rings: Ring[];
+ onEntryMouseEnter: Props['onEntryMouseEnter'];
+ onEntryMouseLeave: Props['onEntryMouseLeave'];
+ };
+
+ const RadarLegendQuadrant = ({
+ segments,
+ quadrant,
+ rings,
+ onEntryMouseEnter,
+ onEntryMouseLeave,
+ }: RadarLegendQuadrantProps) => {
return (
= props => {
y={quadrant.legendY}
width={quadrant.legendWidth}
height={quadrant.legendHeight}
+ data-testid="radar-quadrant"
>
{quadrant.name}
- {rings.map(ring =>
- _renderRing(
- ring,
- _getSegment(segments, quadrant, ring),
- onEntryMouseEnter,
- onEntryMouseLeave,
- ),
- )}
+ {rings.map(ring => (
+
+ ))}
);
};
- const _setupSegments = (entries: Entry[]) => {
+ const setupSegments = (entries: Entry[]) => {
const segments: Segments = {};
for (const entry of entries) {
- const qidx = entry.quadrant.idx;
- const ridx = entry.ring.idx;
+ const quadrantIndex = entry.quadrant.index;
+ const ringIndex = entry.ring.index;
let quadrantData: { [k: number]: Entry[] } = {};
- if (qidx !== undefined) {
- if (segments[qidx] === undefined) {
- segments[qidx] = {};
+ if (quadrantIndex !== undefined) {
+ if (segments[quadrantIndex] === undefined) {
+ segments[quadrantIndex] = {};
}
- quadrantData = segments[qidx];
+ quadrantData = segments[quadrantIndex];
}
let ringData = [];
- if (ridx !== undefined) {
- if (quadrantData[ridx] === undefined) {
- quadrantData[ridx] = [];
+ if (ringIndex !== undefined) {
+ if (quadrantData[ringIndex] === undefined) {
+ quadrantData[ringIndex] = [];
}
- ringData = quadrantData[ridx];
+ ringData = quadrantData[ringIndex];
}
ringData.push(entry);
@@ -209,19 +220,20 @@ const RadarLegend: FC
= props => {
onEntryMouseLeave,
} = props;
- const segments: Segments = _setupSegments(entries);
+ const segments: Segments = setupSegments(entries);
return (
-
- {quadrants.map(quadrant =>
- _renderQuadrant(
- segments,
- quadrant,
- rings,
- onEntryMouseEnter,
- onEntryMouseLeave,
- ),
- )}
+
+ {quadrants.map(quadrant => (
+
+ ))}
);
};
diff --git a/plugins/tech-radar/src/components/RadarPage.tsx b/plugins/tech-radar/src/components/RadarPage.tsx
index 4c7236e01f..1aefb72eca 100644
--- a/plugins/tech-radar/src/components/RadarPage.tsx
+++ b/plugins/tech-radar/src/components/RadarPage.tsx
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import React, { FC } from 'react';
+import React from 'react';
import { Grid } from '@material-ui/core';
import {
Content,
@@ -29,7 +29,7 @@ import {
import RadarComponent from '../components/RadarComponent';
import { techRadarApiRef, TechRadarApi } from '../api';
-const RadarPage: FC<{}> = () => {
+const RadarPage = (): JSX.Element => {
const techRadarApi = useApi(techRadarApiRef);
return (
diff --git a/plugins/tech-radar/src/components/RadarPlot/RadarPlot.test.tsx b/plugins/tech-radar/src/components/RadarPlot/RadarPlot.test.tsx
new file mode 100644
index 0000000000..5b17578478
--- /dev/null
+++ b/plugins/tech-radar/src/components/RadarPlot/RadarPlot.test.tsx
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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 React from 'react';
+import { render } from '@testing-library/react';
+import { ThemeProvider } from '@material-ui/core';
+import { lightTheme } from '@backstage/theme';
+import GetBBoxPolyfill from '../../utils/polyfills/getBBox';
+
+import RadarPlot, { Props } from './RadarPlot';
+
+const minProps: Props = {
+ width: 500,
+ height: 200,
+ radius: 50,
+ quadrants: [{ id: 'languages', name: 'Languages' }],
+ rings: [{ id: 'use', name: 'USE', color: '#93c47d' }],
+ entries: [
+ {
+ id: 'typescript',
+ title: 'TypeScript',
+ quadrant: { id: 'languages', name: 'Languages' },
+ moved: 0,
+ ring: { id: 'use', name: 'USE', color: '#93c47d' },
+ url: '#',
+ },
+ ],
+};
+
+describe('RadarPlot', () => {
+ beforeAll(() => {
+ GetBBoxPolyfill.create(0, 0, 1000, 500);
+ });
+
+ afterAll(() => {
+ GetBBoxPolyfill.remove();
+ });
+
+ it('should render', () => {
+ const rendered = render(
+
+
+ ,
+ );
+
+ expect(rendered.getByTestId('radar-plot')).toBeInTheDocument();
+ expect(rendered.getByTestId('radar-legend')).toBeInTheDocument();
+ expect(rendered.getByTestId('radar-footer')).toBeInTheDocument();
+ expect(rendered.getByTestId('radar-bubble')).toBeInTheDocument();
+ expect(rendered.getAllByTestId('radar-entry')).toHaveLength(1);
+ });
+});
diff --git a/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx b/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx
index 48ad660dde..7f68ffe021 100644
--- a/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx
+++ b/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import React, { FC } from 'react';
-import { Quadrant, Ring, Entry } from '../../utils/types';
+import React from 'react';
+import type { Quadrant, Ring, Entry } from '../../utils/types';
import RadarGrid from '../RadarGrid';
import RadarEntry from '../RadarEntry';
@@ -23,7 +23,7 @@ import RadarBubble from '../RadarBubble';
import RadarFooter from '../RadarFooter';
import RadarLegend from '../RadarLegend';
-type Props = {
+export type Props = {
width: number;
height: number;
radius: number;
@@ -36,7 +36,7 @@ type Props = {
};
// A component that draws the radar circle.
-const RadarPlot: FC = props => {
+const RadarPlot = (props: Props): JSX.Element => {
const {
width,
height,
@@ -50,7 +50,7 @@ const RadarPlot: FC = props => {
} = props;
return (
-
+
= props => {
x={entry.x || 0}
y={entry.y || 0}
color={entry.color || ''}
- number={((entry && entry.idx) || 0) + 1}
+ value={(entry?.index || 0) + 1}
url={entry.url}
moved={entry.moved}
onMouseEnter={onEntryMouseEnter && (() => onEntryMouseEnter(entry))}
@@ -80,9 +80,9 @@ const RadarPlot: FC = props => {
))}
diff --git a/plugins/tech-radar/src/utils/components.tsx b/plugins/tech-radar/src/utils/components.tsx
new file mode 100644
index 0000000000..af5a7b05bd
--- /dev/null
+++ b/plugins/tech-radar/src/utils/components.tsx
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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 React from 'react';
+
+type WithLinkProps = {
+ url?: string;
+ className: string;
+ children: React.ReactNode;
+};
+
+export const WithLink = ({
+ url,
+ className,
+ children,
+}: WithLinkProps): JSX.Element =>
+ url ? (
+
+ {children}
+
+ ) : (
+ <>{children}>
+ );
diff --git a/plugins/tech-radar/src/utils/types.ts b/plugins/tech-radar/src/utils/types.ts
index 14a0554a1f..f3933ee584 100644
--- a/plugins/tech-radar/src/utils/types.ts
+++ b/plugins/tech-radar/src/utils/types.ts
@@ -17,7 +17,7 @@
// Parameters for a ring; its index in an array determines how close to the center this ring is.
export type Ring = {
id: string;
- idx?: number;
+ index?: number;
name: string;
color: string;
outerRadius?: number;
@@ -27,7 +27,7 @@ export type Ring = {
// Parameters for a quadrant (there should be exactly 4 of course)
export type Quadrant = {
id: string;
- idx?: number;
+ index?: number;
name: string;
legendX?: number;
legendY?: number;
@@ -45,9 +45,15 @@ export type Segment = {
random: Function;
};
+export enum MovedState {
+ Down = -1,
+ NoChange = 0,
+ Up = 1,
+}
+
export type Entry = {
id: string;
- idx?: number;
+ index?: number;
x?: number;
y?: number;
color?: string;
@@ -61,7 +67,7 @@ export type Entry = {
// An URL to a longer description as to why this entry is where it is
url?: string;
// How this entry has recently moved; -1 for "down", +1 for "up", 0 for not moved
- moved?: number;
+ moved?: MovedState;
active?: boolean;
};
diff --git a/plugins/techdocs/README.md b/plugins/techdocs/README.md
index 10ddcf9411..36d00fc970 100644
--- a/plugins/techdocs/README.md
+++ b/plugins/techdocs/README.md
@@ -10,7 +10,7 @@ Welcome to the TechDocs plugin - Spotify's docs-like-code approach built directl
## Getting started
-Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/techdocs](http://localhost:3000/techdocs).
+Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/docs](http://localhost:3000/docs).
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
diff --git a/plugins/techdocs/src/plugin.ts b/plugins/techdocs/src/plugin.ts
index fec2b1cf74..89a5ddeb73 100644
--- a/plugins/techdocs/src/plugin.ts
+++ b/plugins/techdocs/src/plugin.ts
@@ -33,6 +33,11 @@ import { createPlugin, createRouteRef } from '@backstage/core';
import { Reader } from './reader/components/Reader';
export const rootRouteRef = createRouteRef({
+ path: '/docs',
+ title: 'TechDocs Landing Page',
+});
+
+export const rootDocsRouteRef = createRouteRef({
path: '/docs/:componentId/*',
title: 'Docs',
});
@@ -41,5 +46,6 @@ export const plugin = createPlugin({
id: 'techdocs',
register({ router }) {
router.addRoute(rootRouteRef, Reader);
+ router.addRoute(rootDocsRouteRef, Reader);
},
});
diff --git a/plugins/techdocs/src/reader/components/DocsCard.tsx b/plugins/techdocs/src/reader/components/DocsCard.tsx
new file mode 100644
index 0000000000..68f42bf84a
--- /dev/null
+++ b/plugins/techdocs/src/reader/components/DocsCard.tsx
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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 React, { FC } from 'react';
+import { Button, Card, Chip, Typography, makeStyles } from '@material-ui/core';
+
+const useStyles = makeStyles(theme => ({
+ header: {
+ color: theme.palette.common.white,
+ padding: theme.spacing(2, 2, 6),
+ backgroundImage:
+ 'linear-gradient(-137deg, rgb(25, 230, 140) 0%, rgb(29, 127, 110) 100%)',
+ },
+ content: {
+ padding: theme.spacing(2),
+ },
+ description: {
+ height: 175,
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ },
+ footer: {
+ display: 'flex',
+ flexDirection: 'row-reverse',
+ },
+}));
+
+type DocsCardProps = {
+ description: string;
+ tags?: string[];
+ title: string;
+ type?: string;
+ label: string;
+ onClick?: () => void;
+};
+export const DocsCard: FC = ({
+ description,
+ tags,
+ title,
+ type,
+ label,
+ onClick,
+}) => {
+ const classes = useStyles();
+
+ return (
+
+
+ {type ?? {type}}
+ {title}
+
+
+ {tags?.map(tag => (
+
+ ))}
+
+ {description}
+
+
+
+
+
+
+ );
+};
diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx
index 0175462cc3..a4ff03af47 100644
--- a/plugins/techdocs/src/reader/components/Reader.tsx
+++ b/plugins/techdocs/src/reader/components/Reader.tsx
@@ -17,15 +17,19 @@
import React from 'react';
import { useShadowDom } from '..';
import { useAsync } from 'react-use';
+import { useLocation, useParams, useNavigate } from 'react-router-dom';
+
+import { Grid } from '@material-ui/core';
+import { Header, Content } from '@backstage/core';
+
import transformer, {
addBaseUrl,
rewriteDocLinks,
addEventListener,
} from '../transformers';
import { docStorageURL } from '../../config';
-import { Link } from '@backstage/core';
-import { useLocation, useParams, useNavigate } from 'react-router-dom';
import URLParser from '../urlParser';
+import { DocsCard } from './DocsCard';
const useFetch = (url: string) => {
const state = useAsync(async () => {
@@ -89,11 +93,39 @@ export const Reader = () => {
return (
<>
-
-
+ {componentId ? (
+
+ ) : (
+ <>
+
+
+
+
+
+ navigate('/docs/mkdocs')}
+ tags={['Developer Tool']}
+ title="MkDocs"
+ label="Read Docs"
+ description="MkDocs is a fast, simple and downright gorgeous static site generator that's geared towards building project documentation. "
+ />
+
+
+ navigate('/docs/backstage-microsite')}
+ tags={['Service']}
+ title="Backstage"
+ label="Read Docs"
+ description="Getting started guides, API Overview, documentation around how to Create a Plugin and more. "
+ />
+
+
+
+ >
+ )}
>
);
};
diff --git a/yarn.lock b/yarn.lock
index 110e83481f..f3e2129cc9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7808,7 +7808,7 @@ dotenv@^6.2.0:
resolved "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
-dotenv@^8.0.0, dotenv@^8.2.0:
+dotenv@^8.0.0:
version "8.2.0"
resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==