frontend-plugin-api: switch to just PageHeaderAction

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2026-02-11 23:18:14 +01:00
parent 4738047ab7
commit d6de6069aa
8 changed files with 36 additions and 147 deletions
@@ -32,19 +32,10 @@ describe('DefaultHeaderActionsApi', () => {
},
]);
const actionsA = api.getHeaderActions('plugin-a');
const actionsB = api.getHeaderActions('plugin-b');
expect(api.getHeaderActions('plugin-a')).toHaveLength(1);
expect(api.getHeaderActions('plugin-b')).toHaveLength(1);
expect(actionsA).toHaveLength(1);
expect(actionsB).toHaveLength(1);
expect(actionsA[0].nodeId).toBe(
'plugin-header-action:plugin-a/action-a',
);
expect(actionsB[0].nodeId).toBe(
'plugin-header-action:plugin-b/action-b',
);
render(<>{actionsA[0].element}</>);
render(<>{api.getHeaderActions('plugin-a')}</>);
await expect(
screen.findByRole('button', { name: 'Action A' }),
).resolves.toBeInTheDocument();
@@ -78,16 +69,8 @@ describe('DefaultHeaderActionsApi', () => {
const actions = api.getHeaderActions('plugin-a');
expect(actions).toHaveLength(2);
expect(actions[0].nodeId).toBe('plugin-header-action:plugin-a/first');
expect(actions[1].nodeId).toBe('plugin-header-action:plugin-a/second');
render(
<>
{actions.map(a => (
<span key={a.nodeId}>{a.element}</span>
))}
</>,
);
render(<>{actions}</>);
await expect(
screen.findByRole('button', { name: 'First' }),
).resolves.toBeInTheDocument();
@@ -14,11 +14,8 @@
* limitations under the License.
*/
import { Suspense, lazy } from 'react';
import {
type HeaderActionsApi,
type HeaderAction,
} from '@backstage/frontend-plugin-api';
import { Suspense, ReactNode, lazy } from 'react';
import { type HeaderActionsApi } from '@backstage/frontend-plugin-api';
type ActionInput = {
loader: () => Promise<JSX.Element>;
@@ -32,18 +29,14 @@ type ActionInput = {
* @internal
*/
export class DefaultHeaderActionsApi implements HeaderActionsApi {
constructor(
private readonly actionsByPlugin: Map<string, HeaderAction[]>,
) {}
constructor(private readonly actionsByPlugin: Map<string, ReactNode[]>) {}
getHeaderActions(pluginId: string): HeaderAction[] {
getHeaderActions(pluginId: string): ReactNode[] {
return this.actionsByPlugin.get(pluginId) ?? [];
}
static fromActions(
actions: Array<ActionInput>,
): DefaultHeaderActionsApi {
const actionsByPlugin = new Map<string, HeaderAction[]>();
static fromActions(actions: Array<ActionInput>): DefaultHeaderActionsApi {
const actionsByPlugin = new Map<string, ReactNode[]>();
for (const action of actions) {
let pluginActions = actionsByPlugin.get(action.pluginId);
@@ -57,14 +50,11 @@ export class DefaultHeaderActionsApi implements HeaderActionsApi {
return { default: () => element };
});
pluginActions.push({
nodeId: action.nodeId,
element: (
<Suspense key={action.nodeId} fallback={null}>
<LazyAction />
</Suspense>
),
});
pluginActions.push(
<Suspense key={action.nodeId} fallback={null}>
<LazyAction />
</Suspense>,
);
}
return new DefaultHeaderActionsApi(actionsByPlugin);
@@ -15,7 +15,7 @@
*/
import {
PluginHeaderActionBlueprint,
HeaderActionBlueprint,
headerActionsApiRef,
createExtensionInput,
ApiBlueprint,
@@ -28,9 +28,7 @@ import { DefaultHeaderActionsApi } from '../apis/HeaderActionsApi';
export const HeaderActionsApi = ApiBlueprint.makeWithOverrides({
name: 'header-actions',
inputs: {
actions: createExtensionInput([
PluginHeaderActionBlueprint.dataRefs.action,
]),
actions: createExtensionInput([HeaderActionBlueprint.dataRefs.action]),
},
factory: (originalFactory, { inputs }) => {
return originalFactory(defineParams =>
@@ -40,9 +38,7 @@ export const HeaderActionsApi = ApiBlueprint.makeWithOverrides({
factory: () => {
return DefaultHeaderActionsApi.fromActions(
inputs.actions.map(actionInput => ({
loader: actionInput.get(
PluginHeaderActionBlueprint.dataRefs.action,
),
loader: actionInput.get(HeaderActionBlueprint.dataRefs.action),
pluginId: actionInput.node.spec.plugin.pluginId,
nodeId: actionInput.node.spec.id,
})),