diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 7032277a95..d443b8ebe7 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -59,6 +59,7 @@ "test": "backstage-cli package test" }, "dependencies": { + "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/core-compat-api": "workspace:^", diff --git a/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx b/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx index 2fbd5bb50a..f4a4e7862a 100644 --- a/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx +++ b/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx @@ -46,15 +46,28 @@ export type DefaultTechDocsHomeProps = TechDocsIndexPageProps; * @public */ export const DefaultTechDocsHome = (props: TechDocsIndexPageProps) => { - const { initialFilter = 'owned', columns, actions, ownerPickerMode } = props; + const { + initialFilter = 'owned', + columns, + actions, + ownerPickerMode, + showHeader, + options, + title, + subtitle, + hideSupport, + } = props; + const Wrapper = showHeader !== false ? TechDocsPageWrapper : React.Fragment; return ( - + - - - Discover documentation in your ecosystem. - - + {hideSupport !== true && ( + + + Discover documentation in your ecosystem. + + + )} @@ -64,11 +77,15 @@ export const DefaultTechDocsHome = (props: TechDocsIndexPageProps) => { - + - + ); }; diff --git a/plugins/techdocs/src/home/components/Grids/InfoCardGrid.tsx b/plugins/techdocs/src/home/components/Grids/InfoCardGrid.tsx new file mode 100644 index 0000000000..46293f9228 --- /dev/null +++ b/plugins/techdocs/src/home/components/Grids/InfoCardGrid.tsx @@ -0,0 +1,94 @@ +/* + * Copyright 2021 The Backstage Authors + * + * 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 { rootDocsRouteRef } from '../../../routes'; +import { toLowerMaybe } from '../../../helpers'; +import { Entity } from '@backstage/catalog-model'; +import { useApi, useRouteRef, configApiRef } from '@backstage/core-plugin-api'; +import { ItemCardGrid, InfoCard, Link } from '@backstage/core-components'; +import { makeStyles } from '@material-ui/core/styles'; +import React from 'react'; + +/** @public */ +export type InfoCardGridClassKey = 'linkSpacer' | 'readMoreLink'; + +const useStyles = makeStyles( + theme => ({ + linkSpacer: { + paddingTop: theme.spacing(0.2), + }, + readMoreLink: { + paddingTop: theme.spacing(0.2), + }, + }), + { name: 'BackstageInfoCardGrid' }, +); + +/** + * Props for {@link InfoCardGird} + * + * @public + */ +export type InfoCardGirdProps = { + entities: Entity[] | undefined; + linkContent?: string | JSX.Element; + linkDest?: (entity: Entity) => string; +}; + +/** + * Component which accepts a list of entities and renders a info card for each entity + * + * @public + */ +export const InfoCardGird = (props: InfoCardGirdProps) => { + const { entities, linkContent, linkDest } = props; + const classes = useStyles(); + const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef); + const config = useApi(configApiRef); + if (!entities) return null; + return ( + + {!entities?.length + ? null + : entities.map(entity => ( + + {entity?.metadata?.description} + + + {linkContent || 'Read Docs'} + + + ))} + + ); +}; diff --git a/plugins/techdocs/src/home/components/Grids/index.ts b/plugins/techdocs/src/home/components/Grids/index.ts index c28b5a0723..136e31d569 100644 --- a/plugins/techdocs/src/home/components/Grids/index.ts +++ b/plugins/techdocs/src/home/components/Grids/index.ts @@ -16,3 +16,4 @@ export * from './EntityListDocsGrid'; export * from './DocsCardGrid'; +export * from './InfoCardGrid'; diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx index 56d086acfc..5024eab5c3 100644 --- a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx +++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx @@ -26,8 +26,9 @@ import { } from '@backstage/plugin-catalog-react'; import { Entity } from '@backstage/catalog-model'; import { DocsTable } from './Tables'; -import { DocsCardGrid } from './Grids'; +import { DocsCardGrid, InfoCardGird } from './Grids'; import { TechDocsPageWrapper } from './TechDocsPageWrapper'; +import { TechDocsIndexPage } from './TechDocsIndexPage'; import { CodeSnippet, @@ -40,10 +41,13 @@ import { } from '@backstage/core-components'; import { useApi } from '@backstage/core-plugin-api'; import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common'; +import { EntityFilterQuery } from '@backstage/catalog-client'; const panels = { DocsTable: DocsTable, DocsCardGrid: DocsCardGrid, + TechDocsIndexPage: TechDocsIndexPage, + InfoCardGird: InfoCardGird, }; /** @@ -51,7 +55,11 @@ const panels = { * * @public */ -export type PanelType = 'DocsCardGrid' | 'DocsTable'; +export type PanelType = + | 'DocsCardGrid' + | 'DocsTable' + | 'TechDocsIndexPage' + | 'InfoCardGird'; /** * Type representing a TechDocsCustomHome panel. @@ -64,6 +72,7 @@ export interface PanelConfig { panelType: PanelType; panelCSS?: CSSProperties; filterPredicate: ((entity: Entity) => boolean) | string; + panelProps?: Record; } /** @@ -119,15 +128,21 @@ const CustomPanel = ({ return ( <> - - {index === 0 ? ( - - Discover documentation in your ecosystem. - - ) : null} - + {config.panelProps?.showHeader !== false && ( + + {index === 0 && config.panelProps?.hideSupport !== true && ( + + Discover documentation in your ecosystem. + + )} + + )} - + > ); @@ -140,10 +155,14 @@ const CustomPanel = ({ */ export type TechDocsCustomHomeProps = { tabsConfig: TabsConfig; + filter?: EntityFilterQuery; + title?: string; + subtitle?: string; + hideSubtitle?: boolean; }; export const TechDocsCustomHome = (props: TechDocsCustomHomeProps) => { - const { tabsConfig } = props; + const { tabsConfig, filter, title, subtitle, hideSubtitle } = props; const [selectedTab, setSelectedTab] = useState(0); const catalogApi: CatalogApi = useApi(catalogApiRef); @@ -153,7 +172,7 @@ export const TechDocsCustomHome = (props: TechDocsCustomHomeProps) => { error, } = useAsync(async () => { const response = await catalogApi.getEntities({ - filter: { + filter: filter || { [`metadata.annotations.${TECHDOCS_ANNOTATION}`]: CATALOG_FILTER_EXISTS, }, fields: [ @@ -165,16 +184,18 @@ export const TechDocsCustomHome = (props: TechDocsCustomHomeProps) => { 'spec.type', ], }); - return response.items.filter((entity: Entity) => { - return !!entity.metadata.annotations?.[TECHDOCS_ANNOTATION]; - }); + return response.items; }); const currentTabConfig = tabsConfig[selectedTab]; if (loading) { return ( - + @@ -184,7 +205,11 @@ export const TechDocsCustomHome = (props: TechDocsCustomHomeProps) => { if (error) { return ( - + { } return ( - + setSelectedTab(index)} diff --git a/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx b/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx index adbb68d3e9..baf50b89f3 100644 --- a/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx +++ b/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx @@ -16,7 +16,11 @@ import React from 'react'; import { useOutlet } from 'react-router-dom'; -import { TableColumn, TableProps } from '@backstage/core-components'; +import { + TableColumn, + TableProps, + TableOptions, +} from '@backstage/core-components'; import { EntityOwnerPickerProps, UserListFilterKind, @@ -34,6 +38,11 @@ export type TechDocsIndexPageProps = { columns?: TableColumn[]; actions?: TableProps['actions']; ownerPickerMode?: EntityOwnerPickerProps['mode']; + showHeader?: boolean; + hideSupport?: boolean; + options?: TableOptions; + title?: string; + subtitle?: string; }; export const TechDocsIndexPage = (props: TechDocsIndexPageProps) => { diff --git a/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx b/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx index a6192c13ee..eedac5ee95 100644 --- a/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx +++ b/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx @@ -26,6 +26,9 @@ import { useApi, configApiRef } from '@backstage/core-plugin-api'; */ export type TechDocsPageWrapperProps = { children?: React.ReactNode; + title?: string; + subtitle?: string; + hideSubtitle?: boolean; }; /** @@ -34,16 +37,18 @@ export type TechDocsPageWrapperProps = { * @public */ export const TechDocsPageWrapper = (props: TechDocsPageWrapperProps) => { - const { children } = props; + const { children, title, subtitle, hideSubtitle } = props; const configApi = useApi(configApiRef); - const generatedSubtitle = `Documentation available in ${ - configApi.getOptionalString('organization.name') ?? 'Backstage' - }`; + const generatedSubtitle = + subtitle || + `Documentation available in ${ + configApi.getOptionalString('organization.name') ?? 'Backstage' + }`; return ( {children} diff --git a/plugins/techdocs/src/overridableComponents.ts b/plugins/techdocs/src/overridableComponents.ts new file mode 100644 index 0000000000..8b371d8798 --- /dev/null +++ b/plugins/techdocs/src/overridableComponents.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2021 The Backstage Authors + * + * 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 { Overrides } from '@material-ui/core/styles/overrides'; +import { StyleRules } from '@material-ui/core/styles/withStyles'; +import { InfoCardGridClassKey } from './home/components/Grids/InfoCardGrid'; + +/** @public */ +export type CatalogReactComponentsNameToClassKey = { + BackstageInfoCardGrid: InfoCardGridClassKey; +}; + +/** @public */ +export type BackstageOverrides = Overrides & { + [Name in keyof CatalogReactComponentsNameToClassKey]?: Partial< + StyleRules + >; +}; + +declare module '@backstage/theme' { + interface OverrideComponentNameToClassKeys + extends CatalogReactComponentsNameToClassKey {} +} diff --git a/yarn.lock b/yarn.lock index 1e2c2e6742..d172cfc75e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8472,6 +8472,7 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-techdocs@workspace:plugins/techdocs" dependencies: + "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^"