refactor(home): restructure extension architecture to match catalog pattern

Reorganize the home plugin's new frontend system extensions to follow
the same pattern as the catalog plugin's EntityContentLayoutBlueprint.
Key changes:
- Widgets now attach directly to page:home instead of through an
  intermediate HomepageBlueprint extension
- Add HomePageLayoutBlueprint in home-react for custom layout creation,
  with a built-in default layout as fallback
- Remove compatWrapper from HomepageWidgetBlueprint (consumer
  responsibility, reduced need with NFS support in old system)
- Remove HomepageCompositionRoot usage (old system artifact)
- Delete HomepageBlueprint (replaced by HomePageLayoutBlueprint)
- Update app-next example and dev harness to use new pattern
Signed-off-by: Adam Kunicki <kunickiaj@gmail.com>
This commit is contained in:
Adam Kunicki
2026-02-07 04:50:04 -08:00
parent 06e0a2cb22
commit 03fc07adb7
11 changed files with 289 additions and 265 deletions
+6
View File
@@ -28,8 +28,14 @@ export {
HomepageWidgetBlueprint,
type HomepageWidgetBlueprintParams,
} from './alpha/blueprints/HomepageWidgetBlueprint';
export {
HomePageLayoutBlueprint,
type HomePageLayoutBlueprintParams,
} from './alpha/blueprints/HomePageLayoutBlueprint';
export {
homePageWidgetDataRef,
type HomePageWidgetData,
homePageLayoutComponentDataRef,
type HomePageLayoutProps,
} from './alpha/dataRefs';
export type { ComponentParts, CardLayout, CardSettings } from './extensions';
@@ -0,0 +1,64 @@
/*
* 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.
* 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 { JSX } from 'react';
import {
createExtensionBlueprint,
ExtensionBoundary,
} from '@backstage/frontend-plugin-api';
import {
homePageLayoutComponentDataRef,
HomePageLayoutProps,
} from '../dataRefs';
/**
* Parameters for creating a home page layout extension.
*
* @alpha
*/
export interface HomePageLayoutBlueprintParams {
/**
* Async loader that returns the layout component.
* The component receives the collected widgets and renders them.
*/
loader: () => Promise<(props: HomePageLayoutProps) => JSX.Element>;
}
/**
* Blueprint for creating custom home page layouts.
*
* A layout receives the list of installed widgets and is responsible for
* arranging them on the home page. This follows the same pattern as
* `EntityContentLayoutBlueprint` in the catalog plugin.
*
* If no layout extension is installed, the home page uses a built-in default.
* Users can install their own layout to customize how widgets are arranged.
*
* @alpha
*/
export const HomePageLayoutBlueprint = createExtensionBlueprint({
kind: 'home-page-layout',
attachTo: { id: 'page:home', input: 'layouts' },
output: [homePageLayoutComponentDataRef],
dataRefs: {
component: homePageLayoutComponentDataRef,
},
*factory({ loader }: HomePageLayoutBlueprintParams, { node }) {
yield homePageLayoutComponentDataRef(
ExtensionBoundary.lazyComponent(node, loader),
);
},
});
@@ -15,7 +15,6 @@
*/
import { lazy, ReactElement } from 'react';
import { compatWrapper } from '@backstage/core-compat-api';
import {
createExtensionBlueprint,
ExtensionBoundary,
@@ -96,12 +95,11 @@ export const HomepageWidgetBlueprint = createExtensionBlueprint({
const Widget = (
props: CardExtensionProps<Record<string, unknown>>,
): ReactElement =>
compatWrapper(
<ExtensionBoundary node={node}>
<LazyCard {...props} />
</ExtensionBoundary>,
);
): ReactElement => (
<ExtensionBoundary node={node}>
<LazyCard {...props} />
</ExtensionBoundary>
);
yield homePageWidgetDataRef({
component: <Widget {...(params.componentProps ?? {})} />,
+28 -1
View File
@@ -15,7 +15,7 @@
*/
import { createExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ReactElement } from 'react';
import { JSX, ReactElement } from 'react';
import type { CardLayout, CardSettings } from '../extensions';
/**
@@ -64,3 +64,30 @@ export const homePageWidgetDataRef =
createExtensionDataRef<HomePageWidgetData>().with({
id: 'home.widget.data',
});
/**
* Props provided to a home page layout component.
*
* @alpha
*/
export interface HomePageLayoutProps {
/**
* The list of widget elements and metadata to render on the home page.
*/
widgets: Array<HomePageWidgetData>;
}
/**
* Extension data ref for home page layout components.
*
* A layout receives the collected widgets and is responsible for arranging
* them on the home page. This follows the same pattern as
* EntityContentLayoutBlueprint in the catalog plugin.
*
* @alpha
*/
export const homePageLayoutComponentDataRef = createExtensionDataRef<
(props: HomePageLayoutProps) => JSX.Element
>().with({
id: 'home.layout.component',
});