diff --git a/.changeset/healthy-shoes-flash.md b/.changeset/healthy-shoes-flash.md new file mode 100644 index 0000000000..8ca1006635 --- /dev/null +++ b/.changeset/healthy-shoes-flash.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': minor +--- + +Added ability for to display a custom error page component via prop "NotFoundPage" diff --git a/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx b/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx index b297466a62..5dfa104a25 100644 --- a/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx @@ -173,4 +173,62 @@ describe('', () => { expect(rendered.getByText('A custom header')).toBeInTheDocument(); }); }); + + it('should render a custom error page if supplied', async () => { + const CustomErrorPage = ({ errorMessage }: { errorMessage: string }) => ( +
{errorMessage}
+ ); + + useParams.mockReturnValue({ + entityRef: 'Component::backstage', + }); + + const scmIntegrationsApi: ScmIntegrationsApi = + ScmIntegrationsApi.fromConfig( + new ConfigReader({ + integrations: {}, + }), + ); + const techdocsApi: Partial = { + getEntityMetadata: () => + Promise.reject({ + name: 'error', + message: 'error message', + }), + getTechDocsMetadata: () => + Promise.resolve({ + site_name: 'string', + site_description: 'string', + }), + }; + + const techdocsStorageApi: Partial = { + getEntityDocs: (): Promise => Promise.resolve('String'), + getBaseUrl: (): Promise => Promise.resolve('String'), + getApiOrigin: (): Promise => Promise.resolve('String'), + }; + const searchApi = { + query: () => + Promise.resolve({ + results: [], + }), + }; + const apiRegistry = TestApiRegistry.from( + [scmIntegrationsApiRef, scmIntegrationsApi], + [techdocsApiRef, techdocsApi], + [techdocsStorageApiRef, techdocsStorageApi], + [searchApiRef, searchApi], + ); + + await act(async () => { + const rendered = render( + wrapInTestApp( + + + , + ), + ); + expect(await rendered.findByText('error message')).toBeInTheDocument(); + }); + }); }); diff --git a/plugins/techdocs/src/reader/components/TechDocsPage.tsx b/plugins/techdocs/src/reader/components/TechDocsPage.tsx index aae88d406e..1fb75f8d76 100644 --- a/plugins/techdocs/src/reader/components/TechDocsPage.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useState, ComponentType } from 'react'; import { useOutlet } from 'react-router'; import { useParams } from 'react-router-dom'; import useAsync from 'react-use/lib/useAsync'; @@ -39,9 +39,13 @@ export type TechDocsPageRenderFunction = ({ export type TechDocsPageProps = { children?: TechDocsPageRenderFunction | React.ReactNode; + NotFoundPage?: ComponentType<{errorMessage: string}> }; -export const TechDocsPage = ({ children }: TechDocsPageProps) => { +export const TechDocsPage = ({ + children, + NotFoundPage = TechDocsNotFound, +}: TechDocsPageProps) => { const outlet = useOutlet(); const [documentReady, setDocumentReady] = useState(false); @@ -67,7 +71,7 @@ export const TechDocsPage = ({ children }: TechDocsPageProps) => { }, [setDocumentReady]); if (entityMetadataError) { - return ; + return ; } if (!children) return outlet || ;