Merge pull request #1508 from spotify/emmaindal/techdocs-base-components

techdocs: base components + basic docs not found page
This commit is contained in:
Emma Indal
2020-07-03 12:12:12 +02:00
committed by GitHub
8 changed files with 261 additions and 37 deletions
+2 -1
View File
@@ -30,6 +30,7 @@
*/
import { createPlugin, createRouteRef } from '@backstage/core';
import { TechDocsHome } from './reader/components/TechDocsHome';
import { Reader } from './reader/components/Reader';
export const rootRouteRef = createRouteRef({
@@ -45,7 +46,7 @@ export const rootDocsRouteRef = createRouteRef({
export const plugin = createPlugin({
id: 'techdocs',
register({ router }) {
router.addRoute(rootRouteRef, Reader);
router.addRoute(rootRouteRef, TechDocsHome);
router.addRoute(rootDocsRouteRef, Reader);
},
});
@@ -17,10 +17,9 @@
import React from 'react';
import { useShadowDom } from '..';
import { useAsync } from 'react-use';
import { useLocation, useParams, useNavigate } from 'react-router-dom';
import { AsyncState } from 'react-use/lib/useAsync';
import { Grid } from '@material-ui/core';
import { Header, Content, ItemCard } from '@backstage/core';
import { useLocation, useParams, useNavigate } from 'react-router-dom';
import transformer, {
addBaseUrl,
@@ -31,10 +30,15 @@ import transformer, {
} from '../transformers';
import { docStorageURL } from '../../config';
import URLFormatter from '../urlFormatter';
import { TechDocsNotFound } from './TechDocsNotFound';
import { TechDocsPageWrapper } from './TechDocsPageWrapper';
const useFetch = (url: string) => {
const useFetch = (url: string): AsyncState<string | Error> => {
const state = useAsync(async () => {
const response = await fetch(url);
if (response.status === 404) {
return new Error('Page not found');
}
const raw = await response.text();
return raw;
}, [url]);
@@ -67,7 +71,11 @@ export const Reader = () => {
React.useEffect(() => {
const divElement = shadowDomRef.current;
if (divElement?.shadowRoot && state.value) {
if (
divElement?.shadowRoot &&
state.value &&
!(state.value instanceof Error)
) {
const transformedElement = transformer(state.value, [
addBaseUrl({
docStorageURL,
@@ -116,39 +124,13 @@ export const Reader = () => {
}
}, [shadowDomRef, state, componentId, path, navigate]);
if (state.value instanceof Error) return <TechDocsNotFound />;
return (
<>
<Header
title={componentId ?? 'Documentation'}
subtitle={componentId ?? 'Documentation available in Backstage'}
/>
<Content>
{componentId ? (
<div ref={shadowDomRef} />
) : (
<Grid container>
<Grid item xs={12} sm={6} md={3}>
<ItemCard
onClick={() => 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. "
/>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<ItemCard
onClick={() => 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. "
/>
</Grid>
</Grid>
)}
</Content>
<TechDocsPageWrapper title={componentId} subtitle={componentId}>
<div ref={shadowDomRef} />
</TechDocsPageWrapper>
</>
);
};
@@ -0,0 +1,36 @@
/*
* 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 { TechDocsHome } from './TechDocsHome';
import React from 'react';
import { render } from '@testing-library/react';
import { wrapInTestApp } from '@backstage/test-utils';
describe('TechDocs Home', () => {
it('should render a TechDocs home page', () => {
const { getByTestId, queryByText } = render(
wrapInTestApp(<TechDocsHome />),
);
// Header
expect(queryByText('Documentation')).toBeInTheDocument();
expect(
queryByText(/Documentation available in Backstage/i),
).toBeInTheDocument();
// Explore Content
expect(getByTestId('docs-explore')).toBeDefined();
});
});
@@ -0,0 +1,74 @@
/*
* 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 { useNavigate } from 'react-router-dom';
import { Grid } from '@material-ui/core';
import { ItemCard } from '@backstage/core';
import { TechDocsPageWrapper } from './TechDocsPageWrapper';
type DocumentationSite = {
title: string;
description: string;
tags: Array<string>;
path: string;
btnLabel: string;
};
const documentationSites: Array<DocumentationSite> = [
{
title: 'MkDocs',
description:
"MkDocs is a fast, simple and downright gorgeous static site generator that's geared towards building project documentation. ",
tags: ['Developer Tool'],
path: '/docs/mkdocs',
btnLabel: 'Read Docs',
},
{
title: 'Backstage Docs',
description:
'Getting started guides, API Overview, documentation around how to Create a Plugin and more. ',
tags: ['Service'],
path: '/docs/backstage-microsite',
btnLabel: 'Read Docs',
},
];
export const TechDocsHome = () => {
const navigate = useNavigate();
return (
<>
<TechDocsPageWrapper
title="Documentation"
subtitle="Documentation available in Backstage"
>
<Grid container data-testid="docs-explore">
{documentationSites.map((site: DocumentationSite, index: number) => (
<Grid key={index} item xs={12} sm={6} md={3}>
<ItemCard
onClick={() => navigate(site.path)}
tags={site.tags}
title={site.title}
label={site.btnLabel}
description={site.description}
/>
</Grid>
))}
</Grid>
</TechDocsPageWrapper>
</>
);
};
@@ -0,0 +1,26 @@
/*
* 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 { TechDocsNotFound } from './TechDocsNotFound';
import React from 'react';
import { render } from '@testing-library/react';
import { wrapInTestApp } from '@backstage/test-utils';
describe('TechDocs Not Found', () => {
it('should render a Documentation not found page', async () => {
const { queryByText } = render(wrapInTestApp(<TechDocsNotFound />));
expect(queryByText(/error: documentation not found/i)).toBeInTheDocument();
});
});
@@ -0,0 +1,34 @@
/*
* 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 { Typography, Button } from '@material-ui/core';
import { TechDocsPageWrapper } from './TechDocsPageWrapper';
export const TechDocsNotFound = () => {
return (
<TechDocsPageWrapper
title="Documentation"
subtitle="Documentation available in Backstage"
>
<Typography>Error: Documentation not found</Typography>
<Typography>Path: {window.location.pathname}</Typography>
<Button color="primary" onClick={() => window.history.back()}>
Go back
</Button>
</TechDocsPageWrapper>
);
};
@@ -0,0 +1,34 @@
/*
* 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 { TechDocsPageWrapper } from './TechDocsPageWrapper';
import { TechDocsHome } from './TechDocsHome';
import React from 'react';
import { render } from '@testing-library/react';
import { wrapInTestApp } from '@backstage/test-utils';
describe('TechDocs Page Wrapper', () => {
it('should render a TechDocs Page Wrapper', async () => {
const { queryByText } = render(
wrapInTestApp(
<TechDocsPageWrapper title="test-title" subtitle="test-subtitle">
<TechDocsHome />
</TechDocsPageWrapper>,
),
);
expect(queryByText(/test-title/i)).toBeInTheDocument();
expect(queryByText(/test-subtitle/i)).toBeInTheDocument();
});
});
@@ -0,0 +1,37 @@
/*
* 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 { Header, Content } from '@backstage/core';
type TechDocsPageWrapperProps = {
title: string;
subtitle: string;
children: any;
};
export const TechDocsPageWrapper = ({
children,
title,
subtitle,
}: TechDocsPageWrapperProps) => {
return (
<>
<Header title={title} subtitle={subtitle} />
<Content>{children}</Content>
</>
);
};