Add component table front-end (#848)

Co-authored-by: Wojciech Adaszynski <wojciecha@spotify.com>
This commit is contained in:
Wojciech Adaszyński
2020-05-14 08:46:45 +02:00
committed by GitHub
parent 5ed0ec374a
commit 359649086f
7 changed files with 219 additions and 39 deletions
@@ -16,19 +16,17 @@
import React from 'react';
import { render } from '@testing-library/react';
import mockFetch from 'jest-fetch-mock';
import CatalogPage from './CatalogPage';
import { ThemeProvider } from '@material-ui/core';
import { lightTheme } from '@backstage/theme';
describe('CatalogPage', () => {
it('should render', async () => {
mockFetch.mockResponse(() => new Promise(() => {}));
const rendered = render(
<ThemeProvider theme={lightTheme}>
<CatalogPage />
</ThemeProvider>,
);
expect(await rendered.findByText('backstage-backend')).toBeInTheDocument();
expect(await rendered.findByText('Your components')).toBeInTheDocument();
});
});
@@ -15,48 +15,25 @@
*/
import React, { FC } from 'react';
import { Typography } from '@material-ui/core';
import { Content, InfoCard, Header, Page, pageTheme } from '@backstage/core';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import { Content, Header, Page, pageTheme } from '@backstage/core';
import { useAsync } from 'react-use';
import { ComponentFactory } from '../../data/component';
import { MockComponentFactory } from '../../data/mock-factory';
import CatalogTable from '../CatalogTable/CatalogTable';
// TODO(freben): Connect to backend
const STATIC_DATA = [
{ id: 'backstage-frontend', kind: 'website' },
{ id: 'backstage-backend', kind: 'service' },
{ id: 'backstage-microsite', kind: 'website' },
];
const componentFactory: ComponentFactory = MockComponentFactory;
const CatalogPage: FC<{}> = () => {
const { value, error, loading } = useAsync(componentFactory.getAllComponents);
return (
<Page theme={pageTheme.home}>
<Header title="Catalog" subtitle="All your stuff" />
<Header title="Catalog" subtitle="Your components" />
<Content>
<Typography variant="h3">All of it</Typography>
<InfoCard>
<TableContainer>
<Table size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Kind</TableCell>
</TableRow>
</TableHead>
<TableBody>
{STATIC_DATA.map((d) => (
<TableRow key={d.id}>
<TableCell>{d.id}</TableCell>
<TableCell>{d.kind}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</InfoCard>
<CatalogTable
components={value || []}
loading={loading}
error={error}
/>
</Content>
</Page>
);
@@ -0,0 +1,56 @@
/*
* 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 * as React from 'react';
import { render } from '@testing-library/react';
import CatalogTable from './CatalogTable';
import { Component } from '../../data/component';
const components: Component[] = [
{ name: 'component1' },
{ name: 'component2' },
{ name: 'component3' },
];
describe('CatalogTable component', () => {
it('should render loading when loading prop it set to true', async () => {
const rendered = render(<CatalogTable components={[]} loading />);
const progress = await rendered.findByTestId('progress');
expect(progress).toBeInTheDOM();
});
it('should render error message when error is passed in props', async () => {
const rendered = render(
<CatalogTable
components={[]}
loading={false}
error={{ code: 'error' }}
/>,
);
const errorMessage = await rendered.findByText(
'Error encountered while fetching components.',
);
expect(errorMessage).toBeInTheDOM();
});
it('should display component names when loading has finished and no error occurred', async () => {
const rendered = render(
<CatalogTable components={components} loading={false} />,
);
expect(await rendered.findByText('component1')).toBeInTheDOM();
expect(await rendered.findByText('component2')).toBeInTheDOM();
expect(await rendered.findByText('component3')).toBeInTheDOM();
});
});
@@ -0,0 +1,60 @@
/*
* 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 { Component } from '../../data/component';
import { InfoCard, Progress, Table, TableColumn } from '@backstage/core';
import { Typography } from '@material-ui/core';
const columns: TableColumn[] = [
{
title: 'Name',
field: 'name',
highlight: true,
},
];
type CatalogTableProps = {
components: Component[];
loading: boolean;
error?: any;
};
const CatalogTable: FC<CatalogTableProps> = ({
components,
loading,
error,
}) => {
if (loading) {
return <Progress />;
}
if (error) {
return (
<InfoCard>
<Typography variant="subtitle1" paragraph>
Error encountered while fetching components.
</Typography>
</InfoCard>
);
}
return (
<Table
columns={columns}
options={{ paging: false }}
title="Your Services"
data={components}
/>
);
};
export default CatalogTable;
+23
View File
@@ -0,0 +1,23 @@
/*
* 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 Component = {
name: string;
};
export interface ComponentFactory {
getAllComponents(): Promise<Component[]>;
getComponentByName(name: string): Promise<Component | undefined>;
}
@@ -0,0 +1,35 @@
[
{
"name": "example.com"
},
{
"name": "subdomain.example.com"
},
{
"name": "subdomain2.example.com"
},
{
"name": "User data pipeline 1"
},
{
"name": "User data pipeline 2"
},
{
"name": "User data pipeline 3"
},
{
"name": "Aggregation CRON job"
},
{
"name": "Authentication service"
},
{
"name": "Payments service"
},
{
"name": "Backstage supervisor"
},
{
"name": "Identity service"
}
]
+31
View File
@@ -0,0 +1,31 @@
/*
* 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 { Component, ComponentFactory } from './component';
import mock from './mock-factory-data.json';
export const MockComponentFactory: ComponentFactory = {
getAllComponents(): Promise<Component[]> {
return new Promise((resolve) => setTimeout(() => resolve(mock), 2000));
},
getComponentByName(name: string): Promise<Component | undefined> {
return new Promise((resolve) =>
setTimeout(
() => resolve(mock.find((component) => component.name === name)),
2000,
),
);
},
};