diff --git a/.changeset/fuzzy-suits-sit.md b/.changeset/fuzzy-suits-sit.md new file mode 100644 index 0000000000..b62f69f8d5 --- /dev/null +++ b/.changeset/fuzzy-suits-sit.md @@ -0,0 +1,16 @@ +--- +'@backstage/plugin-techdocs': minor +--- + +Add two config values to the `page:techdocs/reader` extension that configure default layout, `withoutSearch` and `withoutHeader`. Default are unchanged to `false`. + +E.g. to disable the search and header on the Techdocs Reader Page: + +```yaml +app: + extensions: + - page:techdocs/reader: + config: + withoutSearch: true + withoutHeader: true +``` 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..09553edc8a 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: + withoutSearch: true 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 a4db6d672a..6dcda2a2fb 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -32,14 +32,15 @@ }, "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-app-react": "workspace:^", "@backstage/plugin-catalog": "workspace:^", "@backstage/plugin-techdocs": "workspace:^", "@backstage/plugin-techdocs-react": "workspace:^", @@ -48,7 +49,6 @@ "@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.30.2", diff --git a/packages/techdocs-cli-embedded-app/src/App.test.tsx b/packages/techdocs-cli-embedded-app/src/App.test.tsx index ed479e7e75..fc6d25863c 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: { + withoutSearch: true, + }, + }, + }, + ], + }, 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..229db3142d 100644 --- a/packages/techdocs-cli-embedded-app/src/App.tsx +++ b/packages/techdocs-cli-embedded-app/src/App.tsx @@ -14,85 +14,43 @@ * 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 { appApis, techdocsPluginApis } from './apis'; 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/Sidebar'; +import { + techDocsThemeToggleAddonModule, + techdocsLiveReloadAddonModule, +} from './addons'; + +const appPlugin = createFrontendModule({ + pluginId: 'app', + extensions: [...appApis, SidebarContent], }); -const AppProvider = app.getProvider(); -const AppRouter = app.getRouter(); +const app = createApp({ + features: [ + appPlugin, + techdocsPlugin.withOverrides({ + extensions: [...techdocsPluginApis], + }), + 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..d6f7a2df41 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,53 @@ 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 techdocsPluginApis = [ + 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, + }), + }), + }), +]; + +export const appApis = [ + ApiBlueprint.make({ + name: 'scm-integrations', + params: defineParams => + defineParams({ + api: scmIntegrationsApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi), }), }), - createApiFactory({ - 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/LogoFull.tsx b/packages/techdocs-cli-embedded-app/src/components/Sidebar/LogoFull.tsx similarity index 100% rename from packages/techdocs-cli-embedded-app/src/components/Root/LogoFull.tsx rename to packages/techdocs-cli-embedded-app/src/components/Sidebar/LogoFull.tsx diff --git a/packages/techdocs-cli-embedded-app/src/components/Root/LogoIcon.tsx b/packages/techdocs-cli-embedded-app/src/components/Sidebar/LogoIcon.tsx similarity index 100% rename from packages/techdocs-cli-embedded-app/src/components/Root/LogoIcon.tsx rename to packages/techdocs-cli-embedded-app/src/components/Sidebar/LogoIcon.tsx diff --git a/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx b/packages/techdocs-cli-embedded-app/src/components/Sidebar/Sidebar.tsx similarity index 89% rename from packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx rename to packages/techdocs-cli-embedded-app/src/components/Sidebar/Sidebar.tsx index 6a00fc78cf..fed1f0f36d 100644 --- a/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx +++ b/packages/techdocs-cli-embedded-app/src/components/Sidebar/Sidebar.tsx @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { PropsWithChildren } from 'react'; - import { makeStyles } from '@material-ui/core/styles'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; import LogoFull from './LogoFull'; @@ -30,6 +27,7 @@ import { useSidebarOpenState, Link, } from '@backstage/core-components'; +import { NavContentBlueprint } from '@backstage/plugin-app-react'; const useSidebarLogoStyles = makeStyles({ root: { @@ -63,7 +61,7 @@ const SidebarLogo = () => { ); }; -export const Root = ({ children }: PropsWithChildren<{}>) => ( +export const SidebarNav = (): React.ReactElement => ( @@ -76,6 +74,11 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( /> {/* End global nav */} - {children} ); + +export const SidebarContent = NavContentBlueprint.make({ + params: { + component: () => , + }, +}); diff --git a/packages/techdocs-cli-embedded-app/src/components/Root/index.ts b/packages/techdocs-cli-embedded-app/src/components/Sidebar/index.ts similarity index 93% rename from packages/techdocs-cli-embedded-app/src/components/Root/index.ts rename to packages/techdocs-cli-embedded-app/src/components/Sidebar/index.ts index dff706f08f..485e09a70c 100644 --- a/packages/techdocs-cli-embedded-app/src/components/Root/index.ts +++ b/packages/techdocs-cli-embedded-app/src/components/Sidebar/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { Root } from './Root'; +export { SidebarContent } from './Sidebar'; 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 d839a03f01..3092a010d4 100644 --- a/plugins/techdocs/report-alpha.api.md +++ b/plugins/techdocs/report-alpha.api.md @@ -308,9 +308,13 @@ const _default: OverridableFrontendPlugin< }>; 'page:techdocs/reader': OverridableExtensionDefinition<{ config: { + withoutSearch: boolean; + withoutHeader: boolean; path: string | undefined; }; configInput: { + withoutSearch?: boolean | undefined; + withoutHeader?: boolean | undefined; path?: string | undefined; }; output: diff --git a/plugins/techdocs/src/alpha/index.tsx b/plugins/techdocs/src/alpha/index.tsx index b93d5dfb0d..9e63004046 100644 --- a/plugins/techdocs/src/alpha/index.tsx +++ b/plugins/techdocs/src/alpha/index.tsx @@ -156,7 +156,13 @@ const techDocsReaderPage = PageBlueprint.makeWithOverrides({ inputs: { addons: createExtensionInput([AddonBlueprint.dataRefs.addon]), }, - factory(originalFactory, { apis, inputs }) { + config: { + schema: { + withoutSearch: z => z.boolean().default(false), + withoutHeader: z => z.boolean().default(false), + }, + }, + factory(originalFactory, { apis, inputs, config }) { const addonsApi = apis.get(techdocsAddonsApiRef); return originalFactory({ @@ -182,7 +188,10 @@ const techDocsReaderPage = PageBlueprint.makeWithOverrides({ return import('../Router').then(({ TechDocsReaderRouter }) => ( - + {addons} )); diff --git a/yarn.lock b/yarn.lock index 70e1f861a7..6393dca68e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -48291,14 +48291,15 @@ __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-app-react": "workspace:^" "@backstage/plugin-catalog": "workspace:^" "@backstage/plugin-techdocs": "workspace:^" "@backstage/plugin-techdocs-react": "workspace:^" @@ -48314,7 +48315,6 @@ __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.30.2"