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"