Merge pull request #11087 from backstage/camilal/techdocs-addons-app-intergration

[TechDocs] Integrate add-ons with create-app
This commit is contained in:
Camila Belo
2022-05-16 16:53:51 +02:00
committed by GitHub
12 changed files with 224 additions and 28 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs-module-addons-contrib': patch
---
Improved inline/type documentation for the <ReportIssue /> addon.
+37
View File
@@ -0,0 +1,37 @@
---
'@backstage/create-app': patch
---
Integrates TechDocs add-ons with the app package so add-ons are configured when creating an app using the Backstage CLI. To apply these changes to an existing application do the following:
1. Add the `@backstage/plugin-techdocs-react` and `@backstage/plugin-techdocs-module-addons-contrib` packages to your app's dependencies;
2. And then register the `<ReportIssue/ >` Addon in your `packages/app/src/App.tsx` file, there where you define a route to `<TechDocsReaderPage />`:
```diff
import {
DefaultTechDocsHome,
TechDocsIndexPage,
TechDocsReaderPage,
} from '@backstage/plugin-techdocs';
+ import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
+ import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
// ...
const AppRoutes = () => {
<FlatRoutes>
// ... other plugin routes
<Route path="/docs" element={<TechDocsIndexPage />}>
<DefaultTechDocsHome />
</Route>
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
>
+ <TechDocsAddons>
+ <ReportIssue />
+ </TechDocsAddons>
</Route>
</FlatRoutes>;
};
```
@@ -38,6 +38,7 @@ describe('TechDocs', () => {
it('should navigate to the TechDocs home page from the URL', () => {
cy.visit('/docs');
cy.wait(500);
cy.contains('Documentation');
@@ -45,15 +46,12 @@ describe('TechDocs', () => {
it('should navigate to a specific TechDocs entity from the "Overview" tab', () => {
cy.visit('/docs');
cy.contains('techdocs-e2e-fixture')
.parents()
.eq(2)
.contains('Read Docs')
.click();
cy.contains('techdocs-e2e-fixture').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
'/docs/default/Component/techdocs-e2e-fixture',
'/docs/default/component/techdocs-e2e-fixture',
);
});
});
@@ -84,7 +82,6 @@ describe('TechDocs', () => {
cy.visit(
'/docs/default/Component/techdocs-e2e-fixture/sub-page-two#section-23',
);
cy.waitSectionTwoPage();
// This is used to test the post-render behavior of the techdocs Reader
cy.wait(500);
@@ -101,10 +98,43 @@ describe('TechDocs', () => {
});
});
describe('Rendering TechDocs Addons', () => {
it('should render a content addon', () => {
cy.visit('/docs/default/Component/techdocs-e2e-fixture');
cy.contains('e2e Fixture Documentation');
// 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%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',
);
});
});
describe('Navigating within TechDocs', () => {
it('should navigate to a specific TechDocs page via the navigation bar', () => {
cy.visit('/docs/default/Component/techdocs-e2e-fixture');
cy.waitHomePage();
cy.getTechDocsShadowRoot().within(() => {
cy.getTechDocsNavigation()
@@ -118,14 +148,18 @@ describe('TechDocs', () => {
describe('Navigating within a TechDocs page', () => {
beforeEach(() => {
cy.visit('/docs/default/Component/techdocs-e2e-fixture/sub-page-two');
cy.waitSectionTwoPage();
});
it('should navigate to a specific fragment within the page via the table of contents - Level 1', () => {
return cy.getTechDocsShadowRoot().within(() => {
// Section 3
cy.getTechDocsTableOfContents().within(() => {
cy.get('> div > div > nav > ul > li:nth-child(3) > a').click();
});
cy.getTechDocsTableOfContents()
.find('a')
.contains('Section 2.3')
.click();
// wait scroll timeout
cy.wait(500);
cy.isInViewport('#section-23');
});
@@ -136,20 +170,20 @@ describe('TechDocs', () => {
cy.isNotInViewport('#sub-section-222');
// Section 2.2
cy.getTechDocsTableOfContents()
.find(
'> div > div > nav > ul > li:nth-child(2) > nav > ul > li:nth-child(2) > a',
)
.find('a')
.contains('Section 2.2.2')
.click();
// wait scroll timeout
cy.wait(500);
cy.isInViewport('#sub-section-222');
});
});
it('should navigate to a specific TechDocs page fragment from a link', () => {
return cy.getTechDocsShadowRoot().within(() => {
cy.get('.md-content > article')
.contains('Link to Section 1.1')
.click();
cy.get('article').contains('Link to Section 1.1').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
@@ -162,7 +196,7 @@ describe('TechDocs', () => {
it('should navigate to the next page within a TechDocs entity', () => {
return cy.getTechDocsShadowRoot().within(() => {
cy.get('.md-footer-nav__link--next').click();
cy.get('footer a').contains('Sub-page 3').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
@@ -174,7 +208,7 @@ describe('TechDocs', () => {
it('should navigate to the previous page within a TechDocs entity', () => {
return cy.getTechDocsShadowRoot().within(() => {
cy.get('.md-footer-nav__link--prev').click();
cy.get('footer a').contains('Sub-page 1').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
+3 -3
View File
@@ -25,7 +25,7 @@ Cypress.Commands.add('loginAsGuest', () => {
});
Cypress.Commands.add('getTechDocsShadowRoot', () => {
cy.get('[data-testid="techdocs-content-shadowroot"]').shadow();
cy.get('[data-testid="techdocs-native-shadowroot"]').shadow();
});
Cypress.Commands.add('isNotInViewport', element => {
@@ -57,11 +57,11 @@ Cypress.Commands.add('isInViewport', element => {
});
Cypress.Commands.add('getTechDocsTableOfContents', () => {
cy.get('[data-md-component="toc"]');
cy.get('[data-md-type="toc"]');
});
Cypress.Commands.add('getTechDocsNavigation', () => {
cy.get('[data-md-component="navigation"]');
cy.get('[data-md-type="navigation"]');
});
Cypress.Commands.add('mockSockJSNode', () => {
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

+42 -2
View File
@@ -54,8 +54,48 @@ const AppRoutes = () => {
};
```
That's it! But now, we need the TechDocs Backend plugin for the frontend to
work.
It would be nice to decorate your pages with something else... Having a link that redirects you to a new issue page when you highlight text in your documentation would be really cool, right? Let's learn how to do this using the TechDocs Addon Framework!
With the [TechDocs Addon framework](https://backstage.io/docs/features/techdocs/addons#installing-and-using-addons), you can render React components in documentation pages and these Addons can be provided by any Backstage plugin. The framework is exported by the [@backstage/plugin-techdocs-react](https://www.npmjs.com/package/@backstage/plugin-techdocs-react) package and there is a `<ReportIssue />` Addon in the [@backstage/plugin-techdocs-module-addons-contrib](https://www.npmjs.com/package/@backstage/plugin-techdocs-module-addons-contrib) package for you to use once you have these two dependencies installed:
```diff
import {
DefaultTechDocsHome,
TechDocsIndexPage,
TechDocsReaderPage,
} from '@backstage/plugin-techdocs';
+ import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
+ import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
// ...
const AppRoutes = () => {
<FlatRoutes>
// ... other plugin routes
<Route path="/docs" element={<TechDocsIndexPage />}>
<DefaultTechDocsHome />
</Route>
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
>
+ <TechDocsAddons>
+ <ReportIssue />
+ </TechDocsAddons>
</Route>
</FlatRoutes>;
};
```
I know, you're curious to see how it looks, aren't you? See the image below:
<img data-zoomable src="../../assets/techdocs/report-issue-addon.png" alt="TechDocs Report Issue Add-on" />
By clicking the open new issue button, you will be redirected to the new issue page according to the source code provider you are using:
<img data-zoomable src="../../assets/techdocs/report-issue-template.png" alt="TechDocs Report Issue Template" />
That's it! Now, we need the TechDocs Backend plugin for the frontend to work.
## Adding TechDocs Backend plugin
+2
View File
@@ -91,6 +91,8 @@
"@backstage/plugin-search-backend-node": "",
"@backstage/plugin-tech-radar": "",
"@backstage/plugin-techdocs": "",
"@backstage/plugin-techdocs-react": "",
"@backstage/plugin-techdocs-module-addons-contrib": "",
"@backstage/plugin-techdocs-backend": "",
"@backstage/plugin-user-settings": "",
"@backstage/test-utils": "",
+5
View File
@@ -75,6 +75,8 @@ import { version as pluginSearchBackendModulePg } from '../../../../plugins/sear
import { version as pluginSearchBackendNode } from '../../../../plugins/search-backend-node/package.json';
import { version as pluginTechRadar } from '../../../../plugins/tech-radar/package.json';
import { version as pluginTechdocs } from '../../../../plugins/techdocs/package.json';
import { version as pluginTechdocsReact } from '../../../../plugins/techdocs-react/package.json';
import { version as pluginTechdocsModuleAddonsContrib } from '../../../../plugins/techdocs-module-addons-contrib/package.json';
import { version as pluginTechdocsBackend } from '../../../../plugins/techdocs-backend/package.json';
import { version as pluginUserSettings } from '../../../../plugins/user-settings/package.json';
@@ -120,6 +122,9 @@ export const packageVersions = {
'@backstage/plugin-search-backend-node': pluginSearchBackendNode,
'@backstage/plugin-tech-radar': pluginTechRadar,
'@backstage/plugin-techdocs': pluginTechdocs,
'@backstage/plugin-techdocs-react': pluginTechdocsReact,
'@backstage/plugin-techdocs-module-addons-contrib':
pluginTechdocsModuleAddonsContrib,
'@backstage/plugin-techdocs-backend': pluginTechdocsBackend,
'@backstage/plugin-user-settings': pluginUserSettings,
'@backstage/test-utils': testUtils,
@@ -28,6 +28,8 @@
"@backstage/plugin-search-react": "^{{version '@backstage/plugin-search-react'}}",
"@backstage/plugin-tech-radar": "^{{version '@backstage/plugin-tech-radar'}}",
"@backstage/plugin-techdocs": "^{{version '@backstage/plugin-techdocs'}}",
"@backstage/plugin-techdocs-react": "^{{version '@backstage/plugin-techdocs-react'}}",
"@backstage/plugin-techdocs-module-addons-contrib": "^{{version '@backstage/plugin-techdocs-module-addons-contrib'}}",
"@backstage/plugin-user-settings": "^{{version '@backstage/plugin-user-settings'}}",
"@backstage/theme": "^{{version '@backstage/theme'}}",
"@material-ui/core": "^4.12.2",
@@ -19,6 +19,8 @@ import {
techdocsPlugin,
TechDocsReaderPage,
} from '@backstage/plugin-techdocs';
import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
import { UserSettingsPage } from '@backstage/plugin-user-settings';
import { apis } from './apis';
import { entityPage } from './components/catalog/EntityPage';
@@ -68,7 +70,11 @@ const routes = (
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
/>
>
<TechDocsAddons>
<ReportIssue />
</TechDocsAddons>
</Route>
<Route path="/create" element={<ScaffolderPage />} />
<Route path="/api-docs" element={<ApiExplorerPage />} />
<Route
@@ -82,9 +82,74 @@ export const ExpandableNavigation = techdocsModuleAddonsContribPlugin.provide(
/**
* TechDocs addon that lets you select text and open GitHub/Gitlab issues
*
* @remarks
* Before using it, you should set up an `edit_uri` for your pages as explained {@link https://backstage.io/docs/features/techdocs/faqs#is-it-possible-for-users-to-suggest-changes-or-provide-feedback-on-a-techdocs-page | here} and remember, it only works for Github or Gitlab.
*
* @example
* Here's a simple example:
* ```
* import {
* DefaultTechDocsHome,
* TechDocsIndexPage,
* TechDocsReaderPage,
* } from '@backstage/plugin-techdocs';
* import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
* import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
*
*
* const AppRoutes = () => {
* <FlatRoutes>
* // other plugin routes
* <Route path="/docs" element={<TechDocsIndexPage />}>
* <DefaultTechDocsHome />
* </Route>
* <Route
* path="/docs/:namespace/:kind/:name/*"
* element={<TechDocsReaderPage />}
* >
* <TechDocsAddons>
* <ReportIssue />
* </TechDocsAddons>
* </Route>
* </FlatRoutes>;
* };
* ```
*
* @example
* Here's an example with `debounceTime` and `templateBuilder` props:
* ```
* import {
* DefaultTechDocsHome,
* TechDocsIndexPage,
* TechDocsReaderPage,
* } from '@backstage/plugin-techdocs';
* import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
* import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
*
* const templateBuilder = ({ selection }: ReportIssueTemplateBuilder) => (({
* title: 'Custom issue title',
* body: `Custom issue body: ${selection.toString()}`
* }))
*
* const AppRoutes = () => {
* <FlatRoutes>
* // other plugin routes
* <Route path="/docs" element={<TechDocsIndexPage />}>
* <DefaultTechDocsHome />
* </Route>
* <Route
* path="/docs/:namespace/:kind/:name/*"
* element={<TechDocsReaderPage />}
* >
* <TechDocsAddons>
* <ReportIssue debounceTime={300} templateBuilder={templateBuilder} />
* </TechDocsAddons>
* </Route>
* </FlatRoutes>;
* ```
* @param props - Object that can optionally contain `debounceTime` and `templateBuilder` properties.
* @public
*/
export const ReportIssue = techdocsModuleAddonsContribPlugin.provide(
createTechDocsAddonExtension<ReportIssueProps>({
name: 'ReportIssue',
@@ -107,7 +172,7 @@ export const ReportIssue = techdocsModuleAddonsContribPlugin.provide(
* TechDocsIndexPage,
* TechDocsReaderPage,
* } from '@backstage/plugin-techdocs';
* import { TechDocsAddons } from '@backstage/plugin-techdocs-react/alpha';
* import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
* import { TextSize } from '@backstage/plugin-techdocs-module-addons-contrib';
*
*