Align NFS headers with existing BUI page patterns

Update migrated NFS pages to use the existing HeaderPage contract instead of extending Backstage UI, and move DevTools to real subpages with the legacy DevTools content blueprint removed.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
This commit is contained in:
Patrik Oldsberg
2026-03-07 20:53:52 +01:00
parent aa29b508d1
commit f4a1edd2b0
18 changed files with 245 additions and 269 deletions
+71 -31
View File
@@ -21,13 +21,19 @@ import {
ApiBlueprint,
PageBlueprint,
NavItemBlueprint,
createExtensionInput,
coreExtensionData,
SubPageBlueprint,
} from '@backstage/frontend-plugin-api';
import { devToolsApiRef, DevToolsClient } from '../api';
import BuildIcon from '@material-ui/icons/Build';
import { Content } from '@backstage/core-components';
import { rootRouteRef } from '../routes';
import {
devToolsConfigReadPermission,
devToolsInfoReadPermission,
} from '@backstage/plugin-devtools-common';
import { devToolsTaskSchedulerReadPermission } from '@backstage/plugin-devtools-common/alpha';
import { RequirePermission } from '@backstage/plugin-permission-react';
/** @alpha */
export const devToolsApi = ApiBlueprint.make({
@@ -44,35 +50,62 @@ export const devToolsApi = ApiBlueprint.make({
});
/** @alpha */
export const devToolsPage = PageBlueprint.makeWithOverrides({
inputs: {
contents: createExtensionInput(
[
coreExtensionData.reactElement,
coreExtensionData.routePath,
coreExtensionData.routeRef.optional(),
coreExtensionData.title,
],
{
optional: true,
},
),
export const devToolsPage = PageBlueprint.make({
params: {
path: '/devtools',
routeRef: rootRouteRef,
title: 'DevTools',
},
factory(originalFactory, { inputs }) {
return originalFactory({
path: '/devtools',
routeRef: rootRouteRef,
loader: () => {
const contents = inputs.contents.map(content => ({
path: content.get(coreExtensionData.routePath),
title: content.get(coreExtensionData.title),
children: content.get(coreExtensionData.reactElement),
}));
return import('../components/DevToolsPage/DevToolsPage').then(m => (
<m.NfsDevToolsPage contents={contents} />
));
},
});
});
/** @alpha */
export const devToolsInfoPage = SubPageBlueprint.make({
name: 'info',
params: {
path: 'info',
title: 'Info',
loader: () =>
import('../components/Content').then(m => (
<Content>
<RequirePermission permission={devToolsInfoReadPermission}>
<m.InfoContent />
</RequirePermission>
</Content>
)),
},
});
/** @alpha */
export const devToolsConfigPage = SubPageBlueprint.make({
name: 'config',
params: {
path: 'config',
title: 'Config',
loader: () =>
import('../components/Content').then(m => (
<Content>
<RequirePermission permission={devToolsConfigReadPermission}>
<m.ConfigContent />
</RequirePermission>
</Content>
)),
},
});
/** @alpha */
export const devToolsScheduledTasksPage = SubPageBlueprint.make({
name: 'scheduled-tasks',
params: {
path: 'scheduled-tasks',
title: 'Scheduled Tasks',
loader: () =>
import('../components/Content').then(m => (
<Content>
<RequirePermission permission={devToolsTaskSchedulerReadPermission}>
<m.ScheduledTasksContent />
</RequirePermission>
</Content>
)),
},
});
@@ -94,5 +127,12 @@ export default createFrontendPlugin({
routes: {
root: rootRouteRef,
},
extensions: [devToolsApi, devToolsPage, devToolsNavItem],
extensions: [
devToolsApi,
devToolsPage,
devToolsInfoPage,
devToolsConfigPage,
devToolsScheduledTasksPage,
devToolsNavItem,
],
});
@@ -21,10 +21,7 @@ import {
import { ConfigContent } from '../Content';
import { devToolsTaskSchedulerReadPermission } from '@backstage/plugin-devtools-common/alpha';
import {
DevToolsLayout,
NfsDevToolsLayout,
} from '../DevToolsLayout/DevToolsLayout';
import { DevToolsLayout } from '../DevToolsLayout/DevToolsLayout';
import { InfoContent } from '../Content';
import { RequirePermission } from '@backstage/plugin-permission-react';
import { ScheduledTasksContent } from '../Content/ScheduledTasksContent';
@@ -59,32 +56,3 @@ export const DefaultDevToolsPage = ({ contents }: DevToolsPageProps) => (
))}
</DevToolsLayout>
);
export const NfsDefaultDevToolsPage = ({ contents }: DevToolsPageProps) => (
<NfsDevToolsLayout>
<DevToolsLayout.Route path="info" title="Info">
<RequirePermission permission={devToolsInfoReadPermission}>
<InfoContent />
</RequirePermission>
</DevToolsLayout.Route>
<DevToolsLayout.Route path="config" title="Config">
<RequirePermission permission={devToolsConfigReadPermission}>
<ConfigContent />
</RequirePermission>
</DevToolsLayout.Route>
<DevToolsLayout.Route path="scheduled-tasks" title="Scheduled Tasks">
<RequirePermission permission={devToolsTaskSchedulerReadPermission}>
<ScheduledTasksContent />
</RequirePermission>
</DevToolsLayout.Route>
{contents?.map((content, index) => (
<DevToolsLayout.Route
key={`extension-${index}`}
path={content.path}
title={content.title}
>
{content.children}
</DevToolsLayout.Route>
))}
</NfsDevToolsLayout>
);
@@ -15,7 +15,6 @@
*/
import { Header, Page, RoutedTabs } from '@backstage/core-components';
import { HeaderPage } from '@backstage/ui';
import {
attachComponentData,
useElementFilter,
@@ -83,28 +82,4 @@ export const DevToolsLayout = ({
);
};
export const NfsDevToolsLayout = ({
children,
title,
subtitle,
}: DevToolsLayoutProps) => {
const routes = useElementFilter(children, elements =>
elements
.selectByComponentData({
key: dataKey,
withStrictError:
'Child of DevToolsLayout must be an DevToolsLayout.Route',
})
.getElements<SubRoute>()
.map(child => child.props),
);
return (
<>
<HeaderPage title={title ?? 'Backstage DevTools'} subtitle={subtitle} />
<RoutedTabs routes={routes} />
</>
);
};
DevToolsLayout.Route = Route;
@@ -16,7 +16,6 @@
import { useOutlet } from 'react-router-dom';
import { DefaultDevToolsPage } from '../DefaultDevToolsPage';
import { NfsDefaultDevToolsPage } from '../DefaultDevToolsPage/DefaultDevToolsPage';
import { ReactElement } from 'react';
/**
@@ -40,9 +39,3 @@ export const DevToolsPage = ({ contents }: DevToolsPageProps) => {
return <>{outlet || <DefaultDevToolsPage contents={contents} />}</>;
};
export const NfsDevToolsPage = ({ contents }: DevToolsPageProps) => {
const outlet = useOutlet();
return <>{outlet || <NfsDefaultDevToolsPage contents={contents} />}</>;
};