diff --git a/.changeset/warm-masks-ring.md b/.changeset/warm-masks-ring.md index aefbcf8013..bfb521a005 100644 --- a/.changeset/warm-masks-ring.md +++ b/.changeset/warm-masks-ring.md @@ -20,7 +20,7 @@ const techDocsTabsConfig = [ title: 'Golden Path', description: 'Documentation about standards to follow', panelType: 'DocsCardGrid', - panelProps: { showHeader: false, showSupport: false }, + panelProps: { CustomHeader: () => }, filterPredicate: entity => entity?.metadata?.tags?.includes('golden-path') ?? false, }, @@ -29,8 +29,7 @@ const techDocsTabsConfig = [ description: 'Useful documentation', panelType: 'InfoCardGrid', panelProps: { - showHeader: false, - showSupport: false, + CustomHeader: () => linkDestination: linkDestination, }, filterPredicate: entity => @@ -46,7 +45,7 @@ const techDocsTabsConfig = [ filterPredicate: filterEntity, panelType: 'TechDocsIndexPage', title: 'All', - panelProps: { showHeader: false, showSupport: false, options: options }, + panelProps: { PageWrapper: React.Fragment, CustomHeader: React.Fragment, options: options }, }, ], }, @@ -59,12 +58,11 @@ const AppRoutes = () => { element={ ) => ({children})} /> } /> @@ -92,10 +90,9 @@ const panels: PanelConfig[] = [ panelType: 'InfoCardGrid', title: 'Standards', panelProps: { - showSupport: false, - linkContent: 'Read more', - linkDestination: entity => {}, - }, + CustomHeader: () => + linkDestination: linkDestination, + }, }, { description: '', diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index ea1ac7a372..5a9b8ba140 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -149,7 +149,7 @@ const techDocsTabsConfig = [ title: 'Golden Path', description: 'Documentation about standards to follow', panelType: 'DocsCardGrid', - panelProps: { showHeader: false, showSupport: false }, + panelProps: { CustomHeader: () => }, filterPredicate: entity => entity?.metadata?.tags?.includes('golden-path') ?? false, }, @@ -158,8 +158,7 @@ const techDocsTabsConfig = [ description: 'Useful documentation', panelType: 'InfoCardGrid', panelProps: { - showHeader: false, - showSupport: false, + CustomHeader: () => linkDestination: linkDestination, }, filterPredicate: entity => @@ -175,7 +174,7 @@ const techDocsTabsConfig = [ filterPredicate: filterEntity, panelType: 'TechDocsIndexPage', title: 'All', - panelProps: { showHeader: false, showSupport: false, options: options }, + panelProps: { PageWrapper: React.Fragment, CustomHeader: React.Fragment, options: options }, }, ], }, @@ -188,12 +187,11 @@ const AppRoutes = () => { element={ ) => ({children})} /> } /> diff --git a/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx b/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx index 2f79455ad3..f44bc8eca0 100644 --- a/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx +++ b/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx @@ -51,24 +51,27 @@ export const DefaultTechDocsHome = (props: TechDocsIndexPageProps) => { columns, actions, ownerPickerMode, - showHeader = true, - options, - title, - subtitle, - showSupport = true, pagination, + options, + PageWrapper, + CustomHeader, } = props; - const Wrapper = showHeader ? TechDocsPageWrapper : React.Fragment; + const Wrapper: React.FC<{ + children: React.ReactNode; + title?: string; + subtitle?: string; + }> = PageWrapper ? PageWrapper : TechDocsPageWrapper; + const Header: React.FC = + CustomHeader || + (() => ( + + Discover documentation in your ecosystem. + + )); return ( - + - {showSupport && ( - - - Discover documentation in your ecosystem. - - - )} +
diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx index a366cc7269..bd9725d9a0 100644 --- a/plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx +++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx @@ -19,6 +19,7 @@ import { starredEntitiesApiRef, MockStarredEntitiesApi, } from '@backstage/plugin-catalog-react'; +import { PageWithHeader } from '@backstage/core-components'; import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; import { screen } from '@testing-library/react'; @@ -103,7 +104,7 @@ describe('TechDocsCustomHome', () => { await screen.findByText('Second Tab Description'), ).toBeInTheDocument(); }); - it('should render ContentHeader based on showHeader prop', async () => { + it('should render ContentHeader based on CustomHeader prop', async () => { const tabsConfig = [ { label: 'First Tab', @@ -112,7 +113,7 @@ describe('TechDocsCustomHome', () => { title: 'First Tab', description: 'First Tab Description', panelType: 'DocsCardGrid' as PanelType, - panelProps: { showHeader: false }, + panelProps: { CustomHeader: React.Fragment }, filterPredicate: () => true, }, ], @@ -134,71 +135,6 @@ describe('TechDocsCustomHome', () => { screen.queryByText('Discover documentation in your ecosystem.'), ).not.toBeInTheDocument(); }); - it('should render SupportButton based on showSupport prop', async () => { - const tabsConfig = [ - { - label: 'First Tab', - panels: [ - { - title: 'First Tab', - description: 'First Tab Description', - panelType: 'DocsCardGrid' as PanelType, - filterPredicate: () => true, - panelProps: { showSupport: false }, - }, - ], - }, - ]; - - await renderInTestApp( - - - , - { - mountedRoutes: { - '/docs/:namespace/:kind/:name/*': rootDocsRouteRef, - }, - }, - ); - - expect( - screen.queryByText('Discover documentation in your ecosystem.'), - ).not.toBeInTheDocument(); - }); - it('should hide subtitle when showSubtitle is false', async () => { - const tabsConfig = [ - { - label: 'First Tab', - panels: [ - { - title: 'First Tab', - description: 'First Tab Description', - panelType: 'DocsCardGrid' as PanelType, - filterPredicate: () => true, - }, - ], - }, - ]; - - await renderInTestApp( - - - , - { - mountedRoutes: { - '/docs/:namespace/:kind/:name/*': rootDocsRouteRef, - }, - }, - ); - - expect(screen.getByText('Custom Title')).toBeInTheDocument(); - expect(screen.queryByText('Custom Subtitle')).not.toBeInTheDocument(); - }); it('should render title and subtitle', async () => { const tabsConfig = [ { @@ -218,8 +154,15 @@ describe('TechDocsCustomHome', () => { ) => ( + + {children} + + )} /> , { diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx index b8998b8655..e1c5a93161 100644 --- a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx +++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx @@ -69,11 +69,11 @@ export type PanelType = * @public */ export interface PanelProps { - showHeader?: boolean; - showSupport?: boolean; options?: TableOptions; linkContent?: string | JSX.Element; linkDestination?: (entity: Entity) => string | undefined; + PageWrapper?: React.FC; + CustomHeader?: React.FC; } /** @@ -146,17 +146,21 @@ export const CustomDocsPanel = ({ ); }); + const Header: React.FC = + config.panelProps?.CustomHeader || + (() => ( + + {index === 0 ? ( + + Discover documentation in your ecosystem. + + ) : null} + + )); + return ( <> - {(config.panelProps?.showHeader ?? true) && ( - - {index === 0 && (config.panelProps?.showSupport ?? true) && ( - - Discover documentation in your ecosystem. - - )} - - )} +
{ - const { tabsConfig, filter, title, subtitle, showSubtitle = true } = props; + const { tabsConfig, filter, CustomPageWrapper } = props; const [selectedTab, setSelectedTab] = useState(0); const catalogApi: CatalogApi = useApi(catalogApiRef); @@ -216,11 +218,7 @@ export const TechDocsCustomHome = (props: TechDocsCustomHomeProps) => { if (loading) { return ( - + @@ -230,11 +228,7 @@ 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 50388519ba..221839c492 100644 --- a/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx +++ b/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx @@ -39,12 +39,10 @@ export type TechDocsIndexPageProps = { columns?: TableColumn[]; actions?: TableProps['actions']; ownerPickerMode?: EntityOwnerPickerProps['mode']; - showHeader?: boolean; - showSupport?: boolean; - options?: TableOptions; - title?: string; - subtitle?: string; pagination?: EntityListPagination; + options?: TableOptions; + PageWrapper?: React.FC; + CustomHeader?: React.FC; }; export const TechDocsIndexPage = (props: TechDocsIndexPageProps) => { diff --git a/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx b/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx index 84f8eee327..c07876f7f9 100644 --- a/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx +++ b/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx @@ -26,9 +26,7 @@ import { useApi, configApiRef } from '@backstage/core-plugin-api'; */ export type TechDocsPageWrapperProps = { children?: React.ReactNode; - title?: string; - subtitle?: string; - showSubtitle?: boolean; + CustomPageWrapper?: React.FC<{ children?: React.ReactNode }>; }; /** @@ -37,21 +35,25 @@ export type TechDocsPageWrapperProps = { * @public */ export const TechDocsPageWrapper = (props: TechDocsPageWrapperProps) => { - const { children, title, subtitle, showSubtitle = true } = props; + const { children, CustomPageWrapper } = props; const configApi = useApi(configApiRef); - const generatedSubtitle = - subtitle || - `Documentation available in ${ - configApi.getOptionalString('organization.name') ?? 'Backstage' - }`; + const generatedSubtitle = `Documentation available in ${ + configApi.getOptionalString('organization.name') ?? 'Backstage' + }`; return ( - - {children} - + <> + {CustomPageWrapper ? ( + {children} + ) : ( + + {children} + + )} + ); }; diff --git a/plugins/techdocs/src/index.ts b/plugins/techdocs/src/index.ts index 522bcd0c99..713efc17c6 100644 --- a/plugins/techdocs/src/index.ts +++ b/plugins/techdocs/src/index.ts @@ -67,3 +67,5 @@ export type { DeprecatedTechDocsEntityMetadata as TechDocsEntityMetadata, DeprecatedTechDocsMetadata as TechDocsMetadata, }; + +export * from './overridableComponents';