frontend-plugin-api: add support for sub page icons

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2026-02-13 13:55:19 +01:00
parent a381a6dcdc
commit d2705be180
21 changed files with 202 additions and 4 deletions
@@ -71,6 +71,13 @@ const examplePlugin: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
+18 -1
View File
@@ -1799,6 +1799,13 @@ export const PageBlueprint: ExtensionBlueprint_2<{
{
optional: true;
}
>
| ConfigurableExtensionDataRef_2<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
@@ -1843,6 +1850,8 @@ export interface PageTab {
// (undocumented)
href: string;
// (undocumented)
icon?: IconElement;
// (undocumented)
id: string;
// (undocumented)
label: string;
@@ -2023,6 +2032,7 @@ export const SubPageBlueprint: ExtensionBlueprint_2<{
params: {
path: string;
title: string;
icon?: IconElement;
loader: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
@@ -2036,7 +2046,14 @@ export const SubPageBlueprint: ExtensionBlueprint_2<{
}
>
| ExtensionDataRef_2<JSX_2, 'core.reactElement', {}>
| ExtensionDataRef_2<string, 'core.title', {}>;
| ExtensionDataRef_2<string, 'core.title', {}>
| ExtensionDataRef_2<
IconElement,
'core.icon',
{
optional: true;
}
>;
inputs: {};
config: {
path: string | undefined;
@@ -50,6 +50,7 @@ export const PageBlueprint = createExtensionBlueprint({
coreExtensionData.routeRef.optional(),
coreExtensionData.reactElement,
coreExtensionData.title.optional(),
coreExtensionData.icon.optional(),
]),
},
output: [
@@ -104,9 +105,11 @@ export const PageBlueprint = createExtensionBlueprint({
const tabs: PageTab[] = inputs.pages.map(page => {
const path = page.get(coreExtensionData.routePath);
const tabTitle = page.get(coreExtensionData.title);
const tabIcon = page.get(coreExtensionData.icon);
return {
id: path,
label: tabTitle || path,
icon: tabIcon,
href: path,
matchStrategy: 'prefix' as const,
};
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { IconElement } from '../icons/types';
import { RouteRef } from '../routing';
import { coreExtensionData, createExtensionBlueprint } from '../wiring';
import { ExtensionBoundary } from '../components';
@@ -47,6 +48,7 @@ export const SubPageBlueprint = createExtensionBlueprint({
coreExtensionData.reactElement,
coreExtensionData.title,
coreExtensionData.routeRef.optional(),
coreExtensionData.icon.optional(),
],
config: {
schema: {
@@ -66,6 +68,10 @@ export const SubPageBlueprint = createExtensionBlueprint({
* The title displayed in the tab for this sub-page.
*/
title: string;
/**
* Optional icon for this sub-page, displayed in the tab.
*/
icon?: IconElement;
/**
* A function that returns a promise resolving to the React element to render.
* This enables lazy loading of the sub-page content.
@@ -86,5 +92,8 @@ export const SubPageBlueprint = createExtensionBlueprint({
if (params.routeRef) {
yield coreExtensionData.routeRef(params.routeRef);
}
if (params.icon) {
yield coreExtensionData.icon(params.icon);
}
},
});
@@ -25,6 +25,7 @@ import { createSwappableComponent } from './createSwappableComponent';
export interface PageTab {
id: string;
label: string;
icon?: IconElement;
href: string;
matchStrategy?: 'prefix' | 'exact';
}
@@ -96,12 +97,16 @@ function DefaultPageLayout(props: PageLayoutProps): JSX.Element {
key={tab.id}
href={tab.href}
style={{
display: 'flex',
alignItems: 'center',
gap: '4px',
padding: '8px 12px',
textDecoration: 'none',
color: '#333',
borderBottom: '2px solid transparent',
}}
>
{tab.icon}
{tab.label}
</a>
))}
+7
View File
@@ -545,6 +545,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
+34 -3
View File
@@ -110,6 +110,13 @@ const visualizerPlugin: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
@@ -148,11 +155,19 @@ const visualizerPlugin: OverridableFrontendPlugin<
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<string, 'core.title', {}>;
| ExtensionDataRef<string, 'core.title', {}>
| ExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>;
inputs: {};
params: {
path: string;
title: string;
icon?: IconElement;
loader: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
@@ -178,11 +193,19 @@ const visualizerPlugin: OverridableFrontendPlugin<
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<string, 'core.title', {}>;
| ExtensionDataRef<string, 'core.title', {}>
| ExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>;
inputs: {};
params: {
path: string;
title: string;
icon?: IconElement;
loader: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
@@ -208,11 +231,19 @@ const visualizerPlugin: OverridableFrontendPlugin<
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<string, 'core.title', {}>;
| ExtensionDataRef<string, 'core.title', {}>
| ExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>;
inputs: {};
params: {
path: string;
title: string;
icon?: IconElement;
loader: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
+7
View File
@@ -72,6 +72,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
@@ -220,6 +220,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
@@ -165,6 +165,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
@@ -114,6 +114,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
+14
View File
@@ -1054,6 +1054,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
@@ -1151,6 +1158,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
+7
View File
@@ -111,6 +111,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
+7
View File
@@ -152,6 +152,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
+7
View File
@@ -212,6 +212,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
+7
View File
@@ -86,6 +86,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
@@ -92,6 +92,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
+7
View File
@@ -242,6 +242,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
+14
View File
@@ -117,6 +117,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
@@ -279,6 +286,13 @@ export const searchPage: OverridableExtensionDefinition<{
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
+14
View File
@@ -332,6 +332,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
@@ -403,6 +410,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
@@ -94,6 +94,13 @@ const _default: OverridableFrontendPlugin<
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;