Service Catalog improvements (#976)

* Service Catalog improvements

* Example table data

* Add Create button

* ContentHeader margin

* Fix test

* Checking components value

* Wrap tests in wrapInThemedTestApp

* tests use toBeInTheDocument
This commit is contained in:
Stefan Ålund
2020-05-25 10:24:51 +02:00
committed by GitHub
parent a1357d2710
commit acade31849
14 changed files with 93 additions and 118 deletions
+1
View File
@@ -23,6 +23,7 @@ export { default as Content } from './layout/Content/Content';
export { default as ContentHeader } from './layout/ContentHeader/ContentHeader';
export { default as Header } from './layout/Header/Header';
export { default as HeaderLabel } from './layout/HeaderLabel';
export { default as HomepageTimer } from './layout/HomepageTimer';
export { default as InfoCard } from './layout/InfoCard';
export { CardTab, TabbedCard } from './layout/TabbedCard';
export { default as ErrorBoundary } from './layout/ErrorBoundary';
@@ -30,6 +30,7 @@ const useStyles = makeStyles(theme => ({
flexWrap: 'wrap',
justifyContent: 'flex-end',
alignItems: 'center',
marginBottom: theme.spacing(1),
},
leftItemsBox: {
flex: '1 1 auto',
@@ -15,7 +15,7 @@
*/
import React, { FC } from 'react';
import { HeaderLabel } from '@backstage/core';
import HeaderLabel from '../HeaderLabel';
const timeFormat = { hour: '2-digit', minute: '2-digit' };
const utcOptions = { timeZone: 'UTC', ...timeFormat };
@@ -34,6 +34,8 @@ describe('CatalogPage', () => {
<CatalogPage componentFactory={testComponentFactory} />
</ThemeProvider>,
);
expect(await rendered.findByText('Your components')).toBeInTheDocument();
expect(
await rendered.findByText('Keep track of your software'),
).toBeInTheDocument();
});
});
@@ -15,10 +15,19 @@
*/
import React, { FC } from 'react';
import { Content, Header, Page, pageTheme } from '@backstage/core';
import {
Content,
ContentHeader,
Header,
HomepageTimer,
SupportButton,
Page,
pageTheme,
} from '@backstage/core';
import { useAsync } from 'react-use';
import { ComponentFactory } from '../../data/component';
import CatalogTable from '../CatalogTable/CatalogTable';
import { Button } from '@material-ui/core';
type CatalogPageProps = {
componentFactory: ComponentFactory;
@@ -27,8 +36,16 @@ const CatalogPage: FC<CatalogPageProps> = ({ componentFactory }) => {
const { value, error, loading } = useAsync(componentFactory.getAllComponents);
return (
<Page theme={pageTheme.home}>
<Header title="Catalog" subtitle="Your components" />
<Header title="Service Catalog" subtitle="Keep track of your software">
<HomepageTimer />
</Header>
<Content>
<ContentHeader title="Services">
<Button variant="contained" color="primary" href="/create">
Create Service
</Button>
<SupportButton>All your components</SupportButton>
</ContentHeader>
<CatalogTable
components={value || []}
loading={loading}
@@ -15,6 +15,7 @@
*/
import * as React from 'react';
import { render } from '@testing-library/react';
import { wrapInThemedTestApp } from '@backstage/test-utils';
import CatalogTable from './CatalogTable';
import { Component } from '../../data/component';
@@ -26,31 +27,37 @@ const components: Component[] = [
describe('CatalogTable component', () => {
it('should render loading when loading prop it set to true', async () => {
const rendered = render(<CatalogTable components={[]} loading />);
const rendered = render(
wrapInThemedTestApp(<CatalogTable components={[]} loading />),
);
const progress = await rendered.findByTestId('progress');
expect(progress).toBeInTheDOM();
expect(progress).toBeInTheDocument();
});
it('should render error message when error is passed in props', async () => {
const rendered = render(
<CatalogTable
components={[]}
loading={false}
error={{ code: 'error' }}
/>,
wrapInThemedTestApp(
<CatalogTable
components={[]}
loading={false}
error={{ code: 'error' }}
/>,
),
);
const errorMessage = await rendered.findByText(
'Error encountered while fetching components.',
);
expect(errorMessage).toBeInTheDOM();
expect(errorMessage).toBeInTheDocument();
});
it('should display component names when loading has finished and no error occurred', async () => {
const rendered = render(
<CatalogTable components={components} loading={false} />,
wrapInThemedTestApp(
<CatalogTable components={components} loading={false} />,
),
);
expect(await rendered.findByText('component1')).toBeInTheDOM();
expect(await rendered.findByText('component2')).toBeInTheDOM();
expect(await rendered.findByText('component3')).toBeInTheDOM();
expect(await rendered.findByText('component1')).toBeInTheDocument();
expect(await rendered.findByText('component2')).toBeInTheDocument();
expect(await rendered.findByText('component3')).toBeInTheDocument();
});
});
@@ -15,7 +15,13 @@
*/
import React, { FC } from 'react';
import { Component } from '../../data/component';
import { InfoCard, Progress, Table, TableColumn } from '@backstage/core';
import {
InfoCard,
Progress,
Table,
TableColumn,
StatusOK,
} from '@backstage/core';
import { Typography, Link } from '@material-ui/core';
const columns: TableColumn[] = [
@@ -27,6 +33,32 @@ const columns: TableColumn[] = [
<Link href={`/catalog/${componentData.name}`}>{componentData.name}</Link>
),
},
{
title: 'System',
field: 'system',
},
{
title: 'Owner',
field: 'owner',
},
{
title: 'Lifecycle',
field: 'lifecycle',
},
{
title: 'Status',
field: 'status',
render: (componentData: any) => (
<>
<StatusOK />
{componentData.status || 'Up and running'}
</>
),
},
{
title: 'Description',
field: 'description',
},
];
type CatalogTableProps = {
@@ -55,7 +87,7 @@ const CatalogTable: FC<CatalogTableProps> = ({
<Table
columns={columns}
options={{ paging: false }}
title="Your Services"
title={`Owned (${(components && components.length) || 0})`}
data={components}
/>
);
@@ -28,7 +28,7 @@ describe('ComponentContextMenu', () => {
const button = await menu.findByTestId('menu-button');
button.click();
const unregister = await menu.findByText('Unregister component');
expect(unregister).toBeInTheDOM();
expect(unregister).toBeInTheDocument();
});
});
});
@@ -26,12 +26,12 @@ describe('ComponentMetadataCard component', () => {
const rendered = await render(
<ComponentMetadataCard loading={false} component={testComponent} />,
);
expect(await rendered.findByText('test')).toBeInTheDOM();
expect(await rendered.findByText('test')).toBeInTheDocument();
});
it('should display loader when loading is set to true', async () => {
const rendered = await render(
<ComponentMetadataCard loading component={undefined} />,
);
expect(await rendered.findByRole('progressbar')).toBeInTheDOM();
expect(await rendered.findByRole('progressbar')).toBeInTheDocument();
});
});
@@ -16,8 +16,14 @@
import React, { FC } from 'react';
import { Typography, Link, Grid } from '@material-ui/core';
import HomePageTimer from '../HomepageTimer';
import { Content, InfoCard, Header, Page, pageTheme } from '@backstage/core';
import {
Content,
InfoCard,
Header,
HomepageTimer,
Page,
pageTheme,
} from '@backstage/core';
import SquadTechHealth from './SquadTechHealth';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
@@ -52,7 +58,7 @@ const HomePage: FC<{}> = () => {
title={profile ? `Hello, ${profile.givenName}` : 'Hello'}
subtitle="Welcome to Backstage"
>
<HomePageTimer />
<HomepageTimer />
</Header>
<Content>
<Grid container direction="row" spacing={3}>
@@ -68,7 +74,7 @@ const HomePage: FC<{}> = () => {
</TableRow>
</TableHead>
<TableBody>
{data.map((d) => (
{data.map(d => (
<TableRow key={d.id}>
<TableCell>{d.entity}</TableCell>
<TableCell>{d.kind}</TableCell>
@@ -1,74 +0,0 @@
/*
* 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 { HeaderLabel } from '@backstage/core';
const timeFormat = { hour: '2-digit', minute: '2-digit' };
const utcOptions = { timeZone: 'UTC', ...timeFormat };
const nycOptions = { timeZone: 'America/New_York', ...timeFormat };
const tyoOptions = { timeZone: 'Asia/Tokyo', ...timeFormat };
const stoOptions = { timeZone: 'Europe/Stockholm', ...timeFormat };
const defaultTimes = {
timeNY: '',
timeUTC: '',
timeTYO: '',
timeSTO: '',
};
function getTimes() {
const d = new Date();
const lang = window.navigator.language;
// Using the browser native toLocaleTimeString instead of huge moment-tz
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString
const timeNY = d.toLocaleTimeString(lang, nycOptions);
const timeUTC = d.toLocaleTimeString(lang, utcOptions);
const timeTYO = d.toLocaleTimeString(lang, tyoOptions);
const timeSTO = d.toLocaleTimeString(lang, stoOptions);
return { timeNY, timeUTC, timeTYO, timeSTO };
}
const HomePageTimer: FC<{}> = () => {
const [{ timeNY, timeUTC, timeTYO, timeSTO }, setTimes] = React.useState(
defaultTimes,
);
React.useEffect(() => {
setTimes(getTimes());
const intervalId = setInterval(() => {
setTimes(getTimes());
}, 1000);
return () => {
clearInterval(intervalId);
};
}, []);
return (
<>
<HeaderLabel label="NYC" value={timeNY} />
<HeaderLabel label="UTC" value={timeUTC} />
<HeaderLabel label="STO" value={timeSTO} />
<HeaderLabel label="TYO" value={timeTYO} />
</>
);
};
export default HomePageTimer;
@@ -1,17 +0,0 @@
/*
* 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 { default } from './Timer';
@@ -24,11 +24,11 @@ import {
ListItemText,
Link,
} from '@material-ui/core';
import Timer from '../Timer';
import {
Content,
InfoCard,
Header,
HomepageTimer,
Page,
pageTheme,
ContentHeader,
@@ -45,7 +45,7 @@ const WelcomePage: FC<{}> = () => {
title={`Welcome ${profile.givenName || 'to Backstage'}`}
subtitle="Let's start building a better developer experience"
>
<Timer />
<HomepageTimer />
</Header>
<Content>
<ContentHeader title="Getting Started">