Implement explorer page
This commit is contained in:
+15
-14
@@ -15,30 +15,30 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
createApp,
|
||||
AlertDisplay,
|
||||
OAuthRequestDialog,
|
||||
SignInPage,
|
||||
createApp,
|
||||
createRouteRef,
|
||||
FlatRoutes,
|
||||
OAuthRequestDialog,
|
||||
SignInPage,
|
||||
} from '@backstage/core';
|
||||
import React from 'react';
|
||||
import Root from './components/Root';
|
||||
import * as plugins from './plugins';
|
||||
import { apis } from './apis';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
import { providers } from './identityProviders';
|
||||
import { Router as CatalogRouter } from '@backstage/plugin-catalog';
|
||||
import { Router as DocsRouter } from '@backstage/plugin-techdocs';
|
||||
import { Router as ImportComponentRouter } from '@backstage/plugin-catalog-import';
|
||||
import { ExplorePage } from '@backstage/plugin-explore';
|
||||
import { Router as GraphiQLRouter } from '@backstage/plugin-graphiql';
|
||||
import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar';
|
||||
import { Router as LighthouseRouter } from '@backstage/plugin-lighthouse';
|
||||
import { Router as RegisterComponentRouter } from '@backstage/plugin-register-component';
|
||||
import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar';
|
||||
import { Router as DocsRouter } from '@backstage/plugin-techdocs';
|
||||
import { Router as SettingsRouter } from '@backstage/plugin-user-settings';
|
||||
import { Router as ImportComponentRouter } from '@backstage/plugin-catalog-import';
|
||||
import { Route, Navigate } from 'react-router';
|
||||
|
||||
import React from 'react';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
import { Navigate, Route } from 'react-router';
|
||||
import { apis } from './apis';
|
||||
import { EntityPage } from './components/catalog/EntityPage';
|
||||
import Root from './components/Root';
|
||||
import { providers } from './identityProviders';
|
||||
import * as plugins from './plugins';
|
||||
|
||||
const app = createApp({
|
||||
apis,
|
||||
@@ -78,6 +78,7 @@ const routes = (
|
||||
element={<CatalogRouter EntityPage={EntityPage} />}
|
||||
/>
|
||||
<Route path="/docs" element={<DocsRouter />} />
|
||||
<Route path="/explore" element={<ExplorePage />} />
|
||||
<Route
|
||||
path="/tech-radar"
|
||||
element={<TechRadarRouter width={1500} height={800} />}
|
||||
|
||||
@@ -83,10 +83,10 @@ const Root = ({ children }: PropsWithChildren<{}>) => (
|
||||
<SidebarItem icon={HomeIcon} to="/catalog" text="Home" />
|
||||
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />
|
||||
<SidebarItem icon={LibraryBooks} to="docs" text="Docs" />
|
||||
<SidebarItem icon={LayersIcon} to="explore" text="Explore" />
|
||||
<SidebarItem icon={CreateComponentIcon} to="create" text="Create..." />
|
||||
{/* End global nav */}
|
||||
<SidebarDivider />
|
||||
<SidebarItem icon={LayersIcon} to="explore" text="Explore" />
|
||||
<SidebarItem icon={MapIcon} to="tech-radar" text="Tech Radar" />
|
||||
<SidebarItem icon={RuleIcon} to="lighthouse" text="Lighthouse" />
|
||||
<SidebarItem icon={MoneyIcon} to="cost-insights" text="Cost Insights" />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# explore
|
||||
|
||||
Welcome to the explore plugin!
|
||||
This plugin helps to visualize the domains and in your ecosystem.
|
||||
This plugin helps to visualize the domains and tools in your ecosystem.
|
||||
|
||||
## Getting started
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog';
|
||||
import { plugin } from '../src/plugin';
|
||||
import React from 'react';
|
||||
import { ExplorePage } from '../src/extensions';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin(plugin)
|
||||
.registerApi({
|
||||
api: catalogApiRef,
|
||||
deps: {},
|
||||
@@ -56,4 +56,5 @@ createDevApp()
|
||||
},
|
||||
} as CatalogApi),
|
||||
})
|
||||
.addPage({ element: <ExplorePage />, title: 'Explore' })
|
||||
.render();
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
"start": "backstage-cli plugin:serve"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core": "^0.5.0",
|
||||
"@backstage/catalog-model": "^0.7.0",
|
||||
"@backstage/core": "^0.5.0",
|
||||
"@backstage/plugin-catalog": "^0.2.12",
|
||||
"@backstage/theme": "^0.2.2",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 { DomainEntity } from '@backstage/catalog-model';
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { DomainCardGrid } from './DomainCardGrid';
|
||||
|
||||
describe('<DomainCardGrid />', () => {
|
||||
it('renders a grid of domain cards', () => {
|
||||
const entities: DomainEntity[] = [
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Domain',
|
||||
metadata: {
|
||||
name: 'playback',
|
||||
},
|
||||
spec: {
|
||||
owner: 'guest',
|
||||
},
|
||||
},
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Domain',
|
||||
metadata: {
|
||||
name: 'artists',
|
||||
},
|
||||
spec: {
|
||||
owner: 'guest',
|
||||
},
|
||||
},
|
||||
];
|
||||
const { getByText } = render(<DomainCardGrid entities={entities} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
|
||||
expect(getByText('artists')).toBeInTheDocument();
|
||||
expect(getByText('playback')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
+3
-3
@@ -16,13 +16,13 @@
|
||||
import { DomainEntity } from '@backstage/catalog-model';
|
||||
import { Grid } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { DomainCard } from '../DomainCard';
|
||||
import { DomainCard } from '.';
|
||||
|
||||
type DomainExplorerProps = {
|
||||
type DomainCardGridProps = {
|
||||
entities: DomainEntity[];
|
||||
};
|
||||
|
||||
export const DomainExplorer = ({ entities }: DomainExplorerProps) => (
|
||||
export const DomainCardGrid = ({ entities }: DomainCardGridProps) => (
|
||||
<Grid container spacing={4}>
|
||||
{entities.map((e, i) => (
|
||||
<Grid item xs={12} md={3} key={i}>
|
||||
@@ -14,3 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { DomainCard } from './DomainCard';
|
||||
export { DomainCardGrid } from './DomainCardGrid';
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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 { DomainEntity } from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog';
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { DomainExplorerContent } from './DomainExplorerContent';
|
||||
|
||||
describe('<DomainExplorerContent />', () => {
|
||||
const catalogApi: jest.Mocked<typeof catalogApiRef.T> = {
|
||||
addLocation: jest.fn(_a => new Promise(() => {})),
|
||||
getEntities: jest.fn(),
|
||||
getLocationByEntity: jest.fn(),
|
||||
getLocationById: jest.fn(),
|
||||
removeEntityByUid: jest.fn(),
|
||||
getEntityByName: jest.fn(),
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<MemoryRouter>
|
||||
<ApiProvider apis={ApiRegistry.with(catalogApiRef, catalogApi)}>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('renders a grid of domains', async () => {
|
||||
const entities: DomainEntity[] = [
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Domain',
|
||||
metadata: {
|
||||
name: 'playback',
|
||||
},
|
||||
spec: {
|
||||
owner: 'guest',
|
||||
},
|
||||
},
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Domain',
|
||||
metadata: {
|
||||
name: 'artists',
|
||||
},
|
||||
spec: {
|
||||
owner: 'guest',
|
||||
},
|
||||
},
|
||||
];
|
||||
catalogApi.getEntities.mockResolvedValue({ items: entities });
|
||||
|
||||
const { getByText } = render(<DomainExplorerContent />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText('artists')).toBeInTheDocument();
|
||||
expect(getByText('playback')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders empty state', async () => {
|
||||
catalogApi.getEntities.mockResolvedValue({ items: [] });
|
||||
|
||||
const { getByText } = render(<DomainExplorerContent />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
await waitFor(() =>
|
||||
expect(getByText('No domains to display')).toBeInTheDocument(),
|
||||
);
|
||||
});
|
||||
});
|
||||
+3
-3
@@ -26,7 +26,7 @@ import { catalogApiRef } from '@backstage/plugin-catalog';
|
||||
import { Button } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
import { DomainExplorer } from '../DomainExplorer/DomainExplorer';
|
||||
import { DomainCardGrid } from '../DomainCard';
|
||||
|
||||
export const DomainExplorerContent = () => {
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
@@ -39,7 +39,7 @@ export const DomainExplorerContent = () => {
|
||||
}, [catalogApi]);
|
||||
|
||||
return (
|
||||
<Content>
|
||||
<Content noPadding>
|
||||
<ContentHeader title="Domains">
|
||||
<SupportButton>Discover the domains in your ecosystem.</SupportButton>
|
||||
</ContentHeader>
|
||||
@@ -60,7 +60,7 @@ export const DomainExplorerContent = () => {
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{!loading && entities && <DomainExplorer entities={entities} />}
|
||||
{!loading && entities && <DomainCardGrid entities={entities} />}
|
||||
</Content>
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -13,4 +13,4 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { DomainExplorerPage } from './DomainExplorerPage';
|
||||
export { DomainExplorerContent } from './DomainExplorerContent';
|
||||
@@ -25,7 +25,7 @@ export const ExplorePage = () => {
|
||||
<Page themeId="home">
|
||||
<Header
|
||||
title={`Explore the ${organizationName} ecosystem`}
|
||||
subtitle="Discover available solutions in your ecosystem"
|
||||
subtitle="Discover solutions available in your ecosystem"
|
||||
/>
|
||||
|
||||
<ExploreTabs />
|
||||
|
||||
@@ -14,22 +14,36 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Tabs } from '@backstage/core';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { DomainExplorerContent } from '../DomainExplorerPage/DomainExplorerContent';
|
||||
import { DomainExplorerContent } from '../DomainExplorerContent';
|
||||
import { ToolExplorerContent } from '../ToolExplorerContent';
|
||||
|
||||
// TODO: Support sub routes for these tabs in the future
|
||||
|
||||
const useStyles = makeStyles({
|
||||
layout: {
|
||||
gridArea: 'pageContent',
|
||||
},
|
||||
});
|
||||
|
||||
export const ExploreTabs = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
tabs={[
|
||||
{
|
||||
label: `Domains`,
|
||||
content: <DomainExplorerContent />,
|
||||
},
|
||||
{
|
||||
label: `Tools`,
|
||||
content: <DomainExplorerContent />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div className={classes.layout}>
|
||||
<Tabs
|
||||
tabs={[
|
||||
{
|
||||
label: `Domains`,
|
||||
content: <DomainExplorerContent />,
|
||||
},
|
||||
{
|
||||
label: `Tools`,
|
||||
content: <ToolExplorerContent />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+10
-11
@@ -14,11 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import ExploreCard from './ExploreCard';
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ToolCard } from './ToolCard';
|
||||
|
||||
const minProps = {
|
||||
card: {
|
||||
@@ -30,20 +29,20 @@ const minProps = {
|
||||
},
|
||||
};
|
||||
|
||||
describe('<ExploreCard />', () => {
|
||||
describe('<ToolCard />', () => {
|
||||
it('renders without exploding', () => {
|
||||
const { getByText } = render(wrapInTestApp(<ExploreCard {...minProps} />));
|
||||
const { getByText } = render(wrapInTestApp(<ToolCard {...minProps} />));
|
||||
expect(getByText('Explore')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders props correctly', () => {
|
||||
const { getByText } = render(wrapInTestApp(<ExploreCard {...minProps} />));
|
||||
const { getByText } = render(wrapInTestApp(<ToolCard {...minProps} />));
|
||||
expect(getByText(minProps.card.title)).toBeInTheDocument();
|
||||
expect(getByText(minProps.card.description)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should link out', () => {
|
||||
const rendered = render(wrapInTestApp(<ExploreCard {...minProps} />));
|
||||
const rendered = render(wrapInTestApp(<ToolCard {...minProps} />));
|
||||
const anchor = rendered.container.querySelector('a');
|
||||
expect(anchor.href).toBe(minProps.card.url);
|
||||
});
|
||||
@@ -59,7 +58,7 @@ describe('<ExploreCard />', () => {
|
||||
},
|
||||
};
|
||||
const { getByText } = render(
|
||||
wrapInTestApp(<ExploreCard {...propsWithoutDescription} />),
|
||||
wrapInTestApp(<ToolCard {...propsWithoutDescription} />),
|
||||
);
|
||||
expect(getByText('Description missing')).toBeInTheDocument();
|
||||
});
|
||||
@@ -74,13 +73,13 @@ describe('<ExploreCard />', () => {
|
||||
},
|
||||
};
|
||||
const { queryByText } = render(
|
||||
wrapInTestApp(<ExploreCard {...propsWithLifecycle} />),
|
||||
wrapInTestApp(<ToolCard {...propsWithLifecycle} />),
|
||||
);
|
||||
expect(queryByText('GA')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders tags correctly', () => {
|
||||
const { getByText } = render(wrapInTestApp(<ExploreCard {...minProps} />));
|
||||
const { getByText } = render(wrapInTestApp(<ToolCard {...minProps} />));
|
||||
expect(getByText(minProps.card.tags[0])).toBeInTheDocument();
|
||||
expect(getByText(minProps.card.tags[1])).toBeInTheDocument();
|
||||
});
|
||||
+8
-17
@@ -14,8 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
@@ -23,10 +22,14 @@ import {
|
||||
CardContent,
|
||||
CardMedia,
|
||||
Chip,
|
||||
Typography,
|
||||
makeStyles,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import { CardData } from './types';
|
||||
|
||||
// TODO: Align styling between Domain and ToolCard
|
||||
|
||||
const useStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
card: {
|
||||
@@ -65,22 +68,12 @@ const useStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export type CardData = {
|
||||
title: string;
|
||||
description: string;
|
||||
url: string;
|
||||
image: string;
|
||||
tags?: string[];
|
||||
lifecycle?: string;
|
||||
newsTag?: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
card: CardData;
|
||||
objectFit?: 'cover' | 'contain';
|
||||
};
|
||||
|
||||
const ExploreCard = ({ card, objectFit }: Props) => {
|
||||
export const ToolCard = ({ card, objectFit }: Props) => {
|
||||
const classes = useStyles();
|
||||
|
||||
const { title, description, url, image, lifecycle, newsTag, tags } = card;
|
||||
@@ -130,5 +123,3 @@ const ExploreCard = ({ card, objectFit }: Props) => {
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExploreCard;
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 { BackstageTheme } from '@backstage/theme';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { ToolCard } from './ToolCard';
|
||||
import { CardData } from './types';
|
||||
|
||||
const useStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
container: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fill, 296px)',
|
||||
gridGap: theme.spacing(3),
|
||||
marginBottom: theme.spacing(6),
|
||||
},
|
||||
}));
|
||||
|
||||
type ToolCardGridProps = {
|
||||
tools: CardData[];
|
||||
};
|
||||
|
||||
export const ToolCardGrid = ({ tools }: ToolCardGridProps) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className={classes.container}>
|
||||
{tools.map((card: CardData, ix: any) => (
|
||||
<ToolCard card={card} key={ix} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+3
-19
@@ -13,22 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { configApiRef, Header, Page, useApi } from '@backstage/core';
|
||||
import React from 'react';
|
||||
import { DomainExplorerContent } from './DomainExplorerContent';
|
||||
|
||||
export const DomainExplorerPage = () => {
|
||||
const configApi = useApi(configApiRef);
|
||||
const organizationName =
|
||||
configApi.getOptionalString('organization.name') ?? 'Backstage';
|
||||
|
||||
return (
|
||||
<Page themeId="home">
|
||||
<Header
|
||||
title={`Explore the ${organizationName} ecosystem`}
|
||||
subtitle="Discover the domains in your ecosystem"
|
||||
/>
|
||||
<DomainExplorerContent />
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
export { ToolCard } from './ToolCard';
|
||||
export { ToolCardGrid } from './ToolCardGrid';
|
||||
export type { CardData } from './types';
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type CardData = {
|
||||
title: string;
|
||||
description: string;
|
||||
url: string;
|
||||
image: string;
|
||||
tags?: string[];
|
||||
lifecycle?: string;
|
||||
newsTag?: string;
|
||||
};
|
||||
+11
-41
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
* Copyright 2021 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,27 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Content, ContentHeader, SupportButton } from '@backstage/core';
|
||||
import React from 'react';
|
||||
import { makeStyles, Typography } from '@material-ui/core';
|
||||
import {
|
||||
Content,
|
||||
ContentHeader,
|
||||
Header,
|
||||
Page,
|
||||
SupportButton,
|
||||
} from '@backstage/core';
|
||||
import ExploreCard, { CardData } from './ExploreCard';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import { ToolCardGrid } from '../ToolCard';
|
||||
|
||||
const useStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
container: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fill, 296px)',
|
||||
gridGap: theme.spacing(3),
|
||||
marginBottom: theme.spacing(6),
|
||||
},
|
||||
}));
|
||||
// TODO: Provide API to configure the toolsCards
|
||||
|
||||
const toolsCards = [
|
||||
{
|
||||
@@ -113,27 +97,13 @@ const toolsCards = [
|
||||
},
|
||||
];
|
||||
|
||||
export const ExplorePluginPage = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
export const ToolExplorerContent = () => {
|
||||
return (
|
||||
<Page themeId="home">
|
||||
<Header
|
||||
title="Explore"
|
||||
subtitle="Tools and services available in Backstage"
|
||||
/>
|
||||
<Content>
|
||||
<ContentHeader title="Tools">
|
||||
<SupportButton>
|
||||
<Typography>Explore tools available in Backstage</Typography>
|
||||
</SupportButton>
|
||||
</ContentHeader>
|
||||
<div className={classes.container}>
|
||||
{toolsCards.map((card: CardData, ix: any) => (
|
||||
<ExploreCard card={card} key={ix} />
|
||||
))}
|
||||
</div>
|
||||
</Content>
|
||||
</Page>
|
||||
<Content noPadding>
|
||||
<ContentHeader title="Tools">
|
||||
<SupportButton>Discover the tools in your ecosystem.</SupportButton>
|
||||
</ContentHeader>
|
||||
<ToolCardGrid tools={toolsCards} />
|
||||
</Content>
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -13,4 +13,4 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { DomainExplorer } from './DomainExplorer';
|
||||
export { ToolExplorerContent } from './ToolExplorerContent';
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { plugin } from './plugin';
|
||||
import { explorePlugin } from './plugin';
|
||||
|
||||
describe('explore', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(plugin).toBeDefined();
|
||||
expect(explorePlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,5 @@ const NoIcon = () => null;
|
||||
|
||||
export const exploreRouteRef = createRouteRef({
|
||||
icon: NoIcon,
|
||||
path: '',
|
||||
title: 'Explore',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user