techdocs: add extensions for techdocs addons (#28644)

* techdocs: add extensions for techdocs addons

Signed-off-by: Jackson Chen <jacksonc@spotify.com>

* techdocs: add blueprint extension for techdocs addons

Signed-off-by: Jackson Chen <jacksonc@spotify.com>

* techdocs: move addons blueprint to alpha

Signed-off-by: Jackson Chen <jacksonc@spotify.com>

* techdocs: add addon extensions for new frontend system and add docs

Signed-off-by: Jackson Chen <jacksonc@spotify.com>

* techdocs: fix addon modules naming patterns

Signed-off-by: Jackson Chen <jacksonc@spotify.com>

* techdocs: update test utils with entity presentation api

Signed-off-by: Jackson Chen <jacksonc@spotify.com>

---------

Signed-off-by: Jackson Chen <jacksonc@spotify.com>
This commit is contained in:
Jackson Chen
2025-02-21 16:39:45 -05:00
committed by GitHub
parent b8ef3783fb
commit b5a82087a7
20 changed files with 589 additions and 30 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-techdocs': patch
'@backstage/plugin-techdocs-react': patch
'@backstage/plugin-techdocs-module-addons-contrib': patch
---
Added `TechDocsAddonsBlueprint` extension to allow adding of techdocs addons.
+204
View File
@@ -0,0 +1,204 @@
---
id: addons
title: TechDocs Addons
description: How to find, use, or create TechDocs Addons.
---
:::info
This documentation is written for [the new frontend system](../../frontend-system/index.md) which is still in alpha and is only supported by a small number of plugins.
:::
## Concepts
TechDocs is a centralized platform for publishing, viewing, and discovering
technical documentation across an entire organization. It's a solid foundation!
But it doesn't solve higher-order documentation needs on its own: how do you
create and reinforce a culture of documentation? How do you build trust in the
quality of technical documentation?
TechDocs Addons are a mechanism by which you can customize the TechDocs
experience in order to try and address some of these higher-order needs.
### Addons
An Addon is just a react component. Like any react component, it can retrieve
and render data using normal Backstage or native hooks, APIs, and components.
Props can be used to configure its behavior, where appropriate.
### Locations
Addons declare a `location` where they will be rendered. Most locations are
representative of physical spaces in the TechDocs UI:
- `Header`: For Addons which fill up the header from the right, on the same
line as the title.
- `Subheader`: For Addons that sit below the header but above all content.
This is a great location for tooling/configuration of TechDocs display.
- `Settings`: These addons are items added to the settings menu list and are designed to make
the reader experience customizable, for example accessibility options.
- `PrimarySidebar`: Left of the content, above of the navigation.
- `SecondarySidebar`: Right of the content, above the table of contents.
- `Content`: A special location intended for Addons which augment the
statically generated content of the documentation itself.
- `Component`: A [proposed-but-not-yet-implemented](https://github.com/backstage/backstage/issues/11109)
virtual location, aimed at simplifying a common type of Addon.
<!-- todo: Needs zoomable plugin -->
![TechDocs Addon Location Guide](../../assets/techdocs/addon-locations.png)
### Addon Registry
The installation and configuration of Addons happens within a Backstage app's
frontend. Addons are imported from plugins and registered as a plugin extension which
are configured for both the TechDocs Reader page as well as the Entity docs page.
Addons are rendered in the order in which they are registered.
## Installing and using Addons
To start using Addons you need to add the `@backstage/plugin-techdocs-module-addons-contrib` package to your app. You can do that by running this command from the root of your project: `yarn --cwd packages/app add @backstage/plugin-techdocs-module-addons-contrib`
Addons can then be installed as a module in your `App.tsx`:
```tsx
// packages/app/src/App.tsx
import { createApp } from '@backstage/frontend-defaults';
import { createFrontendModule } from '@backstage/frontend-plugin-api';
import { techDocsReportIssueAddonModule } from '@backstage/plugin-techdocs-module-addons-contrib/alpha';
// ...
const app = createApp({
features: [
// ...
techDocsReportIssueAddonModule,
// ...other techdocs addon modules
],
});
export default app.createRoot();
```
Note that on the entity page, because the Catalog plugin is responsible for the
page header, TechDocs Addons whose location is `Header` will not be rendered.
## Available Addons
Addons can, in principle, be provided by any plugin! To make it easier to
discover available Addons, we've compiled a list of them here:
| Addon | Package/Plugin | Description |
| ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`techDocsExpandableNavigationAddonModule`](https://backstage.io/docs/reference/plugin-techdocs-module-addons-contrib.expandablenavigation) | `@backstage/plugin-techdocs-module-addons-contrib/alpha` | Allows TechDocs users to expand or collapse the entire TechDocs main navigation, and keeps the user's preferred state between documentation sites. |
| [`techDocsReportIssueAddonModule`](https://backstage.io/docs/reference/plugin-techdocs-module-addons-contrib.reportissue) | `@backstage/plugin-techdocs-module-addons-contrib/alpha` | Allows TechDocs users to select a portion of text on a TechDocs page and open an issue against the repository that contains the documentation, populating the issue description with the selected text according to a configurable template. |
| [`techDocsTextSizeAddonModule`](https://backstage.io/docs/reference/plugin-techdocs-module-addons-contrib.textsize) | `@backstage/plugin-techdocs-module-addons-contrib/alpha` | This TechDocs addon allows users to customize text size on documentation pages, they can select how much they want to increase or decrease the font size via slider or buttons. The default value for font size is 100% and this setting is kept in the browser's local storage whenever it is changed. |
| [`techDocsLightBoxAddonModule`](https://backstage.io/docs/reference/plugin-techdocs-module-addons-contrib.lightbox) | `@backstage/plugin-techdocs-module-addons-contrib/alpha` | This TechDocs addon allows users to open images in a light-box on documentation pages, they can navigate between images if there are several on one page. The image size of the light-box image is the same as the image size on the document page. When clicking on the zoom icon it zooms the image to fit in the screen (similar to `background-size: contain`). |
Got an Addon to contribute? Feel free to add a row above!
## Creating an Addon
The simplest Addons are plain old react components that get rendered in
specific locations within a TechDocs site. To package such a react component as
an Addon, follow these steps:
1. Write the component in your plugin like any other component
2. Create the addon extension using the `TechDocsAddonBlueprint`
3. Create and export the addon module from your plugin
```ts
// plugins/your-plugin/src/plugin.ts
import { TechDocsAddonLocations } from '@backstage/plugin-techdocs-react';
import { AddonBlueprint } from '@backstage/plugin-techdocs-react/alpha';
import { CatGifComponent } from './addons';
import { createFrontendModule } from '@backstage/frontend-plugin-api';
// ...
const techDocsCatGifAddon = AddonBlueprint.make({
name: 'cat-gif',
params: {
name: 'CatGif',
location: TechDocsAddonLocations.Header,
component: CatGifComponent,
},
});
export const techDocsCatGifAddonModule = createFrontendModule({
pluginId: 'techdocs',
extensions: [techDocsCatGifAddon],
});
```
### Addons in the Content location
Beyond the "render a component in a region" use-case, it's also possible for
Addons to access and manipulate a TechDocs site's DOM; this could be used to,
for example, load and instantiate client-side diagramming libraries, replace
elements with dynamically loaded content, etc.
This type of Addon is still expressed as a react component, but instead of
returning a react element to be rendered, it updates the DOM via side-effects
(e.g. with `useEffect`). Access to the DOM is made available via utility hooks
provided by the Addon framework.
```tsx
// plugins/your-plugin/src/addons/MakeAllImagesCatGifs.tsx
import React, { useEffect } from 'react';
import { useShadowRootElements } from '@backstage/plugin-techdocs-react';
// This is a normal react component; in order to make it an Addon, you would
// still create and provide it via your plugin as described above. The only
// difference is that you'd set `location` to `TechDocsAddonLocations.Content`.
export const MakeAllImagesCatGifsAddon = () => {
// This hook can be used to get references to specific elements. If you need
// access to the whole shadow DOM, use the underlying useShadowRoot()
// hook instead.
const images = useShadowRootElements<HTMLImageElement>(['img']);
useEffect(() => {
images.forEach(img => {
if (img.src !== 'https://example.com/cat.gif') {
img.src = 'https://example.com/cat.gif';
}
});
}, [images]);
// Nothing to render directly, so we can just return null.
return null;
};
```
### Testing Addons
Install `@backstage/plugin-techdocs-addons-test-utils` as a `devDependency` in
your plugin for access to utilities that make testing such Addons easier.
A test for the above Addon might look something like this:
```tsx
// plugins/your-plugin/src/addons/MakeAllImagesCatGifs.test.tsx
import { TechDocsAddonTester } from '@backstage/plugin-techdocs-addons-test-utils';
// Note: import your actual addon (the one provided by your plugin).
import { MakeAllImagesCatGifs } from '../plugin.ts';
describe('MakeAllImagesCatGifs', () => {
it('replaces img srcs with cat gif', async () => {
const { getByTestId } = await TechDocsAddonTester.buildAddonsInTechDocs([
<MakeAllImagesCatGifs />,
])
.withDom(<img data-testid="fixture" src="http://example.com/dog.jpg" />)
.renderWithEffects();
expect(getByTestId('fixture')).toHaveAttribute(
'src',
'https://example.com/cat.gif',
);
});
});
```
+4
View File
@@ -4,6 +4,10 @@ title: TechDocs Addons
description: How to find, use, or create TechDocs Addons.
---
:::info
This documentation is written for [the old frontend system](./getting-started.md#adding-techdocs-frontend-plugin). If you are on the [new frontend system](../../frontend-system/index.md) you may want to read [its own article](./addons--new.md) instead.
:::
## Concepts
TechDocs is a centralized platform for publishing, viewing, and discovering
@@ -38,7 +38,11 @@ import {
techdocsStorageApiRef,
} from '@backstage/plugin-techdocs-react';
import { TechDocsReaderPage, techdocsPlugin } from '@backstage/plugin-techdocs';
import { entityRouteRef } from '@backstage/plugin-catalog-react';
import {
EntityPresentationApi,
entityPresentationApiRef,
entityRouteRef,
} from '@backstage/plugin-catalog-react';
import { searchApiRef } from '@backstage/plugin-search-react';
import { scmIntegrationsApiRef } from '@backstage/integration-react';
@@ -223,8 +227,17 @@ export class TechDocsAddonTester {
}),
};
const entityPresentationApi: EntityPresentationApi = {
forEntity: jest.fn().mockReturnValue({
snapshot: {
primaryTitle: 'Test Entity',
},
}),
};
const apis: TechdocsAddonTesterApis<any[]> = [
[fetchApiRef, fetchApi],
[entityPresentationApiRef, entityPresentationApi],
[discoveryApiRef, discoveryApi],
[techdocsApiRef, techdocsApi],
[techdocsStorageApiRef, techdocsStorageApi],
@@ -8,9 +8,7 @@
"pluginPackage": "@backstage/plugin-techdocs"
},
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
"access": "public"
},
"keywords": [
"backstage",
@@ -24,8 +22,23 @@
},
"license": "Apache-2.0",
"sideEffects": false,
"exports": {
".": "./src/index.ts",
"./alpha": "./src/alpha.ts",
"./package.json": "./package.json"
},
"main": "src/index.ts",
"types": "src/index.ts",
"typesVersions": {
"*": {
"alpha": [
"src/alpha.ts"
],
"package.json": [
"package.json"
]
}
},
"files": [
"dist"
],
@@ -41,6 +54,7 @@
"dependencies": {
"@backstage/core-components": "workspace:^",
"@backstage/core-plugin-api": "workspace:^",
"@backstage/frontend-plugin-api": "workspace:^",
"@backstage/integration": "workspace:^",
"@backstage/integration-react": "workspace:^",
"@backstage/plugin-techdocs-react": "workspace:^",
@@ -0,0 +1,21 @@
## API Report File for "@backstage/plugin-techdocs-module-addons-contrib"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { FrontendModule } from '@backstage/frontend-plugin-api';
// @alpha (undocumented)
export const techDocsExpandableNavigationAddonModule: FrontendModule;
// @alpha (undocumented)
export const techDocsLightBoxAddonModule: FrontendModule;
// @alpha (undocumented)
export const techDocsReportIssueAddonModule: FrontendModule;
// @alpha (undocumented)
export const techDocsTextSizeAddonModule: FrontendModule;
// (No @packageDocumentation comment for this package)
```
@@ -0,0 +1,86 @@
/*
* Copyright 2025 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 { TechDocsAddonLocations } from '@backstage/plugin-techdocs-react';
import { AddonBlueprint } from '@backstage/plugin-techdocs-react/alpha';
import { TextSizeAddon } from './TextSize';
import { ReportIssueAddon } from './ReportIssue';
import { ExpandableNavigationAddon } from './ExpandableNavigation';
import { LightBoxAddon } from './LightBox';
import { createFrontendModule } from '@backstage/frontend-plugin-api';
/** @alpha */
const techDocsExpandableNavigationAddon = AddonBlueprint.make({
name: 'expandable-navigation',
params: {
name: 'ExpandableNavigation',
location: TechDocsAddonLocations.PrimarySidebar,
component: ExpandableNavigationAddon,
},
});
/** @alpha */
export const techDocsExpandableNavigationAddonModule = createFrontendModule({
pluginId: 'techdocs',
extensions: [techDocsExpandableNavigationAddon],
});
/** @alpha */
const techDocsReportIssueAddon = AddonBlueprint.make({
name: 'report-issue',
params: {
name: 'ReportIssue',
location: TechDocsAddonLocations.Content,
component: ReportIssueAddon,
},
});
/** @alpha */
export const techDocsReportIssueAddonModule = createFrontendModule({
pluginId: 'techdocs',
extensions: [techDocsReportIssueAddon],
});
/** @alpha */
const techDocsTextSizeAddon = AddonBlueprint.make({
name: 'text-size',
params: {
name: 'TextSize',
location: TechDocsAddonLocations.Settings,
component: TextSizeAddon,
},
});
/** @alpha */
export const techDocsTextSizeAddonModule = createFrontendModule({
pluginId: 'techdocs',
extensions: [techDocsTextSizeAddon],
});
/** @alpha */
const techDocsLightBoxAddon = AddonBlueprint.make({
name: 'light-box',
params: {
name: 'LightBox',
location: TechDocsAddonLocations.Content,
component: LightBoxAddon,
},
});
/** @alpha */
export const techDocsLightBoxAddonModule = createFrontendModule({
pluginId: 'techdocs',
extensions: [techDocsLightBoxAddon],
});
@@ -22,7 +22,7 @@ import {
import { ExpandableNavigationAddon } from './ExpandableNavigation';
import { ReportIssueAddon, ReportIssueProps } from './ReportIssue';
import { TextSizeAddon } from './TextSize';
import { LightBoxAddon } from './LigthBox';
import { LightBoxAddon } from './LightBox';
/**
* The TechDocs addons contrib plugin
+17 -3
View File
@@ -14,9 +14,7 @@
]
},
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
"access": "public"
},
"keywords": [
"backstage",
@@ -30,8 +28,23 @@
},
"license": "Apache-2.0",
"sideEffects": false,
"exports": {
".": "./src/index.ts",
"./alpha": "./src/alpha.ts",
"./package.json": "./package.json"
},
"main": "src/index.ts",
"types": "src/index.ts",
"typesVersions": {
"*": {
"alpha": [
"src/alpha.ts"
],
"package.json": [
"package.json"
]
}
},
"files": [
"dist"
],
@@ -49,6 +62,7 @@
"@backstage/config": "workspace:^",
"@backstage/core-components": "workspace:^",
"@backstage/core-plugin-api": "workspace:^",
"@backstage/frontend-plugin-api": "workspace:^",
"@backstage/version-bridge": "workspace:^",
"@material-ui/core": "^4.12.2",
"@material-ui/styles": "^4.11.0",
@@ -0,0 +1,63 @@
## API Report File for "@backstage/plugin-techdocs-react"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { ComponentType } from 'react';
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprint } from '@backstage/frontend-plugin-api';
// @alpha
export const AddonBlueprint: ExtensionBlueprint<{
kind: 'addon';
name: undefined;
params: TechDocsAddonOptions;
output: ConfigurableExtensionDataRef<
TechDocsAddonOptions,
'techdocs.addon',
{}
>;
inputs: {};
config: {};
configInput: {};
dataRefs: {
addon: ConfigurableExtensionDataRef<
TechDocsAddonOptions,
'techdocs.addon',
{}
>;
};
}>;
// @alpha (undocumented)
export const attachTechDocsAddonComponentData: <P>(
techDocsAddon: ComponentType<P>,
data: TechDocsAddonOptions,
) => void;
// @alpha (undocumented)
export const techDocsAddonDataRef: ConfigurableExtensionDataRef<
TechDocsAddonOptions,
'techdocs.addon',
{}
>;
// @public
export const TechDocsAddonLocations: Readonly<{
readonly Header: 'Header';
readonly Subheader: 'Subheader';
readonly Settings: 'Settings';
readonly PrimarySidebar: 'PrimarySidebar';
readonly SecondarySidebar: 'SecondarySidebar';
readonly Content: 'Content';
}>;
// @public
export type TechDocsAddonOptions<TAddonProps = {}> = {
name: string;
location: keyof typeof TechDocsAddonLocations;
component: ComponentType<TAddonProps>;
};
// (No @packageDocumentation comment for this package)
```
+1 -1
View File
@@ -49,7 +49,7 @@ export const TechDocsAddons: React.ComponentType<
attachComponentData(TechDocsAddons, TECHDOCS_ADDONS_WRAPPER_KEY, true);
const getDataKeyByName = (name: string) => {
export const getDataKeyByName = (name: string) => {
return `${TECHDOCS_ADDONS_KEY}.${name.toLocaleLowerCase('en-US')}`;
};
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright 2025 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 { TechDocsAddonOptions } from './types';
import { attachComponentData } from '@backstage/core-plugin-api';
import { ComponentType } from 'react';
import { getDataKeyByName, TECHDOCS_ADDONS_KEY } from './addons';
import {
createExtensionBlueprint,
createExtensionDataRef,
} from '@backstage/frontend-plugin-api';
/** @alpha */
export type { TechDocsAddonOptions, TechDocsAddonLocations } from './types';
/** @alpha */
export const techDocsAddonDataRef =
createExtensionDataRef<TechDocsAddonOptions>().with({
id: 'techdocs.addon',
});
/**
* Creates an extension to add addons to the TechDocs standalone reader and entity pages.
* @alpha
*/
export const AddonBlueprint = createExtensionBlueprint({
kind: 'addon',
attachTo: [
{ id: 'page:techdocs/reader', input: 'addons' },
{ id: 'entity-content:techdocs', input: 'addons' },
],
output: [techDocsAddonDataRef],
factory: (params: TechDocsAddonOptions) => [techDocsAddonDataRef(params)],
dataRefs: {
addon: techDocsAddonDataRef,
},
});
/** @alpha */
export const attachTechDocsAddonComponentData = <P>(
techDocsAddon: ComponentType<P>,
data: TechDocsAddonOptions,
) => {
attachComponentData(techDocsAddon, TECHDOCS_ADDONS_KEY, data);
attachComponentData(techDocsAddon, getDataKeyByName(data.name), true);
};
+27 -3
View File
@@ -17,6 +17,7 @@ import { RouteRef } from '@backstage/frontend-plugin-api';
import { SearchResultItemExtensionComponent } from '@backstage/plugin-search-react/alpha';
import { SearchResultItemExtensionPredicate } from '@backstage/plugin-search-react/alpha';
import { SearchResultListItemBlueprintParams } from '@backstage/plugin-search-react/alpha';
import { TechDocsAddonOptions } from '@backstage/plugin-techdocs-react';
// @alpha (undocumented)
const _default: FrontendPlugin<
@@ -151,8 +152,6 @@ const _default: FrontendPlugin<
params: SearchResultListItemBlueprintParams;
}>;
'page:techdocs/reader': ExtensionDefinition<{
kind: 'page';
name: 'reader';
config: {
path: string | undefined;
};
@@ -173,7 +172,21 @@ const _default: FrontendPlugin<
optional: true;
}
>;
inputs: {};
inputs: {
addons: ExtensionInput<
ConfigurableExtensionDataRef<
TechDocsAddonOptions,
'techdocs.addon',
{}
>,
{
singleton: false;
optional: false;
}
>;
};
kind: 'page';
name: 'reader';
params: {
defaultPath: string;
loader: () => Promise<JSX.Element>;
@@ -234,6 +247,17 @@ const _default: FrontendPlugin<
}
>;
inputs: {
addons: ExtensionInput<
ConfigurableExtensionDataRef<
TechDocsAddonOptions,
'techdocs.addon',
{}
>,
{
singleton: false;
optional: false;
}
>;
emptyState: ExtensionInput<
ConfigurableExtensionDataRef<
React_2.JSX.Element,
+20 -1
View File
@@ -56,6 +56,26 @@ export const Router = () => {
);
};
export const TechDocsReaderRouter = (props: PropsWithChildren) => {
const { children } = props;
// Using objects instead of <Route> elements, otherwise "outlet" will be null on sub-pages and add-ons won't render
const element = useRoutes([
{
path: '*',
element: <TechDocsReaderPage />,
children: [
{
path: '*',
element: children,
},
],
},
]);
return element;
};
export const EmbeddedDocsRouter = (
props: PropsWithChildren<{
emptyState?: React.ReactElement;
@@ -90,7 +110,6 @@ export const EmbeddedDocsRouter = (
)
);
}
return element;
};
+47 -17
View File
@@ -38,16 +38,20 @@ import {
} from '@backstage/core-compat-api';
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
import { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha';
import {
techdocsApiRef,
techdocsStorageApiRef,
} from '@backstage/plugin-techdocs-react';
import { AddonBlueprint } from '@backstage/plugin-techdocs-react/alpha';
import { TechDocsClient, TechDocsStorageClient } from './client';
import {
rootCatalogDocsRouteRef,
rootDocsRouteRef,
rootRouteRef,
} from './routes';
import { TechDocsReaderLayout } from './reader';
import { attachTechDocsAddonComponentData } from '@backstage/plugin-techdocs-react/alpha';
import {
TechDocsAddons,
techdocsApiRef,
techdocsStorageApiRef,
} from '@backstage/plugin-techdocs-react';
/** @alpha */
const techDocsStorageApi = ApiBlueprint.make({
@@ -138,15 +142,32 @@ const techDocsPage = PageBlueprint.make({
*
* @alpha
*/
const techDocsReaderPage = PageBlueprint.make({
const techDocsReaderPage = PageBlueprint.makeWithOverrides({
name: 'reader',
params: {
defaultPath: '/docs/:namespace/:kind/:name',
routeRef: convertLegacyRouteRef(rootDocsRouteRef),
loader: () =>
import('./reader/components/TechDocsReaderPage').then(m =>
compatWrapper(<m.TechDocsReaderPage />),
),
inputs: {
addons: createExtensionInput([AddonBlueprint.dataRefs.addon]),
},
factory(originalFactory, { inputs }) {
const addons = inputs.addons.map(output => {
const options = output.get(AddonBlueprint.dataRefs.addon);
const Addon = options.component;
attachTechDocsAddonComponentData(Addon, options);
return <Addon key={options.name} />;
});
return originalFactory({
defaultPath: '/docs/:namespace/:kind/:name',
routeRef: convertLegacyRouteRef(rootDocsRouteRef),
loader: async () =>
await import('./Router').then(({ TechDocsReaderRouter }) => {
return compatWrapper(
<TechDocsReaderRouter>
<TechDocsReaderLayout />
<TechDocsAddons>{addons}</TechDocsAddons>
</TechDocsReaderRouter>,
);
}),
});
},
});
@@ -157,6 +178,7 @@ const techDocsReaderPage = PageBlueprint.make({
*/
const techDocsEntityContent = EntityContentBlueprint.makeWithOverrides({
inputs: {
addons: createExtensionInput([AddonBlueprint.dataRefs.addon]),
emptyState: createExtensionInput(
[coreExtensionData.reactElement.optional()],
{
@@ -172,15 +194,23 @@ const techDocsEntityContent = EntityContentBlueprint.makeWithOverrides({
defaultTitle: 'TechDocs',
routeRef: convertLegacyRouteRef(rootCatalogDocsRouteRef),
loader: () =>
import('./Router').then(({ EmbeddedDocsRouter }) =>
compatWrapper(
import('./Router').then(({ EmbeddedDocsRouter }) => {
const addons = context.inputs.addons.map(output => {
const options = output.get(AddonBlueprint.dataRefs.addon);
const Addon = options.component;
attachTechDocsAddonComponentData(Addon, options);
return <Addon key={options.name} />;
});
return compatWrapper(
<EmbeddedDocsRouter
emptyState={context.inputs.emptyState?.get(
coreExtensionData.reactElement,
)}
/>,
),
),
>
<TechDocsAddons>{addons}</TechDocsAddons>
</EmbeddedDocsRouter>,
);
}),
},
context,
);
+2
View File
@@ -8113,6 +8113,7 @@ __metadata:
"@backstage/cli": "workspace:^"
"@backstage/core-components": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/frontend-plugin-api": "workspace:^"
"@backstage/integration": "workspace:^"
"@backstage/integration-react": "workspace:^"
"@backstage/plugin-techdocs-addons-test-utils": "workspace:^"
@@ -8195,6 +8196,7 @@ __metadata:
"@backstage/config": "workspace:^"
"@backstage/core-components": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/frontend-plugin-api": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/theme": "workspace:^"
"@backstage/version-bridge": "workspace:^"