feat(home): add new frontend system support
Migrates home plugin to support the new frontend system architecture by introducing extension blueprints for composable homepage functionality. Key changes: - Add CustomHomepageWidgetBlueprint for creating installable homepage widgets - Add CustomHomepageBlueprint for composing pages from widget extensions - Introduce titleExtensionDataRef for NFS title handling This attempts to bring the home plugin up to par with other core plugins that have migrated to the new frontend system Signed-off-by: Adam Kunicki <kunickiaj@gmail.com>
This commit is contained in:
+202
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
* 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.
|
||||
@@ -13,14 +13,205 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { homePlugin, HomepageCompositionRoot } from '../src/plugin';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin(homePlugin)
|
||||
.addPage({
|
||||
element: <HomepageCompositionRoot />,
|
||||
title: 'Root Page',
|
||||
path: '/',
|
||||
})
|
||||
.render();
|
||||
import { Content, Header, Page } from '@backstage/core-components';
|
||||
import { createApp } from '@backstage/frontend-defaults';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils';
|
||||
import catalogPlugin from '@backstage/plugin-catalog/alpha';
|
||||
import HomeIcon from '@material-ui/icons/Home';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import {
|
||||
ApiBlueprint,
|
||||
createFrontendModule,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { HomepageWidgetBlueprint } from '@backstage/plugin-home-react/alpha';
|
||||
import { HeaderWorldClock, WelcomeTitle, type ClockConfig } from '../src';
|
||||
import homePlugin, {
|
||||
HomepageBlueprint,
|
||||
type HomepageGridProps,
|
||||
} from '../src/alpha';
|
||||
|
||||
const clockConfigs: ClockConfig[] = [
|
||||
{ label: 'NYC', timeZone: 'America/New_York' },
|
||||
{ label: 'UTC', timeZone: 'UTC' },
|
||||
{ label: 'STO', timeZone: 'Europe/Stockholm' },
|
||||
{ label: 'TYO', timeZone: 'Asia/Tokyo' },
|
||||
];
|
||||
|
||||
const timeFormat: Intl.DateTimeFormatOptions = {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false,
|
||||
};
|
||||
|
||||
const defaultGridConfig: NonNullable<HomepageGridProps['config']> = [
|
||||
{
|
||||
component: 'HomePageToolkit',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 12,
|
||||
height: 4,
|
||||
movable: false,
|
||||
resizable: false,
|
||||
},
|
||||
{
|
||||
component: 'HomePageStarredEntities',
|
||||
x: 0,
|
||||
y: 4,
|
||||
width: 6,
|
||||
height: 5,
|
||||
},
|
||||
{
|
||||
component: 'HomePageRandomJoke',
|
||||
x: 6,
|
||||
y: 4,
|
||||
width: 6,
|
||||
height: 5,
|
||||
},
|
||||
];
|
||||
|
||||
const homePage = HomepageBlueprint.make({
|
||||
params: {
|
||||
title: 'Home',
|
||||
grid: {
|
||||
config: defaultGridConfig,
|
||||
},
|
||||
render: ({ grid }) => (
|
||||
<Page themeId="home">
|
||||
<Header title={<WelcomeTitle />} pageTitleOverride="Home">
|
||||
<HeaderWorldClock
|
||||
clockConfigs={clockConfigs}
|
||||
customTimeFormat={timeFormat}
|
||||
/>
|
||||
</Header>
|
||||
<Content>{grid}</Content>
|
||||
</Page>
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
const homePageToolkitWidget = HomepageWidgetBlueprint.make({
|
||||
name: 'home-toolkit',
|
||||
params: {
|
||||
name: 'HomePageToolkit',
|
||||
title: 'Toolkit',
|
||||
components: () =>
|
||||
import('../src/homePageComponents/Toolkit').then(m => ({
|
||||
Content: m.Content,
|
||||
ContextProvider: m.ContextProvider,
|
||||
})),
|
||||
componentProps: {
|
||||
tools: [
|
||||
{
|
||||
url: 'https://backstage.io',
|
||||
label: 'Backstage Homepage',
|
||||
icon: <HomeIcon />,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const homePageStarredEntitiesWidget = HomepageWidgetBlueprint.make({
|
||||
name: 'home-starred-entities',
|
||||
params: {
|
||||
name: 'HomePageStarredEntities',
|
||||
title: 'Your Starred Entities',
|
||||
components: () =>
|
||||
import('../src/homePageComponents/StarredEntities').then(m => ({
|
||||
Content: m.Content,
|
||||
})),
|
||||
},
|
||||
});
|
||||
|
||||
const homePageRandomJokeWidget = HomepageWidgetBlueprint.make({
|
||||
name: 'home-random-joke',
|
||||
params: {
|
||||
name: 'HomePageRandomJoke',
|
||||
title: 'Random Joke',
|
||||
description: 'Shows a random programming joke',
|
||||
components: () =>
|
||||
import('../src/homePageComponents/RandomJoke').then(m => ({
|
||||
Content: m.Content,
|
||||
Settings: m.Settings,
|
||||
Actions: m.Actions,
|
||||
ContextProvider: m.ContextProvider,
|
||||
})),
|
||||
layout: {
|
||||
height: { minRows: 4 },
|
||||
width: { minColumns: 3 },
|
||||
},
|
||||
settings: {
|
||||
schema: {
|
||||
title: 'Random Joke settings',
|
||||
type: 'object',
|
||||
properties: {
|
||||
defaultCategory: {
|
||||
title: 'Category',
|
||||
type: 'string',
|
||||
enum: ['any', 'programming', 'dad'],
|
||||
default: 'any',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const homeDevModule = createFrontendModule({
|
||||
pluginId: 'home',
|
||||
extensions: [
|
||||
homePage,
|
||||
homePageToolkitWidget,
|
||||
homePageStarredEntitiesWidget,
|
||||
homePageRandomJokeWidget,
|
||||
],
|
||||
});
|
||||
|
||||
const entities = [
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'example',
|
||||
annotations: {
|
||||
'backstage.io/managed-by-location': 'file:/path/to/catalog-info.yaml',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
type: 'service',
|
||||
lifecycle: 'production',
|
||||
owner: 'guest',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const catalogApi = catalogApiMock({ entities });
|
||||
|
||||
const catalogPluginOverrides = createFrontendModule({
|
||||
pluginId: 'catalog',
|
||||
extensions: [
|
||||
ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
api: catalogApiRef,
|
||||
deps: {},
|
||||
factory: () => catalogApi,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const app = createApp({
|
||||
features: [
|
||||
catalogPlugin,
|
||||
catalogPluginOverrides,
|
||||
homePlugin, // Load the home plugin
|
||||
homeDevModule, // Load the widgets and homepage content
|
||||
],
|
||||
});
|
||||
|
||||
const root = app.createRoot();
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(root);
|
||||
|
||||
Reference in New Issue
Block a user