diff --git a/packages/frontend-plugin-api/src/blueprints/HeaderActionBlueprint.tsx b/packages/frontend-plugin-api/src/blueprints/HeaderActionBlueprint.tsx index cbc6f6ccf5..c2bf6052b7 100644 --- a/packages/frontend-plugin-api/src/blueprints/HeaderActionBlueprint.tsx +++ b/packages/frontend-plugin-api/src/blueprints/HeaderActionBlueprint.tsx @@ -14,16 +14,14 @@ * limitations under the License. */ +import { lazy as reactLazy } from 'react'; +import { ExtensionBoundary } from '../components'; import { + coreExtensionData, createExtensionBlueprint, createExtensionBlueprintParams, - createExtensionDataRef, } from '../wiring'; -const actionDataRef = createExtensionDataRef<() => Promise>().with( - { id: 'core.header-action.loader' }, -); - /** * Creates extensions that provide plugin-scoped header actions. * @@ -37,14 +35,18 @@ const actionDataRef = createExtensionDataRef<() => Promise>().with( export const HeaderActionBlueprint = createExtensionBlueprint({ kind: 'header-action', attachTo: { id: 'api:app/header-actions', input: 'actions' }, - output: [actionDataRef], - dataRefs: { - action: actionDataRef, - }, + output: [coreExtensionData.reactElement], defineParams(params: { loader: () => Promise }) { return createExtensionBlueprintParams(params); }, - *factory(params) { - yield actionDataRef(params.loader); + *factory(params, { node }) { + const LazyAction = reactLazy(() => + params.loader().then(element => ({ default: () => element })), + ); + yield coreExtensionData.reactElement( + + + , + ); }, }); diff --git a/plugins/app/src/apis/HeaderActionsApi/DefaultHeaderActionsApi.test.tsx b/plugins/app/src/apis/HeaderActionsApi/DefaultHeaderActionsApi.test.tsx index dd881a786f..4713896f40 100644 --- a/plugins/app/src/apis/HeaderActionsApi/DefaultHeaderActionsApi.test.tsx +++ b/plugins/app/src/apis/HeaderActionsApi/DefaultHeaderActionsApi.test.tsx @@ -18,17 +18,15 @@ import { render, screen } from '@testing-library/react'; import { DefaultHeaderActionsApi } from './DefaultHeaderActionsApi'; describe('DefaultHeaderActionsApi', () => { - it('should return actions for a specific plugin', async () => { + it('should return actions for a specific plugin', () => { const api = DefaultHeaderActionsApi.fromActions([ { - loader: async () => , + element: , pluginId: 'plugin-a', - nodeId: 'plugin-header-action:plugin-a/action-a', }, { - loader: async () => , + element: , pluginId: 'plugin-b', - nodeId: 'plugin-header-action:plugin-b/action-b', }, ]); @@ -36,34 +34,31 @@ describe('DefaultHeaderActionsApi', () => { expect(api.getHeaderActions('plugin-b')).toHaveLength(1); render(<>{api.getHeaderActions('plugin-a')}); - await expect( - screen.findByRole('button', { name: 'Action A' }), - ).resolves.toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Action A' }), + ).toBeInTheDocument(); }); it('should return an empty array for unknown plugins', () => { const api = DefaultHeaderActionsApi.fromActions([ { - loader: async () => Action, + element: Action, pluginId: 'plugin-a', - nodeId: 'plugin-header-action:plugin-a/action', }, ]); expect(api.getHeaderActions('unknown-plugin')).toEqual([]); }); - it('should group multiple actions by plugin', async () => { + it('should group multiple actions by plugin', () => { const api = DefaultHeaderActionsApi.fromActions([ { - loader: async () => , + element: , pluginId: 'plugin-a', - nodeId: 'plugin-header-action:plugin-a/first', }, { - loader: async () => , + element: , pluginId: 'plugin-a', - nodeId: 'plugin-header-action:plugin-a/second', }, ]); @@ -71,11 +66,7 @@ describe('DefaultHeaderActionsApi', () => { expect(actions).toHaveLength(2); render(<>{actions}); - await expect( - screen.findByRole('button', { name: 'First' }), - ).resolves.toBeInTheDocument(); - await expect( - screen.findByRole('button', { name: 'Second' }), - ).resolves.toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'First' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Second' })).toBeInTheDocument(); }); }); diff --git a/plugins/app/src/apis/HeaderActionsApi/DefaultHeaderActionsApi.tsx b/plugins/app/src/apis/HeaderActionsApi/DefaultHeaderActionsApi.tsx index 13f94da55b..d165410a4d 100644 --- a/plugins/app/src/apis/HeaderActionsApi/DefaultHeaderActionsApi.tsx +++ b/plugins/app/src/apis/HeaderActionsApi/DefaultHeaderActionsApi.tsx @@ -14,13 +14,12 @@ * limitations under the License. */ -import { Suspense, ReactNode, lazy } from 'react'; +import { ReactNode } from 'react'; import { type HeaderActionsApi } from '@backstage/frontend-plugin-api'; type ActionInput = { - loader: () => Promise; + element: JSX.Element; pluginId: string; - nodeId: string; }; /** @@ -45,16 +44,7 @@ export class DefaultHeaderActionsApi implements HeaderActionsApi { actionsByPlugin.set(action.pluginId, pluginActions); } - const LazyAction = lazy(async () => { - const element = await action.loader(); - return { default: () => element }; - }); - - pluginActions.push( - - - , - ); + pluginActions.push(action.element); } return new DefaultHeaderActionsApi(actionsByPlugin); diff --git a/plugins/app/src/extensions/HeaderActionsApi.ts b/plugins/app/src/extensions/HeaderActionsApi.ts index 6768bb3655..b1779b09a3 100644 --- a/plugins/app/src/extensions/HeaderActionsApi.ts +++ b/plugins/app/src/extensions/HeaderActionsApi.ts @@ -15,7 +15,7 @@ */ import { - HeaderActionBlueprint, + coreExtensionData, headerActionsApiRef, createExtensionInput, ApiBlueprint, @@ -28,7 +28,7 @@ import { DefaultHeaderActionsApi } from '../apis/HeaderActionsApi'; export const HeaderActionsApi = ApiBlueprint.makeWithOverrides({ name: 'header-actions', inputs: { - actions: createExtensionInput([HeaderActionBlueprint.dataRefs.action]), + actions: createExtensionInput([coreExtensionData.reactElement]), }, factory: (originalFactory, { inputs }) => { return originalFactory(defineParams => @@ -38,9 +38,8 @@ export const HeaderActionsApi = ApiBlueprint.makeWithOverrides({ factory: () => { return DefaultHeaderActionsApi.fromActions( inputs.actions.map(actionInput => ({ - loader: actionInput.get(HeaderActionBlueprint.dataRefs.action), + element: actionInput.get(coreExtensionData.reactElement), pluginId: actionInput.node.spec.plugin.pluginId, - nodeId: actionInput.node.spec.id, })), ); },