From 443accb4bb2e26f029b0902a4e7d5b36e53b4931 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 19 May 2022 14:23:52 +0200 Subject: [PATCH 1/5] fix(techdocs): use object routes on entity docs pages Signed-off-by: Camila Belo --- .../app/src/components/catalog/EntityPage.tsx | 27 ++++++++++++++++--- plugins/techdocs/api-report.md | 6 +++-- plugins/techdocs/src/Router.tsx | 24 +++++++++++------ 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 22d6cc3ab1..c0edbc6ed0 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -140,6 +140,12 @@ import { EntityGoCdContent, isGoCdAvailable } from '@backstage/plugin-gocd'; import React, { ReactNode, useMemo, useState } from 'react'; +import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; +import { + TextSize, + ReportIssue, +} from '@backstage/plugin-techdocs-module-addons-contrib'; + const customEntityFilterKind = ['Component', 'API', 'System']; const EntityLayoutWrapper = (props: { children?: ReactNode }) => { @@ -398,7 +404,12 @@ const serviceEntityPage = ( - + + + + + + - + + + + + + - + + + + + + diff --git a/plugins/techdocs/api-report.md b/plugins/techdocs/api-report.md index a8204bc9af..b2090bc879 100644 --- a/plugins/techdocs/api-report.md +++ b/plugins/techdocs/api-report.md @@ -111,7 +111,9 @@ export type DocsTableRow = { }; // @public -export const EmbeddedDocsRouter: (props: PropsWithChildren<{}>) => JSX.Element; +export const EmbeddedDocsRouter: ( + props: PropsWithChildren<{}>, +) => JSX.Element | null; // @public export const EntityListDocsGrid: () => JSX.Element; @@ -153,7 +155,7 @@ export type EntityListDocsTableProps = { // @public export const EntityTechdocsContent: (props: { children?: ReactNode; -}) => JSX.Element; +}) => JSX.Element | null; // @public export const isTechDocsAvailable: (entity: Entity) => boolean; diff --git a/plugins/techdocs/src/Router.tsx b/plugins/techdocs/src/Router.tsx index de63838bc8..12b3ddf611 100644 --- a/plugins/techdocs/src/Router.tsx +++ b/plugins/techdocs/src/Router.tsx @@ -15,7 +15,7 @@ */ import React, { PropsWithChildren } from 'react'; -import { Route, Routes } from 'react-router-dom'; +import { Route, Routes, useRoutes } from 'react-router-dom'; import { Entity } from '@backstage/catalog-model'; import { useEntity } from '@backstage/plugin-catalog-react'; @@ -61,17 +61,25 @@ export const EmbeddedDocsRouter = (props: PropsWithChildren<{}>) => { const { children } = props; const { entity } = useEntity(); + // Using objects instead of elements, otherwise "outlet" will be null on sub-pages and add-ons won't render + const element = useRoutes([ + { + path: '/*', + element: , + children: [ + { + path: '/*', + element: children, + }, + ], + }, + ]); + const projectId = entity.metadata.annotations?.[TECHDOCS_ANNOTATION]; if (!projectId) { return ; } - return ( - - }> - {children} - - - ); + return element; }; From 89e78851cc7b0f250edbd8f3e98e6613f5607476 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 19 May 2022 16:37:19 +0200 Subject: [PATCH 2/5] feat(create-app): add docs addons on entity page Signed-off-by: Camila Belo --- .../app/src/components/catalog/EntityPage.tsx | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx index 84d094410b..2695f4033e 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -68,6 +68,11 @@ import { RELATION_PROVIDES_API, } from '@backstage/catalog-model'; +import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; +import { + ReportIssue, +} from '@backstage/plugin-techdocs-module-addons-contrib'; + const cicdContent = ( // This is an example of how you can implement your company's logic in entity page. // You can for example enforce that all components of type 'service' should use GitHubActions @@ -167,7 +172,11 @@ const serviceEntityPage = ( - + + + + + ); @@ -194,7 +203,11 @@ const websiteEntityPage = ( - + + + + + ); @@ -213,7 +226,11 @@ const defaultEntityPage = ( - + + + + + ); From 9bf2f0f476c54eb47ee30c8f39117b1df410ab16 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 19 May 2022 16:40:29 +0200 Subject: [PATCH 3/5] test(cypress): docs addons on catalog sub-pages Signed-off-by: Camila Belo --- .../src/integration/plugins/catalog.spec.ts | 113 ++++++++++++++++++ .../src/integration/plugins/techdocs.spec.ts | 39 +++++- 2 files changed, 151 insertions(+), 1 deletion(-) diff --git a/cypress/src/integration/plugins/catalog.spec.ts b/cypress/src/integration/plugins/catalog.spec.ts index adf9b9a384..50ef3145e5 100644 --- a/cypress/src/integration/plugins/catalog.spec.ts +++ b/cypress/src/integration/plugins/catalog.spec.ts @@ -82,5 +82,118 @@ describe('Catalog', () => { .contains('Sub-page 1') .should('be.visible'); }); + + it('Should render addons on docs tab homepage', () => { + cy.loginAsGuest(); + + cy.visit('/catalog'); + + cy.contains('techdocs-e2e-fixture').click(); + + cy.location().should(loc => { + expect(loc.pathname).to.eq( + '/catalog/default/component/techdocs-e2e-fixture', + ); + }); + + cy.getCatalogDocsTab().click(); + + cy.wait(300); + + cy.getTechDocsShadowRoot() + .find('h1') + .contains('Home page') + .should('be.visible'); + + // highlight a snippet of text + cy.getTechDocsShadowRoot() + .find('article > p') + .then($el => { + const el = $el[0]; + const document = el.ownerDocument; + const range = document.createRange(); + range.selectNodeContents(el); + document?.getSelection()?.removeAllRanges(); + document?.getSelection()?.addRange(range); + }); + + cy.document().trigger('selectionchange'); + + // wait for new issue default debounce time + cy.wait(600); + + // assert that the new issue button has a right url + cy.getTechDocsShadowRoot() + .contains('Open new Github issue') + .should( + 'have.attr', + 'href', + 'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fcatalog%2Fdefault%2Fcomponent%2Ftechdocs-e2e-fixture%2Fdocs%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Findex.md%3E', + ); + }); + + it('Should render addons on docs tab sup-page', () => { + cy.loginAsGuest(); + + cy.visit('/catalog'); + + cy.contains('techdocs-e2e-fixture').click(); + + cy.location().should(loc => { + expect(loc.pathname).to.eq( + '/catalog/default/component/techdocs-e2e-fixture', + ); + }); + + cy.getCatalogDocsTab().click(); + + cy.wait(300); + + cy.getTechDocsShadowRoot() + .find('h1') + .contains('Home page') + .should('be.visible'); + + cy.getTechDocsShadowRoot().within(() => { + cy.getTechDocsNavigation().find('a').contains('Sub-page 1').click(); + }); + + cy.location().should(loc => { + expect(loc.pathname).to.eq( + '/catalog/default/component/techdocs-e2e-fixture/docs/sub-page-one/', + ); + }); + + cy.getTechDocsShadowRoot() + .find('h1') + .contains('Sub-page 1') + .should('be.visible'); + + // highlight a snippet of text + cy.getTechDocsShadowRoot() + .find('#section-11') + .then($el => { + const el = $el[0]; + const document = el.ownerDocument; + const range = document.createRange(); + range.selectNodeContents(el); + document?.getSelection()?.removeAllRanges(); + document?.getSelection()?.addRange(range); + }); + + cy.document().trigger('selectionchange'); + + // wait for new issue default debounce time + cy.wait(600); + + // assert that the new issue button has a right url + cy.getTechDocsShadowRoot() + .contains('Open new Github issue') + .should( + 'have.attr', + 'href', + 'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20Section%201.1%C2%B6&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20Section%201.1%C2%B6%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fcatalog%2Fdefault%2Fcomponent%2Ftechdocs-e2e-fixture%2Fdocs%2Fsub-page-one%2F%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Fsub-page-one.md%3E', + ); + }); }); }); diff --git a/cypress/src/integration/plugins/techdocs.spec.ts b/cypress/src/integration/plugins/techdocs.spec.ts index 1e69ba46ad..31094cb128 100644 --- a/cypress/src/integration/plugins/techdocs.spec.ts +++ b/cypress/src/integration/plugins/techdocs.spec.ts @@ -99,7 +99,7 @@ describe('TechDocs', () => { }); describe('Rendering TechDocs Addons', () => { - it('should render a content addon', () => { + it('should render a content addon in homepage', () => { cy.visit('/docs/default/Component/techdocs-e2e-fixture'); cy.contains('e2e Fixture Documentation'); @@ -130,6 +130,43 @@ describe('TechDocs', () => { 'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fdocs%2Fdefault%2FComponent%2Ftechdocs-e2e-fixture%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Findex.md%3E', ); }); + + it('should render a content addon in sub-pages', () => { + cy.visit('/docs/default/Component/techdocs-e2e-fixture'); + + cy.contains('e2e Fixture Documentation'); + + // open sub-page + cy.getTechDocsShadowRoot().within(() => { + cy.getTechDocsNavigation().find('a').contains('Sub-page 1').click(); + }); + + // highlight a snippet of text + cy.getTechDocsShadowRoot() + .find('#section-11') + .then($el => { + const el = $el[0]; + const document = el.ownerDocument; + const range = document.createRange(); + range.selectNodeContents(el); + document?.getSelection()?.removeAllRanges(); + document?.getSelection()?.addRange(range); + }); + + cy.document().trigger('selectionchange'); + + // wait for new issue default debounce time + cy.wait(600); + + // assert that the new issue button has a right url + cy.getTechDocsShadowRoot() + .contains('Open new Github issue') + .should( + 'have.attr', + 'href', + 'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20Section%201.1%C2%B6&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20Section%201.1%C2%B6%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fdocs%2Fdefault%2FComponent%2Ftechdocs-e2e-fixture%2Fsub-page-one%2F%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Fsub-page-one.md%3E', + ); + }); }); describe('Navigating within TechDocs', () => { From 881fbd7e8dea79b4da27b96b54234dc70680be06 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 19 May 2022 14:41:09 +0200 Subject: [PATCH 4/5] chore: add changeset files Signed-off-by: Camila Belo --- .changeset/fair-grapes-joke.md | 5 +++++ .changeset/techdocs-buttons-film.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/fair-grapes-joke.md create mode 100644 .changeset/techdocs-buttons-film.md diff --git a/.changeset/fair-grapes-joke.md b/.changeset/fair-grapes-joke.md new file mode 100644 index 0000000000..5abfa0bb50 --- /dev/null +++ b/.changeset/fair-grapes-joke.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Register the `TechDocs` addons on the catalog page and also test their rendering in `EntityDocs` sub pages. diff --git a/.changeset/techdocs-buttons-film.md b/.changeset/techdocs-buttons-film.md new file mode 100644 index 0000000000..46895ff7ce --- /dev/null +++ b/.changeset/techdocs-buttons-film.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Fix `EntityDocs` component to use objects instead of `` elements, otherwise "outlet" will be null on sub-pages and add-ons won't render. From 1e5d66f7018b92554601848fa4ce97980cc911e8 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 19 May 2022 17:32:23 +0200 Subject: [PATCH 5/5] refactor: apply review suggestions Signed-off-by: Camila Belo --- .changeset/fair-grapes-joke.md | 43 ++++++++++++++++++- .changeset/techdocs-buttons-film.md | 2 +- .../app/src/components/catalog/EntityPage.tsx | 31 ++++++------- .../app/src/components/catalog/EntityPage.tsx | 30 ++++++------- 4 files changed, 68 insertions(+), 38 deletions(-) diff --git a/.changeset/fair-grapes-joke.md b/.changeset/fair-grapes-joke.md index 5abfa0bb50..8ab6516833 100644 --- a/.changeset/fair-grapes-joke.md +++ b/.changeset/fair-grapes-joke.md @@ -2,4 +2,45 @@ '@backstage/create-app': patch --- -Register the `TechDocs` addons on the catalog page and also test their rendering in `EntityDocs` sub pages. +Register `TechDocs` addons on catalog entity pages, follow the steps below to add them manually: + +```diff +// packages/app/src/components/catalog/EntityPage.tsx + ++ import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; ++ import { ++ ReportIssue, ++ } from '@backstage/plugin-techdocs-module-addons-contrib'; + ++ const techdocsContent = ( ++ ++ ++ ++ ++ ++ ); + +const defaultEntityPage = ( + ... + ++ {techdocsContent} + + ... +); + +const serviceEntityPage = ( + ... + ++ {techdocsContent} + + ... +); + +const websiteEntityPage = ( + ... + ++ {techdocsContent} + + ... +); +``` diff --git a/.changeset/techdocs-buttons-film.md b/.changeset/techdocs-buttons-film.md index 46895ff7ce..1af1ead4c7 100644 --- a/.changeset/techdocs-buttons-film.md +++ b/.changeset/techdocs-buttons-film.md @@ -2,4 +2,4 @@ '@backstage/plugin-techdocs': patch --- -Fix `EntityDocs` component to use objects instead of `` elements, otherwise "outlet" will be null on sub-pages and add-ons won't render. +Fix `EntityTechdocsContent` component to use objects instead of `` elements, otherwise "outlet" will be null on sub-pages and add-ons won't render. diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index c0edbc6ed0..799fe3e4d9 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -174,6 +174,15 @@ const EntityLayoutWrapper = (props: { children?: ReactNode }) => { ); }; +const techdocsContent = ( + + + + + + +); + /** * NOTE: This page is designed to work on small screens such as mobile devices. * This is based on Material UI Grid. If breakpoints are used, each grid item must set the `xs` prop to a column size or to `true`, @@ -404,12 +413,7 @@ const serviceEntityPage = ( - - - - - - + {techdocsContent} - - - - - - + {techdocsContent} + - - - - - - + {techdocsContent} diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx index 2695f4033e..6ec4da055d 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -69,9 +69,15 @@ import { } from '@backstage/catalog-model'; import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; -import { - ReportIssue, -} from '@backstage/plugin-techdocs-module-addons-contrib'; +import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; + +const techdocsContent = ( + + + + + +); const cicdContent = ( // This is an example of how you can implement your company's logic in entity page. @@ -172,11 +178,7 @@ const serviceEntityPage = ( - - - - - + {techdocsContent} ); @@ -203,11 +205,7 @@ const websiteEntityPage = ( - - - - - + {techdocsContent} ); @@ -226,11 +224,7 @@ const defaultEntityPage = ( - - - - - + {techdocsContent} );