From 27798df0a3ae92a058c273fe63df81688ee3d7d4 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sun, 11 Jan 2026 17:05:51 +0100 Subject: [PATCH] Migrate techdocs-cli-embedded-app to NFS Signed-off-by: Gabriel Dugny --- .changeset/fuzzy-suits-sit.md | 16 +++ .changeset/old-paths-jump.md | 5 + .../techdocs-cli-embedded-app/app-config.yaml | 5 + .../techdocs-cli-embedded-app/knip-report.md | 18 +-- .../techdocs-cli-embedded-app/package.json | 8 +- .../src/App.test.tsx | 20 +++- .../techdocs-cli-embedded-app/src/App.tsx | 104 +++++------------- .../techdocs-cli-embedded-app/src/addons.tsx | 48 ++++++++ .../techdocs-cli-embedded-app/src/apis.ts | 87 ++++++++------- .../LiveReload}/LiveReloadAddon.test.tsx | 0 .../LiveReload}/LiveReloadAddon.tsx | 0 .../src/components/Root/Root.tsx | 6 + .../components/TechDocsPage/TechDocsPage.tsx | 4 +- .../techdocs-cli-embedded-app/src/index.tsx | 2 +- .../techdocs-cli-embedded-app/src/plugins.ts | 17 --- plugins/techdocs/report-alpha.api.md | 4 + plugins/techdocs/src/alpha/index.tsx | 13 ++- yarn.lock | 6 +- 18 files changed, 204 insertions(+), 159 deletions(-) create mode 100644 .changeset/fuzzy-suits-sit.md create mode 100644 .changeset/old-paths-jump.md create mode 100644 packages/techdocs-cli-embedded-app/src/addons.tsx rename packages/techdocs-cli-embedded-app/src/{ => components/LiveReload}/LiveReloadAddon.test.tsx (100%) rename packages/techdocs-cli-embedded-app/src/{ => components/LiveReload}/LiveReloadAddon.tsx (100%) delete mode 100644 packages/techdocs-cli-embedded-app/src/plugins.ts diff --git a/.changeset/fuzzy-suits-sit.md b/.changeset/fuzzy-suits-sit.md new file mode 100644 index 0000000000..a5c8f6b7a0 --- /dev/null +++ b/.changeset/fuzzy-suits-sit.md @@ -0,0 +1,16 @@ +--- +'@backstage/plugin-techdocs': minor +--- + +Add 2 config elements to extension "page:techdocs/reader" to configure default layout `withSearch` and `withHeader`. Default are unchanged to `true`. + +E.g. to disable the search and header on the Techdocs Reader Page: + +```yaml +app: + extensions: + - page:techdocs/reader: + config: + withSearch: false + withHeader: false +``` diff --git a/.changeset/old-paths-jump.md b/.changeset/old-paths-jump.md new file mode 100644 index 0000000000..7a5d9aef43 --- /dev/null +++ b/.changeset/old-paths-jump.md @@ -0,0 +1,5 @@ +--- +'@techdocs/cli': patch +--- + +Migrate the Techdocs CLI embedded app to the New Frontend System (NFS) diff --git a/packages/techdocs-cli-embedded-app/app-config.yaml b/packages/techdocs-cli-embedded-app/app-config.yaml index c8dfa1683c..be60d56756 100644 --- a/packages/techdocs-cli-embedded-app/app-config.yaml +++ b/packages/techdocs-cli-embedded-app/app-config.yaml @@ -1,6 +1,11 @@ app: title: Techdocs Preview App baseUrl: http://localhost:3000 + extensions: + - sign-in-page:app: false + - page:techdocs/reader: + config: + withSearch: false backend: baseUrl: http://localhost:3000 diff --git a/packages/techdocs-cli-embedded-app/knip-report.md b/packages/techdocs-cli-embedded-app/knip-report.md index 3711ac996d..1b6bfe5aaa 100644 --- a/packages/techdocs-cli-embedded-app/knip-report.md +++ b/packages/techdocs-cli-embedded-app/knip-report.md @@ -1,17 +1,9 @@ # Knip report -## Unused dependencies (2) +## Unused devDependencies (2) -| Name | Location | Severity | -| :-------- | :----------- | :------- | -| react-use | packages/techdocs-cli-embedded-app/package.json | error | -| history | packages/techdocs-cli-embedded-app/package.json | error | - -## Unused devDependencies (3) - -| Name | Location | Severity | -| :-------------------------- | :----------- | :------- | -| @testing-library/user-event | packages/techdocs-cli-embedded-app/package.json | error | -| @testing-library/dom | packages/techdocs-cli-embedded-app/package.json | error | -| cross-env | packages/techdocs-cli-embedded-app/package.json | error | +| Name | Location | Severity | +| :-------------------------- | :---------------- | :------- | +| @testing-library/user-event | package.json:61:6 | error | +| cross-env | package.json:64:6 | error | diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 99aa3bb222..62503eed6d 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -32,13 +32,13 @@ }, "prettier": "@backstage/cli/config/prettier", "dependencies": { - "@backstage/app-defaults": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/config": "workspace:^", "@backstage/core-app-api": "workspace:^", "@backstage/core-components": "workspace:^", - "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-defaults": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@backstage/integration-react": "workspace:^", "@backstage/plugin-catalog": "workspace:^", "@backstage/plugin-techdocs": "workspace:^", @@ -48,11 +48,9 @@ "@backstage/ui": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "history": "^5.0.0", "react": "^18.0.2", "react-dom": "^18.0.2", - "react-router-dom": "^6.3.0", - "react-use": "^17.2.4" + "react-router-dom": "^6.3.0" }, "devDependencies": { "@backstage/cli": "workspace:^", diff --git a/packages/techdocs-cli-embedded-app/src/App.test.tsx b/packages/techdocs-cli-embedded-app/src/App.test.tsx index ed479e7e75..4a7eaef911 100644 --- a/packages/techdocs-cli-embedded-app/src/App.test.tsx +++ b/packages/techdocs-cli-embedded-app/src/App.test.tsx @@ -15,13 +15,27 @@ */ import { renderWithEffects } from '@backstage/test-utils'; -import App from './App'; +import app from './App'; jest.mock('./config', () => ({ configLoader: async () => [ { data: { - app: { title: 'Test' }, + app: { + title: 'Test', + extensions: [ + { + 'sign-in-page:app': false, + }, + { + 'page:techdocs/reader': { + config: { + withSearch: false, + }, + }, + }, + ], + }, backend: { baseUrl: 'http://localhost:7007' }, techdocs: { storageUrl: 'http://localhost:7007/api/techdocs/static/docs', @@ -34,7 +48,7 @@ jest.mock('./config', () => ({ describe('App', () => { it('should render', async () => { - const rendered = await renderWithEffects(); + const rendered = await renderWithEffects(app); expect(rendered.getByText('Docs Preview')).toBeInTheDocument(); }); }); diff --git a/packages/techdocs-cli-embedded-app/src/App.tsx b/packages/techdocs-cli-embedded-app/src/App.tsx index 13d5b925f0..0c8fdea781 100644 --- a/packages/techdocs-cli-embedded-app/src/App.tsx +++ b/packages/techdocs-cli-embedded-app/src/App.tsx @@ -14,85 +14,41 @@ * limitations under the License. */ -import { Navigate, Route } from 'react-router-dom'; +import techdocsPlugin from '@backstage/plugin-techdocs/alpha'; -import { - DefaultTechDocsHome, - TechDocsIndexPage, - TechDocsReaderPage, - techdocsPlugin, -} from '@backstage/plugin-techdocs'; -import { - createTechDocsAddonExtension, - TechDocsAddons, - TechDocsAddonLocations, -} from '@backstage/plugin-techdocs-react'; -import { createApp } from '@backstage/app-defaults'; -import { FlatRoutes } from '@backstage/core-app-api'; -import { CatalogEntityPage } from '@backstage/plugin-catalog'; +import { createApp } from '@backstage/frontend-defaults'; +import { ConfigReader } from '@backstage/core-app-api'; +import catalogPlugin from '@backstage/plugin-catalog/alpha'; import { apis } from './apis'; -import * as plugins from './plugins'; import { configLoader } from './config'; -import { Root } from './components/Root'; -import { techDocsPage, TechDocsThemeToggle } from './components/TechDocsPage'; -import { TechDocsLiveReload } from './LiveReloadAddon'; -const app = createApp({ - apis, - configLoader, - plugins: Object.values(plugins), +import { createFrontendModule } from '@backstage/frontend-plugin-api'; +import { SidebarContent } from './components/Root/Root'; +import { + techDocsThemeToggleAddonModule, + techdocsLiveReloadAddonModule, +} from './addons'; + +const appPlugin = createFrontendModule({ + pluginId: 'app', + extensions: [...apis, SidebarContent], }); -const AppProvider = app.getProvider(); -const AppRouter = app.getRouter(); +const app = createApp({ + features: [ + appPlugin, + techdocsPlugin, + catalogPlugin, + techDocsThemeToggleAddonModule, + techdocsLiveReloadAddonModule, + ], + advanced: { + async configLoader() { + const appConfigs = await configLoader(); + return { config: ConfigReader.fromConfigs(appConfigs) }; + }, + }, +}); -const ThemeToggleAddon = techdocsPlugin.provide( - createTechDocsAddonExtension({ - name: 'ThemeToggleAddon', - component: TechDocsThemeToggle, - location: TechDocsAddonLocations.Header, - }), -); - -const LiveReloadAddon = techdocsPlugin.provide( - createTechDocsAddonExtension({ - name: 'LiveReloadAddon', - component: TechDocsLiveReload, - location: TechDocsAddonLocations.Content, - }), -); - -const routes = ( - - - {/* we need this route as TechDocs header links relies on it */} - } - /> - }> - - - } - > - {techDocsPage} - - - - - - -); - -const App = () => ( - - - {routes} - - -); - -export default App; +export default app.createRoot(); diff --git a/packages/techdocs-cli-embedded-app/src/addons.tsx b/packages/techdocs-cli-embedded-app/src/addons.tsx new file mode 100644 index 0000000000..26ead00959 --- /dev/null +++ b/packages/techdocs-cli-embedded-app/src/addons.tsx @@ -0,0 +1,48 @@ +/* + * Copyright 2026 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 { AddonBlueprint } from '@backstage/plugin-techdocs-react/alpha'; +import { TechDocsAddonLocations } from '@backstage/plugin-techdocs-react'; +import { createFrontendModule } from '@backstage/frontend-plugin-api'; +import { TechDocsThemeToggle } from './components/TechDocsPage'; +import { TechDocsLiveReload } from './components/LiveReload/LiveReloadAddon'; + +const techDocsThemeToggleAddonExtension = AddonBlueprint.make({ + name: 'techdocs-theme-toggle-addon', + params: { + name: 'ThemeToggleAddon', + component: TechDocsThemeToggle, + location: TechDocsAddonLocations.Header, + }, +}); + +export const techDocsThemeToggleAddonModule = createFrontendModule({ + pluginId: 'techdocs', + extensions: [techDocsThemeToggleAddonExtension], +}); + +const techdocsLiveReloadAddonExtension = AddonBlueprint.make({ + name: 'techdocs-live-reload-addon', + params: { + name: 'LiveReloadAddon', + component: TechDocsLiveReload, + location: TechDocsAddonLocations.Content, + }, +}); + +export const techdocsLiveReloadAddonModule = createFrontendModule({ + pluginId: 'techdocs', + extensions: [techdocsLiveReloadAddonExtension], +}); diff --git a/packages/techdocs-cli-embedded-app/src/apis.ts b/packages/techdocs-cli-embedded-app/src/apis.ts index 5bb19a563d..1b6387938b 100644 --- a/packages/techdocs-cli-embedded-app/src/apis.ts +++ b/packages/techdocs-cli-embedded-app/src/apis.ts @@ -13,6 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { + ApiBlueprint, + configApiRef, + DiscoveryApi, + discoveryApiRef, + IdentityApi, + identityApiRef, +} from '@backstage/frontend-plugin-api'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; @@ -20,15 +28,6 @@ import { scmIntegrationsApiRef, ScmIntegrationsApi, } from '@backstage/integration-react'; -import { - AnyApiFactory, - configApiRef, - createApiFactory, - DiscoveryApi, - discoveryApiRef, - IdentityApi, - identityApiRef, -} from '@backstage/core-plugin-api'; import { SyncResult, TechDocsApi, @@ -158,38 +157,50 @@ class TechDocsDevApi implements TechDocsApi { } } -export const apis: AnyApiFactory[] = [ - createApiFactory({ - api: techdocsStorageApiRef, - deps: { - configApi: configApiRef, - discoveryApi: discoveryApiRef, - identityApi: identityApiRef, - }, - factory: ({ configApi, discoveryApi, identityApi }) => - new TechDocsDevStorageApi({ - configApi, - discoveryApi, - identityApi, +export const apis = [ + ApiBlueprint.make({ + name: 'techdocs-dev-storage', + params: defineParams => + defineParams({ + api: techdocsStorageApiRef, + deps: { + configApi: configApiRef, + discoveryApi: discoveryApiRef, + identityApi: identityApiRef, + }, + factory: ({ configApi, discoveryApi, identityApi }) => + new TechDocsDevStorageApi({ + configApi, + discoveryApi, + identityApi, + }), }), }), - createApiFactory({ - api: techdocsApiRef, - deps: { - configApi: configApiRef, - discoveryApi: discoveryApiRef, - identityApi: identityApiRef, - }, - factory: ({ configApi, discoveryApi, identityApi }) => - new TechDocsDevApi({ - configApi, - discoveryApi, - identityApi, + ApiBlueprint.make({ + name: 'techdocs-dev', + params: defineParams => + defineParams({ + api: techdocsApiRef, + deps: { + configApi: configApiRef, + discoveryApi: discoveryApiRef, + identityApi: identityApiRef, + }, + factory: ({ configApi, discoveryApi, identityApi }) => + new TechDocsDevApi({ + configApi, + discoveryApi, + identityApi, + }), }), }), - createApiFactory({ - api: scmIntegrationsApiRef, - deps: { configApi: configApiRef }, - factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi), + ApiBlueprint.make({ + name: 'scm-integrations', + params: defineParams => + defineParams({ + api: scmIntegrationsApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi), + }), }), ]; diff --git a/packages/techdocs-cli-embedded-app/src/LiveReloadAddon.test.tsx b/packages/techdocs-cli-embedded-app/src/components/LiveReload/LiveReloadAddon.test.tsx similarity index 100% rename from packages/techdocs-cli-embedded-app/src/LiveReloadAddon.test.tsx rename to packages/techdocs-cli-embedded-app/src/components/LiveReload/LiveReloadAddon.test.tsx diff --git a/packages/techdocs-cli-embedded-app/src/LiveReloadAddon.tsx b/packages/techdocs-cli-embedded-app/src/components/LiveReload/LiveReloadAddon.tsx similarity index 100% rename from packages/techdocs-cli-embedded-app/src/LiveReloadAddon.tsx rename to packages/techdocs-cli-embedded-app/src/components/LiveReload/LiveReloadAddon.tsx diff --git a/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx b/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx index 6a00fc78cf..c8dbe8634b 100644 --- a/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx +++ b/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx @@ -30,6 +30,7 @@ import { useSidebarOpenState, Link, } from '@backstage/core-components'; +import { NavContentBlueprint } from '@backstage/frontend-plugin-api'; const useSidebarLogoStyles = makeStyles({ root: { @@ -79,3 +80,8 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( {children} ); +export const SidebarContent = NavContentBlueprint.make({ + params: { + component: ({}) => , + }, +}); diff --git a/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx b/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx index dfe728da86..eae55d3424 100644 --- a/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx +++ b/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx @@ -25,7 +25,7 @@ import IconButton from '@material-ui/core/IconButton'; import LightIcon from '@material-ui/icons/Brightness7'; import DarkIcon from '@material-ui/icons/Brightness4'; -import { appThemeApiRef, useApi } from '@backstage/core-plugin-api'; +import { appThemeApiRef, useApi } from '@backstage/frontend-plugin-api'; import { TechDocsReaderPage, @@ -93,7 +93,7 @@ export const TechDocsThemeToggle = () => { ); }; -const DefaultTechDocsPage = () => { +export const DefaultTechDocsPage = () => { return ( diff --git a/packages/techdocs-cli-embedded-app/src/index.tsx b/packages/techdocs-cli-embedded-app/src/index.tsx index f50db079b0..84d77cc7e1 100644 --- a/packages/techdocs-cli-embedded-app/src/index.tsx +++ b/packages/techdocs-cli-embedded-app/src/index.tsx @@ -19,4 +19,4 @@ import ReactDOM from 'react-dom/client'; import '@backstage/ui/css/styles.css'; import App from './App'; -ReactDOM.createRoot(document.getElementById('root')!).render(); +ReactDOM.createRoot(document.getElementById('root')!).render(App); diff --git a/packages/techdocs-cli-embedded-app/src/plugins.ts b/packages/techdocs-cli-embedded-app/src/plugins.ts deleted file mode 100644 index 42fc16b339..0000000000 --- a/packages/techdocs-cli-embedded-app/src/plugins.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2020 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. - */ - -export { plugin as TechDocsPlugin } from '@backstage/plugin-techdocs'; diff --git a/plugins/techdocs/report-alpha.api.md b/plugins/techdocs/report-alpha.api.md index 96c778b09b..3e6a8945b3 100644 --- a/plugins/techdocs/report-alpha.api.md +++ b/plugins/techdocs/report-alpha.api.md @@ -265,9 +265,13 @@ const _default: OverridableFrontendPlugin< }>; 'page:techdocs/reader': OverridableExtensionDefinition<{ config: { + withSearch: boolean; + withHeader: boolean; path: string | undefined; }; configInput: { + withSearch?: boolean | undefined; + withHeader?: boolean | undefined; path?: string | undefined; }; output: diff --git a/plugins/techdocs/src/alpha/index.tsx b/plugins/techdocs/src/alpha/index.tsx index d3fc94c7a8..b97bc7c97b 100644 --- a/plugins/techdocs/src/alpha/index.tsx +++ b/plugins/techdocs/src/alpha/index.tsx @@ -152,7 +152,13 @@ const techDocsReaderPage = PageBlueprint.makeWithOverrides({ inputs: { addons: createExtensionInput([AddonBlueprint.dataRefs.addon]), }, - factory(originalFactory, { inputs }) { + config: { + schema: { + withSearch: z => z.boolean().default(true), + withHeader: z => z.boolean().default(true), + }, + }, + factory(originalFactory, { inputs, config }) { const addons = inputs.addons.map(output => { const options = output.get(AddonBlueprint.dataRefs.addon); const Addon = options.component; @@ -166,7 +172,10 @@ const techDocsReaderPage = PageBlueprint.makeWithOverrides({ loader: async () => await import('../Router').then(({ TechDocsReaderRouter }) => ( - + {addons} )), diff --git a/yarn.lock b/yarn.lock index b812d13734..67cf817550 100644 --- a/yarn.lock +++ b/yarn.lock @@ -47899,13 +47899,13 @@ __metadata: version: 0.0.0-use.local resolution: "techdocs-cli-embedded-app@workspace:packages/techdocs-cli-embedded-app" dependencies: - "@backstage/app-defaults": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/core-app-api": "workspace:^" "@backstage/core-components": "workspace:^" - "@backstage/core-plugin-api": "workspace:^" + "@backstage/frontend-defaults": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@backstage/integration-react": "workspace:^" "@backstage/plugin-catalog": "workspace:^" "@backstage/plugin-techdocs": "workspace:^" @@ -47922,11 +47922,9 @@ __metadata: "@types/react": "npm:*" "@types/react-dom": "npm:*" cross-env: "npm:^10.0.0" - history: "npm:^5.0.0" react: "npm:^18.0.2" react-dom: "npm:^18.0.2" react-router-dom: "npm:^6.3.0" - react-use: "npm:^17.2.4" languageName: unknown linkType: soft