From 39bdaa0046f41c395d0e43f7b00d6d4239471b30 Mon Sep 17 00:00:00 2001 From: "Chongyang Adrian, Ke" Date: Tue, 6 Apr 2021 17:18:38 +0800 Subject: [PATCH 01/12] Add TechDocs landing page customization and exported components Signed-off-by: Chongyang Adrian, Ke --- .changeset/techdocs-quiet-pens-prove.md | 5 + docs/features/techdocs/how-to-guides.md | 77 ++++++++ ...Content.test.tsx => DocsCardGrid.test.tsx} | 17 +- .../src/home/components/DocsCardGrid.tsx | 58 ++++++ ...nedContent.test.tsx => DocsTable.test.tsx} | 44 +---- .../{OwnedContent.tsx => DocsTable.tsx} | 68 +++---- .../src/home/components/OverviewContent.tsx | 73 -------- .../components/TechDocsCustomHome.test.tsx | 83 +++++++++ .../home/components/TechDocsCustomHome.tsx | 169 ++++++++++++++++++ .../src/home/components/TechDocsHome.test.tsx | 26 ++- .../src/home/components/TechDocsHome.tsx | 118 ++++-------- plugins/techdocs/src/index.ts | 5 + plugins/techdocs/src/plugin.ts | 39 ++++ 13 files changed, 538 insertions(+), 244 deletions(-) create mode 100644 .changeset/techdocs-quiet-pens-prove.md rename plugins/techdocs/src/home/components/{OverviewContent.test.tsx => DocsCardGrid.test.tsx} (77%) create mode 100644 plugins/techdocs/src/home/components/DocsCardGrid.tsx rename plugins/techdocs/src/home/components/{OwnedContent.test.tsx => DocsTable.test.tsx} (68%) rename plugins/techdocs/src/home/components/{OwnedContent.tsx => DocsTable.tsx} (67%) delete mode 100644 plugins/techdocs/src/home/components/OverviewContent.tsx create mode 100644 plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx create mode 100644 plugins/techdocs/src/home/components/TechDocsCustomHome.tsx diff --git a/.changeset/techdocs-quiet-pens-prove.md b/.changeset/techdocs-quiet-pens-prove.md new file mode 100644 index 0000000000..379c20e72a --- /dev/null +++ b/.changeset/techdocs-quiet-pens-prove.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Add customization and exportable components for TechDocs landing page diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index 2cb769b0e3..a76905d6c5 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -82,3 +82,80 @@ Caveat: Currently TechDocs sites built using URL Reader will be cached for 30 minutes which means they will not be re-built if new changes are made within 30 minutes. This cache invalidation will be replaced by commit timestamp based implementation very soon. + +## How to use a custom TechDocs home page? + +### 1st way: TechDocsCustomHome with a custom configuration + +In your main App.tsx: + +``` +import { + TechDocsCustomHome, + WidgetType, + TechDocsReaderPage, +} from '@backstage/plugin-techdocs'; +import { Entity } from '@backstage/catalog-model'; + +const tabsConfig = [ + { + label: 'Custom Tab', + widgets: [ + { + title: 'Custom Documents Cards 1', + description: + 'Explore your internal technical ecosystem through documentation.', + // sets maximum height of widget, as CSS maxHeight attribute + widgetMaxHeight: '400px' + widgetType: 'DocsCardGrid' as WidgetType, + filterPredicate: (entity: Entity) => !!entity.metadata.annotations?.['customCardAnnotationOne']; + }, + { + title: 'Custom Documents Cards 2', + description: + 'Explore your internal technical ecosystem through documentation.', + widgetMaxHeight: '400px' + widgetType: 'DocsCardGrid' as WidgetType, + filterPredicate: (entity: Entity) => !!entity.metadata.annotations?.['customCardAnnotationTwo']; + }, + ], + }, + { + label: 'Overview', + widgets: [ + { + title: 'Overview', + description: + 'Explore your internal technical ecosystem through documentation.', + widgetType: 'DocsTable' as WidgetType, + filterPredicate: () => true, + }, + ], + }, +]; + +const routes = ( + + } + /> + } + /> + +``` + +An example of tabsConfig that corresponds to the default documentation home page +can be found at `plugins/techdocs/src/home/components/TechDocsHome.tsx`. + +### 2nd way: custom home page plugin + +A custom home page plugin can be built that uses the components extensions +DocsCardGrid and DocsTable, exported from @backstage/techdocs. They both take a +array of documentation entities ( have 'backstage.io/techdocs-ref' annotation ) +as an 'entities' attribute. + +For an reference to the React structure of the default home page, please refer +to `plugins/techdocs/src/home/components/TechDocsCustomHome.tsx`. diff --git a/plugins/techdocs/src/home/components/OverviewContent.test.tsx b/plugins/techdocs/src/home/components/DocsCardGrid.test.tsx similarity index 77% rename from plugins/techdocs/src/home/components/OverviewContent.test.tsx rename to plugins/techdocs/src/home/components/DocsCardGrid.test.tsx index ec67c346d7..1ddaa4c373 100644 --- a/plugins/techdocs/src/home/components/OverviewContent.test.tsx +++ b/plugins/techdocs/src/home/components/DocsCardGrid.test.tsx @@ -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. @@ -17,13 +17,13 @@ import { wrapInTestApp } from '@backstage/test-utils'; import { render } from '@testing-library/react'; import React from 'react'; -import { OverviewContent } from './OverviewContent'; +import { DocsCardGrid } from './DocsCardGrid'; -describe('TechDocs Overview Content', () => { - it('should render all TechDocs Documents', async () => { +describe('Entity Docs Card Grid', () => { + it('should render all entities passed ot it', async () => { const { findByText } = render( wrapInTestApp( - { />, ), ); - - expect(await findByText('Overview')).toBeInTheDocument(); - expect( - await findByText( - /Explore your internal technical ecosystem through documentation./i, - ), - ).toBeInTheDocument(); expect(await findByText('testName')).toBeInTheDocument(); expect(await findByText('testName2')).toBeInTheDocument(); }); diff --git a/plugins/techdocs/src/home/components/DocsCardGrid.tsx b/plugins/techdocs/src/home/components/DocsCardGrid.tsx new file mode 100644 index 0000000000..47f747442c --- /dev/null +++ b/plugins/techdocs/src/home/components/DocsCardGrid.tsx @@ -0,0 +1,58 @@ +/* + * 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. + * 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 { generatePath } from 'react-router-dom'; + +import { Entity } from '@backstage/catalog-model'; +import { Button, ItemCardGrid, ItemCardHeader } from '@backstage/core'; +import { Card, CardActions, CardContent, CardMedia } from '@material-ui/core'; + +import { rootDocsRouteRef } from '../../plugin'; + +export const DocsCardGrid = ({ + entities, +}: { + entities: Entity[] | undefined; +}) => { + if (!entities) return null; + return ( + + {!entities?.length + ? null + : entities.map((entity, index: number) => ( + + + + + {entity.metadata.description} + + + + + ))} + + ); +}; diff --git a/plugins/techdocs/src/home/components/OwnedContent.test.tsx b/plugins/techdocs/src/home/components/DocsTable.test.tsx similarity index 68% rename from plugins/techdocs/src/home/components/OwnedContent.test.tsx rename to plugins/techdocs/src/home/components/DocsTable.test.tsx index edccb0d3c6..8a7d8d9af1 100644 --- a/plugins/techdocs/src/home/components/OwnedContent.test.tsx +++ b/plugins/techdocs/src/home/components/DocsTable.test.tsx @@ -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. @@ -16,37 +16,13 @@ import React from 'react'; import { render } from '@testing-library/react'; import { wrapInTestApp } from '@backstage/test-utils'; -import { OwnedContent } from './OwnedContent'; +import { DocsTable } from './DocsTable'; -jest.mock('../hooks', () => ({ - useOwnUser: () => { - return { - value: { - apiVersion: 'version', - kind: 'User', - metadata: { - name: 'owned', - namespace: 'default', - }, - relations: [ - { - target: { - kind: 'TestKind', - name: 'testName', - }, - type: 'ownerOf', - }, - ], - }, - }; - }, -})); - -describe('TechDocs Owned Content', () => { - it('should render TechDocs Owned Documents', async () => { - const { findByText, queryByText } = render( +describe('DocsTable test', () => { + it('should render documents passed', async () => { + const { findByText } = render( wrapInTestApp( - { ), ); - expect(await findByText('Owned documents')).toBeInTheDocument(); - expect(await findByText(/Access your documentation./i)).toBeInTheDocument(); expect(await findByText('testName')).toBeInTheDocument(); - expect(await queryByText('testName2')).not.toBeInTheDocument(); + expect(await findByText('testName2')).toBeInTheDocument(); }); it('should render empty state if no owned documents exist', async () => { - const { findByText } = render( - wrapInTestApp(), - ); + const { findByText } = render(wrapInTestApp()); expect(await findByText('No documents to show')).toBeInTheDocument(); }); diff --git a/plugins/techdocs/src/home/components/OwnedContent.tsx b/plugins/techdocs/src/home/components/DocsTable.tsx similarity index 67% rename from plugins/techdocs/src/home/components/OwnedContent.tsx rename to plugins/techdocs/src/home/components/DocsTable.tsx index f962147345..341ff852be 100644 --- a/plugins/techdocs/src/home/components/OwnedContent.tsx +++ b/plugins/techdocs/src/home/components/DocsTable.tsx @@ -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. @@ -20,46 +20,34 @@ import { generatePath } from 'react-router-dom'; import { IconButton, Tooltip } from '@material-ui/core'; import ShareIcon from '@material-ui/icons/Share'; -import { - Content, - ContentHeader, - SupportButton, - Table, - EmptyState, - Button, - SubvalueCell, - Link, -} from '@backstage/core'; +import { Table, EmptyState, Button, SubvalueCell, Link } from '@backstage/core'; import { Entity } from '@backstage/catalog-model'; -import { isOwnerOf } from '@backstage/plugin-catalog-react'; import { rootDocsRouteRef } from '../../plugin'; -import { useOwnUser } from '../hooks'; -export const OwnedContent = ({ +export const DocsTable = ({ entities, + title, }: { entities: Entity[] | undefined; + title?: string | undefined; }) => { const [, copyToClipboard] = useCopyToClipboard(); - const { value: user } = useOwnUser(); - if (!entities || !user) return null; + if (!entities) return null; - const ownedDocuments = entities - .filter((entity: Entity) => isOwnerOf(user, entity)) - .map(entity => { - return { + const documents = entities.map(entity => { + return { + name: entity.metadata.name, + description: entity.metadata.description, + owner: entity?.spec?.owner, + type: entity?.spec?.type, + docsUrl: generatePath(rootDocsRouteRef.path, { + namespace: entity.metadata.namespace ?? 'default', + kind: entity.kind, name: entity.metadata.name, - description: entity.metadata.description, - owner: entity?.spec?.owner, - type: entity?.spec?.type, - docsUrl: generatePath(rootDocsRouteRef.path, { - namespace: entity.metadata.namespace ?? 'default', - kind: entity.kind, - name: entity.metadata.name, - }), - }; - }); + }), + }; + }); const columns = [ { @@ -99,19 +87,17 @@ export const OwnedContent = ({ ]; return ( - - - Discover documentation you own. - - {ownedDocuments && ownedDocuments.length > 0 ? ( + <> + {documents && documents.length > 0 ? ( ) : ( )} - + ); }; diff --git a/plugins/techdocs/src/home/components/OverviewContent.tsx b/plugins/techdocs/src/home/components/OverviewContent.tsx deleted file mode 100644 index e0030691f5..0000000000 --- a/plugins/techdocs/src/home/components/OverviewContent.tsx +++ /dev/null @@ -1,73 +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 from 'react'; -import { generatePath } from 'react-router-dom'; - -import { Entity } from '@backstage/catalog-model'; -import { - Button, - Content, - ContentHeader, - SupportButton, - ItemCardGrid, - ItemCardHeader, -} from '@backstage/core'; -import { Card, CardActions, CardContent, CardMedia } from '@material-ui/core'; - -import { rootDocsRouteRef } from '../../plugin'; - -export const OverviewContent = ({ - entities, -}: { - entities: Entity[] | undefined; -}) => { - if (!entities) return null; - return ( - - - Discover documentation in your ecosystem. - - - {!entities?.length - ? null - : entities.map((entity, index: number) => ( - - - - - {entity.metadata.description} - - - - - ))} - - - ); -}; diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx new file mode 100644 index 0000000000..a7f4d90240 --- /dev/null +++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx @@ -0,0 +1,83 @@ +/* + * 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. + * 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 { ApiProvider, ApiRegistry } from '@backstage/core'; +import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; +import React from 'react'; +import { TechDocsCustomHome, WidgetType } from './TechDocsCustomHome'; + +describe('TechDocsCustomHome', () => { + const catalogApi: Partial = { + getEntities: async () => ({ items: [] }), + }; + + const apiRegistry = ApiRegistry.with(catalogApiRef, catalogApi); + + it('should render a TechDocs home page', async () => { + const tabsConfig = [ + { + label: 'First Tab', + widgets: [ + { + title: 'First Tab', + description: 'First Tab Description', + widgetType: 'DocsCardGrid' as WidgetType, + filterPredicate: () => true, + }, + ], + }, + { + label: 'Second Tab ', + widgets: [ + { + title: 'Second Tab', + description: 'Second Tab Description', + widgetType: 'DocsTable' as WidgetType, + filterPredicate: () => true, + }, + ], + }, + ]; + + await renderInTestApp( + + + , + ); + + // Header + expect(await screen.findByText('Documentation')).toBeInTheDocument(); + expect( + await screen.findByText(/Documentation available in Backstage/i), + ).toBeInTheDocument(); + + // Explore Content + expect(await screen.findByTestId('docs-explore')).toBeDefined(); + + // Tabs + expect(await screen.findAllByText('First Tab')).toHaveLength(2); + expect(await screen.findByText('Second Tab')).toBeInTheDocument(); + expect( + await screen.findByText('First Tab Description'), + ).toBeInTheDocument(); + (await screen.findByText('Second Tab')).click(); + expect( + await screen.findByText('Second Tab Description'), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx new file mode 100644 index 0000000000..37a9dadddb --- /dev/null +++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx @@ -0,0 +1,169 @@ +/* + * 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. + * 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, { useState } from 'react'; +import { useAsync } from 'react-use'; +import { makeStyles } from '@material-ui/core'; + +import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog-react'; +import { Entity } from '@backstage/catalog-model'; +import { + CodeSnippet, + Content, + ConfigApi, + configApiRef, + Header, + HeaderTabs, + Page, + Progress, + useApi, + WarningPanel, + SupportButton, + ContentHeader, +} from '@backstage/core'; +import { DocsTable } from './DocsTable'; +import { DocsCardGrid } from './DocsCardGrid'; + +const widgets = { + DocsTable: DocsTable, + DocsCardGrid: DocsCardGrid, +}; + +export type WidgetType = 'DocsCardGrid' | 'DocsTable'; + +export interface WidgetConfig { + title: string; + description: string; + widgetType: WidgetType; + widgetMaxHeight?: string; + filterPredicate: (entity: Entity) => boolean; +} + +export interface TabConfig { + label: string; + widgets: WidgetConfig[]; +} + +export type TabsConfig = TabConfig[]; + +const CustomWidget = ({ + config, + entities, + index, +}: { + config: WidgetConfig; + entities: Entity[]; + index: number; +}) => { + const useStyles = makeStyles({ + widgetContainer: { + maxHeight: config.widgetMaxHeight || 'inherit', + marginBottom: '2rem', + overflow: 'auto', + }, + }); + const classes = useStyles(); + const Widget = widgets[config.widgetType]; + const shownEntities = entities.filter(config.filterPredicate); + return ( + <> + + {index === 0 ? ( + + Discover documentation in your ecosystem. + + ) : null} + +
+ +
+ + ); +}; + +export const TechDocsCustomHome = ({ + tabsConfig, +}: { + tabsConfig: TabsConfig; +}) => { + const [selectedTab, setSelectedTab] = useState(0); + const catalogApi: CatalogApi = useApi(catalogApiRef); + const configApi: ConfigApi = useApi(configApiRef); + + const { value: entities, loading, error } = useAsync(async () => { + const response = await catalogApi.getEntities(); + return response.items.filter((entity: Entity) => { + return !!entity.metadata.annotations?.['backstage.io/techdocs-ref']; + }); + }); + + const generatedSubtitle = `Documentation available in ${ + configApi.getOptionalString('organization.name') ?? 'Backstage' + }`; + + const currentTabConfig = tabsConfig[selectedTab]; + + if (loading) { + return ( + +
+ + + + + ); + } + + if (error) { + return ( + +
+ + + + + + + ); + } + + return ( + +
+ setSelectedTab(index)} + tabs={tabsConfig.map(({ label }, index) => ({ + id: index.toString(), + label, + }))} + /> + + {currentTabConfig.widgets.map((config, index) => ( + + ))} + + + ); +}; diff --git a/plugins/techdocs/src/home/components/TechDocsHome.test.tsx b/plugins/techdocs/src/home/components/TechDocsHome.test.tsx index 8067767d39..566ed3bd0b 100644 --- a/plugins/techdocs/src/home/components/TechDocsHome.test.tsx +++ b/plugins/techdocs/src/home/components/TechDocsHome.test.tsx @@ -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. @@ -27,6 +27,30 @@ import { screen } from '@testing-library/react'; import React from 'react'; import { TechDocsHome } from './TechDocsHome'; +jest.mock('../hooks', () => ({ + useOwnUser: () => { + return { + value: { + apiVersion: 'version', + kind: 'User', + metadata: { + name: 'owned', + namespace: 'default', + }, + relations: [ + { + target: { + kind: 'TestKind', + name: 'testName', + }, + type: 'ownerOf', + }, + ], + }, + }; + }, +})); + describe('TechDocs Home', () => { const catalogApi: Partial = { getEntities: async () => ({ items: [] }), diff --git a/plugins/techdocs/src/home/components/TechDocsHome.tsx b/plugins/techdocs/src/home/components/TechDocsHome.tsx index 5e9e25e7a0..904c759b11 100644 --- a/plugins/techdocs/src/home/components/TechDocsHome.tsx +++ b/plugins/techdocs/src/home/components/TechDocsHome.tsx @@ -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. @@ -14,88 +14,44 @@ * limitations under the License. */ -import React, { useState } from 'react'; -import { useAsync } from 'react-use'; - -import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog-react'; +import React from 'react'; import { Entity } from '@backstage/catalog-model'; -import { - CodeSnippet, - ConfigApi, - configApiRef, - Content, - Header, - HeaderTabs, - Page, - Progress, - useApi, - WarningPanel, -} from '@backstage/core'; - -import { OverviewContent } from './OverviewContent'; -import { OwnedContent } from './OwnedContent'; +import { useOwnUser } from '../hooks'; +import { isOwnerOf } from '@backstage/plugin-catalog-react'; +import { WidgetType, TechDocsCustomHome } from './TechDocsCustomHome'; export const TechDocsHome = () => { - const [selectedTab, setSelectedTab] = useState(0); - const catalogApi: CatalogApi = useApi(catalogApiRef); - const configApi: ConfigApi = useApi(configApiRef); + const { value: user } = useOwnUser(); - const tabs = [{ label: 'Overview' }, { label: 'Owned Documents' }]; - - const { value, loading, error } = useAsync(async () => { - const response = await catalogApi.getEntities(); - return response.items.filter((entity: Entity) => { - return !!entity.metadata.annotations?.['backstage.io/techdocs-ref']; - }); - }); - - const generatedSubtitle = `Documentation available in ${ - configApi.getOptionalString('organization.name') ?? 'Backstage' - }`; - - if (loading) { - return ( - -
- - - - - ); - } - - if (error) { - return ( - -
- - - - - - - ); - } - - return ( - -
- setSelectedTab(index)} - tabs={tabs.map(({ label }, index) => ({ - id: index.toString(), - label, - }))} - /> - {selectedTab === 0 ? ( - - ) : ( - - )} - - ); + const tabsConfig = [ + { + label: 'Overview', + widgets: [ + { + title: 'Overview', + description: + 'Explore your internal technical ecosystem through documentation.', + widgetType: 'DocsCardGrid' as WidgetType, + filterPredicate: () => true, + }, + ], + }, + { + label: 'Owned', + widgets: [ + { + title: 'Owned documents', + description: 'Access your documentation.', + widgetType: 'DocsTable' as WidgetType, + filterPredicate: (entity: Entity) => { + if (!user) { + return false; + } + return isOwnerOf(user, entity); + }, + }, + ], + }, + ]; + return ; }; diff --git a/plugins/techdocs/src/index.ts b/plugins/techdocs/src/index.ts index 30c143e31f..f72938e385 100644 --- a/plugins/techdocs/src/index.ts +++ b/plugins/techdocs/src/index.ts @@ -19,7 +19,12 @@ export { techdocsPlugin as plugin, TechdocsPage, EntityTechdocsContent, + DocsCardGrid, + DocsTable, + TechDocsCustomHome, + TechDocsReaderPage, } from './plugin'; export { Router, EmbeddedDocsRouter } from './Router'; export * from './reader'; export * from './api'; +export type { WidgetType } from './home/components/TechDocsCustomHome'; diff --git a/plugins/techdocs/src/plugin.ts b/plugins/techdocs/src/plugin.ts index a945ca1494..d9b38a2c76 100644 --- a/plugins/techdocs/src/plugin.ts +++ b/plugins/techdocs/src/plugin.ts @@ -37,6 +37,7 @@ import { discoveryApiRef, identityApiRef, createRoutableExtension, + createComponentExtension, } from '@backstage/core'; import { techdocsStorageApiRef, @@ -111,3 +112,41 @@ export const EntityTechdocsContent = techdocsPlugin.provide( mountPoint: rootCatalogDocsRouteRef, }), ); + +// takes a list of entities and renders documentation cards +export const DocsCardGrid = techdocsPlugin.provide( + createComponentExtension({ + component: { + lazy: () => + import('./home/components/DocsCardGrid').then(m => m.DocsCardGrid), + }, + }), +); + +// takes a list of entities and renders table listing documentation +export const DocsTable = techdocsPlugin.provide( + createComponentExtension({ + component: { + lazy: () => import('./home/components/DocsTable').then(m => m.DocsTable), + }, + }), +); + +// takes a custom tabs config object and renders a documentation landing page +export const TechDocsCustomHome = techdocsPlugin.provide( + createRoutableExtension({ + component: () => + import('./home/components/TechDocsCustomHome').then( + m => m.TechDocsCustomHome, + ), + mountPoint: rootRouteRef, + }), +); + +export const TechDocsReaderPage = techdocsPlugin.provide( + createRoutableExtension({ + component: () => + import('./reader/components/TechDocsPage').then(m => m.TechDocsPage), + mountPoint: rootDocsRouteRef, + }), +); From 10579c3d0daf824ae2aa41a63e280039e50e14b4 Mon Sep 17 00:00:00 2001 From: Adrian Ke Chongyang <14866712+adrianke77@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:17:50 +0800 Subject: [PATCH 02/12] Update docs/features/techdocs/how-to-guides.md Co-authored-by: Himanshu Mishra Signed-off-by: Chongyang Adrian, Ke --- docs/features/techdocs/how-to-guides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index a76905d6c5..2b3fd08f76 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -89,7 +89,7 @@ implementation very soon. In your main App.tsx: -``` +```tsx import { TechDocsCustomHome, WidgetType, From 74c91998bfd295d2422a3b63327344bdf739ba8d Mon Sep 17 00:00:00 2001 From: Adrian Ke Chongyang <14866712+adrianke77@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:17:55 +0800 Subject: [PATCH 03/12] Update docs/features/techdocs/how-to-guides.md Co-authored-by: Himanshu Mishra Signed-off-by: Chongyang Adrian, Ke --- docs/features/techdocs/how-to-guides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index 2b3fd08f76..f387379e06 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -150,7 +150,7 @@ const routes = ( An example of tabsConfig that corresponds to the default documentation home page can be found at `plugins/techdocs/src/home/components/TechDocsHome.tsx`. -### 2nd way: custom home page plugin +### 2nd way: Custom home page plugin A custom home page plugin can be built that uses the components extensions DocsCardGrid and DocsTable, exported from @backstage/techdocs. They both take a From 6db5564af60c4a704c476daad80e8aa8a13fdf62 Mon Sep 17 00:00:00 2001 From: Adrian Ke Chongyang <14866712+adrianke77@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:18:01 +0800 Subject: [PATCH 04/12] Update docs/features/techdocs/how-to-guides.md Co-authored-by: Himanshu Mishra Signed-off-by: Chongyang Adrian, Ke --- docs/features/techdocs/how-to-guides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index f387379e06..e1dbe8b494 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -157,5 +157,5 @@ DocsCardGrid and DocsTable, exported from @backstage/techdocs. They both take a array of documentation entities ( have 'backstage.io/techdocs-ref' annotation ) as an 'entities' attribute. -For an reference to the React structure of the default home page, please refer +For a reference to the React structure of the default home page, please refer to `plugins/techdocs/src/home/components/TechDocsCustomHome.tsx`. From 9f7d5662e3002356c06abe40dbfade36473a239c Mon Sep 17 00:00:00 2001 From: "Chongyang Adrian, Ke" Date: Mon, 19 Apr 2021 16:24:59 +0800 Subject: [PATCH 05/12] update how-to-guides.md Signed-off-by: Chongyang Adrian, Ke --- docs/features/techdocs/how-to-guides.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index e1dbe8b494..1da925ae5a 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -154,8 +154,8 @@ can be found at `plugins/techdocs/src/home/components/TechDocsHome.tsx`. A custom home page plugin can be built that uses the components extensions DocsCardGrid and DocsTable, exported from @backstage/techdocs. They both take a -array of documentation entities ( have 'backstage.io/techdocs-ref' annotation ) -as an 'entities' attribute. +array of documentation entities ( i.e.have a 'backstage.io/techdocs-ref' +annotation ) as an 'entities' attribute. -For a reference to the React structure of the default home page, please refer -to `plugins/techdocs/src/home/components/TechDocsCustomHome.tsx`. +For a reference to the React structure of the default home page, please refer to +`plugins/techdocs/src/home/components/TechDocsCustomHome.tsx`. From cb321bae90356b4eaa8609144e99ae082b2d939a Mon Sep 17 00:00:00 2001 From: "Chongyang Adrian, Ke" Date: Thu, 22 Apr 2021 11:53:30 +0800 Subject: [PATCH 06/12] change naming from widget to panel Signed-off-by: Chongyang Adrian, Ke --- docs/features/techdocs/how-to-guides.md | 18 ++++++----- .../src/home/components/DocsTable.tsx | 6 +++- .../components/TechDocsCustomHome.test.tsx | 10 +++---- .../home/components/TechDocsCustomHome.tsx | 30 +++++++++---------- .../src/home/components/TechDocsHome.tsx | 11 +++---- plugins/techdocs/src/index.ts | 2 +- 6 files changed, 43 insertions(+), 34 deletions(-) diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index 1da925ae5a..682051dd83 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -92,7 +92,7 @@ In your main App.tsx: ```tsx import { TechDocsCustomHome, - WidgetType, + PanelType, TechDocsReaderPage, } from '@backstage/plugin-techdocs'; import { Entity } from '@backstage/catalog-model'; @@ -105,17 +105,17 @@ const tabsConfig = [ title: 'Custom Documents Cards 1', description: 'Explore your internal technical ecosystem through documentation.', - // sets maximum height of widget, as CSS maxHeight attribute - widgetMaxHeight: '400px' - widgetType: 'DocsCardGrid' as WidgetType, + panelType: 'DocsCardGrid' as PanelType, + // optional, is applied to a container of the panel (excludes header of panel) + panelCSS: { maxHeight: '400px' }, filterPredicate: (entity: Entity) => !!entity.metadata.annotations?.['customCardAnnotationOne']; }, { title: 'Custom Documents Cards 2', description: 'Explore your internal technical ecosystem through documentation.', - widgetMaxHeight: '400px' - widgetType: 'DocsCardGrid' as WidgetType, + panelType: 'DocsCardGrid' as PanelType, + panelCSS: { maxHeight: '400px' }, filterPredicate: (entity: Entity) => !!entity.metadata.annotations?.['customCardAnnotationTwo']; }, ], @@ -127,7 +127,7 @@ const tabsConfig = [ title: 'Overview', description: 'Explore your internal technical ecosystem through documentation.', - widgetType: 'DocsTable' as WidgetType, + panelType: 'DocsTable' as PanelType, filterPredicate: () => true, }, ], @@ -150,6 +150,10 @@ const routes = ( An example of tabsConfig that corresponds to the default documentation home page can be found at `plugins/techdocs/src/home/components/TechDocsHome.tsx`. +Currently `panelType` has DocsCardGrid and DocsTable available. We currently +recommend that DocsCardGrid can be optionally vertically stacked by setting a +maxHeight using `panelCSS`, and DocsTable to be in a tab by itself. + ### 2nd way: Custom home page plugin A custom home page plugin can be built that uses the components extensions diff --git a/plugins/techdocs/src/home/components/DocsTable.tsx b/plugins/techdocs/src/home/components/DocsTable.tsx index 341ff852be..cb0a3b4329 100644 --- a/plugins/techdocs/src/home/components/DocsTable.tsx +++ b/plugins/techdocs/src/home/components/DocsTable.tsx @@ -90,7 +90,11 @@ export const DocsTable = ({ <> {documents && documents.length > 0 ? (
{ const catalogApi: Partial = { @@ -32,22 +32,22 @@ describe('TechDocsCustomHome', () => { const tabsConfig = [ { label: 'First Tab', - widgets: [ + panels: [ { title: 'First Tab', description: 'First Tab Description', - widgetType: 'DocsCardGrid' as WidgetType, + panelType: 'DocsCardGrid' as PanelType, filterPredicate: () => true, }, ], }, { label: 'Second Tab ', - widgets: [ + panels: [ { title: 'Second Tab', description: 'Second Tab Description', - widgetType: 'DocsTable' as WidgetType, + panelType: 'DocsTable' as PanelType, filterPredicate: () => true, }, ], diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx index 37a9dadddb..ffcb02451b 100644 --- a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx +++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx @@ -37,46 +37,46 @@ import { import { DocsTable } from './DocsTable'; import { DocsCardGrid } from './DocsCardGrid'; -const widgets = { +const panels = { DocsTable: DocsTable, DocsCardGrid: DocsCardGrid, }; -export type WidgetType = 'DocsCardGrid' | 'DocsTable'; +export type PanelType = 'DocsCardGrid' | 'DocsTable'; -export interface WidgetConfig { +export interface PanelConfig { title: string; description: string; - widgetType: WidgetType; - widgetMaxHeight?: string; + panelType: PanelType; + panelCSS?: {}; filterPredicate: (entity: Entity) => boolean; } export interface TabConfig { label: string; - widgets: WidgetConfig[]; + panels: PanelConfig[]; } export type TabsConfig = TabConfig[]; -const CustomWidget = ({ +const CustomPanel = ({ config, entities, index, }: { - config: WidgetConfig; + config: PanelConfig; entities: Entity[]; index: number; }) => { const useStyles = makeStyles({ - widgetContainer: { - maxHeight: config.widgetMaxHeight || 'inherit', + panelContainer: { marginBottom: '2rem', overflow: 'auto', + ...(config.panelCSS ? config.panelCSS : {}), }, }); const classes = useStyles(); - const Widget = widgets[config.widgetType]; + const Panel = panels[config.panelType]; const shownEntities = entities.filter(config.filterPredicate); return ( <> @@ -87,8 +87,8 @@ const CustomWidget = ({ ) : null} -
- +
+
); @@ -155,8 +155,8 @@ export const TechDocsCustomHome = ({ }))} /> - {currentTabConfig.widgets.map((config, index) => ( - ( + { const { value: user } = useOwnUser(); @@ -26,23 +26,24 @@ export const TechDocsHome = () => { const tabsConfig = [ { label: 'Overview', - widgets: [ + panels: [ { title: 'Overview', description: 'Explore your internal technical ecosystem through documentation.', - widgetType: 'DocsCardGrid' as WidgetType, + panelType: 'DocsCardGrid' as PanelType, filterPredicate: () => true, }, ], }, { label: 'Owned', - widgets: [ + panels: [ { title: 'Owned documents', description: 'Access your documentation.', - widgetType: 'DocsTable' as WidgetType, + panelType: 'DocsTable' as PanelType, + paneCSS: { maxHeight: '200px' }, filterPredicate: (entity: Entity) => { if (!user) { return false; diff --git a/plugins/techdocs/src/index.ts b/plugins/techdocs/src/index.ts index f72938e385..b33b91e2c5 100644 --- a/plugins/techdocs/src/index.ts +++ b/plugins/techdocs/src/index.ts @@ -27,4 +27,4 @@ export { export { Router, EmbeddedDocsRouter } from './Router'; export * from './reader'; export * from './api'; -export type { WidgetType } from './home/components/TechDocsCustomHome'; +export type { PanelType } from './home/components/TechDocsCustomHome'; From f5dab83b5972ae9c317a51a7b77ccf1c788b788e Mon Sep 17 00:00:00 2001 From: Adrian Ke Chongyang <14866712+adrianke77@users.noreply.github.com> Date: Fri, 23 Apr 2021 11:00:08 +0800 Subject: [PATCH 07/12] Update docs/features/techdocs/how-to-guides.md Signed-off-by: Eric Peterson Co-authored-by: Eric Peterson Signed-off-by: Chongyang Adrian, Ke --- docs/features/techdocs/how-to-guides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index 682051dd83..1b19f12b8a 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -100,7 +100,7 @@ import { Entity } from '@backstage/catalog-model'; const tabsConfig = [ { label: 'Custom Tab', - widgets: [ + panels: [ { title: 'Custom Documents Cards 1', description: From 7a3b3b2be6bec8bbc733313e56cc0d396be2c16d Mon Sep 17 00:00:00 2001 From: Adrian Ke Chongyang <14866712+adrianke77@users.noreply.github.com> Date: Fri, 23 Apr 2021 11:00:17 +0800 Subject: [PATCH 08/12] Update docs/features/techdocs/how-to-guides.md Signed-off-by: Eric Peterson Co-authored-by: Eric Peterson Signed-off-by: Chongyang Adrian, Ke --- docs/features/techdocs/how-to-guides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index 1b19f12b8a..384656ea38 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -122,7 +122,7 @@ const tabsConfig = [ }, { label: 'Overview', - widgets: [ + panels: [ { title: 'Overview', description: From 64ade20227f269f26ad3d4ffa1c20fcf6d223c77 Mon Sep 17 00:00:00 2001 From: Adrian Ke Chongyang <14866712+adrianke77@users.noreply.github.com> Date: Fri, 23 Apr 2021 11:00:30 +0800 Subject: [PATCH 09/12] Update plugins/techdocs/src/home/components/TechDocsHome.tsx Signed-off-by: Eric Peterson Co-authored-by: Eric Peterson Signed-off-by: Chongyang Adrian, Ke --- plugins/techdocs/src/home/components/TechDocsHome.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/techdocs/src/home/components/TechDocsHome.tsx b/plugins/techdocs/src/home/components/TechDocsHome.tsx index a9641391e2..ef2749015a 100644 --- a/plugins/techdocs/src/home/components/TechDocsHome.tsx +++ b/plugins/techdocs/src/home/components/TechDocsHome.tsx @@ -43,7 +43,6 @@ export const TechDocsHome = () => { title: 'Owned documents', description: 'Access your documentation.', panelType: 'DocsTable' as PanelType, - paneCSS: { maxHeight: '200px' }, filterPredicate: (entity: Entity) => { if (!user) { return false; From 4666ddb275465dc499d9b9c30d2dbb3546233d4b Mon Sep 17 00:00:00 2001 From: Adrian Ke Chongyang <14866712+adrianke77@users.noreply.github.com> Date: Fri, 23 Apr 2021 11:00:37 +0800 Subject: [PATCH 10/12] Update plugins/techdocs/src/home/components/TechDocsHome.tsx Signed-off-by: Eric Peterson Co-authored-by: Eric Peterson Signed-off-by: Chongyang Adrian, Ke --- plugins/techdocs/src/home/components/TechDocsHome.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/techdocs/src/home/components/TechDocsHome.tsx b/plugins/techdocs/src/home/components/TechDocsHome.tsx index ef2749015a..72f59d5446 100644 --- a/plugins/techdocs/src/home/components/TechDocsHome.tsx +++ b/plugins/techdocs/src/home/components/TechDocsHome.tsx @@ -37,7 +37,7 @@ export const TechDocsHome = () => { ], }, { - label: 'Owned', + label: 'Owned Documents', panels: [ { title: 'Owned documents', From 7dd94b075056ea9118047d4c66e0285f23e9139c Mon Sep 17 00:00:00 2001 From: "Chongyang Adrian, Ke" Date: Fri, 23 Apr 2021 11:20:46 +0800 Subject: [PATCH 11/12] add CSSProperties type for panelCSS Signed-off-by: Chongyang Adrian, Ke --- plugins/techdocs/package.json | 1 + plugins/techdocs/src/home/components/TechDocsCustomHome.tsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 88b3b6a1df..775e57d00f 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -41,6 +41,7 @@ "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", + "@material-ui/styles": "^4.10.0", "react": "^16.13.1", "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx index ffcb02451b..7230adb29b 100644 --- a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx +++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx @@ -17,7 +17,7 @@ import React, { useState } from 'react'; import { useAsync } from 'react-use'; import { makeStyles } from '@material-ui/core'; - +import { CSSProperties } from '@material-ui/styles'; import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog-react'; import { Entity } from '@backstage/catalog-model'; import { @@ -48,7 +48,7 @@ export interface PanelConfig { title: string; description: string; panelType: PanelType; - panelCSS?: {}; + panelCSS?: CSSProperties; filterPredicate: (entity: Entity) => boolean; } From 813a4a04f55752bf273c2dac304f1d1dbd2152f8 Mon Sep 17 00:00:00 2001 From: "Chongyang Adrian, Ke" Date: Fri, 23 Apr 2021 11:38:09 +0800 Subject: [PATCH 12/12] fix DocsTable rendering of EmptyState Signed-off-by: Chongyang Adrian, Ke --- docs/features/techdocs/how-to-guides.md | 6 +++--- plugins/techdocs/src/home/components/TechDocsCustomHome.tsx | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index 384656ea38..6c64e83a76 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -87,7 +87,7 @@ implementation very soon. ### 1st way: TechDocsCustomHome with a custom configuration -In your main App.tsx: +As an example, in your main App.tsx: ```tsx import { @@ -107,7 +107,7 @@ const tabsConfig = [ 'Explore your internal technical ecosystem through documentation.', panelType: 'DocsCardGrid' as PanelType, // optional, is applied to a container of the panel (excludes header of panel) - panelCSS: { maxHeight: '400px' }, + panelCSS: { maxHeight: '400px', overflow:'auto' }, filterPredicate: (entity: Entity) => !!entity.metadata.annotations?.['customCardAnnotationOne']; }, { @@ -115,7 +115,7 @@ const tabsConfig = [ description: 'Explore your internal technical ecosystem through documentation.', panelType: 'DocsCardGrid' as PanelType, - panelCSS: { maxHeight: '400px' }, + panelCSS: { maxHeight: '400px', overflow:'auto' }, filterPredicate: (entity: Entity) => !!entity.metadata.annotations?.['customCardAnnotationTwo']; }, ], diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx index 7230adb29b..ff9f1bf101 100644 --- a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx +++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx @@ -71,7 +71,6 @@ const CustomPanel = ({ const useStyles = makeStyles({ panelContainer: { marginBottom: '2rem', - overflow: 'auto', ...(config.panelCSS ? config.panelCSS : {}), }, });