frontend-app-api: added IconsApi implementation and icons option for createApp

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-01-18 14:18:47 +01:00
parent 7eae3e0c70
commit 42ebf27c0b
5 changed files with 87 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': patch
---
Added `IconsApi` implementation and the ability to configure icons through the `icons` option for `createApp` and `createSpecializedApp`. This is not a long-term solution as icons should be installable via extensions instead. This is just a stop-gap to make sure there is feature parity with the existing frontend system.
+7
View File
@@ -8,12 +8,16 @@ import { ConfigApi } from '@backstage/core-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
import { FrontendFeature } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { JSX as JSX_2 } from 'react';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { SubRouteRef } from '@backstage/frontend-plugin-api';
// @public (undocumented)
export function createApp(options?: {
icons?: {
[key in string]: IconComponent;
};
features?: (FrontendFeature | CreateAppFeatureLoader)[];
configLoader?: () => Promise<{
config: ConfigApi;
@@ -49,6 +53,9 @@ export function createExtensionTree(options: { config: Config }): ExtensionTree;
// @public
export function createSpecializedApp(options?: {
icons?: {
[key in string]: IconComponent;
};
features?: FrontendFeature[];
config?: ConfigApi;
bindRoutes?(context: { bind: CreateAppRouteBinder }): void;
@@ -0,0 +1,38 @@
/*
* Copyright 2023 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 { IconComponent, IconsApi } from '@backstage/frontend-plugin-api';
/**
* Implementation for the {@link IconsApi}
*
* @internal
*/
export class DefaultIconsApi implements IconsApi {
#icons: Map<string, IconComponent>;
constructor(icons: { [key in string]: IconComponent }) {
this.#icons = new Map(Object.entries(icons));
}
getIcon(key: string): IconComponent | undefined {
return this.#icons.get(key);
}
listIconKeys: IconsApi['listIconKeys'] = () => {
return { keys: this.#icons.keys() };
};
}
@@ -0,0 +1,17 @@
/*
* Copyright 2023 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.
*/
export { DefaultIconsApi } from './IconsApi';
@@ -29,6 +29,7 @@ import {
createTranslationExtension,
ExtensionDataRef,
FrontendFeature,
iconsApiRef,
RouteRef,
useRouteRef,
} from '@backstage/frontend-plugin-api';
@@ -105,7 +106,10 @@ import { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiri
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides';
import { DefaultComponentsApi } from '../apis/implementations/ComponentsApi';
import { DefaultIconsApi } from '../apis/implementations/IconsApi';
import { stringifyError } from '@backstage/errors';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { icons as defaultIcons } from '../../../app-defaults/src/defaults';
const DefaultApis = defaultApis.map(factory => createApiExtension({ factory }));
@@ -269,6 +273,7 @@ export interface CreateAppFeatureLoader {
/** @public */
export function createApp(options?: {
icons?: { [key in string]: IconComponent };
features?: (FrontendFeature | CreateAppFeatureLoader)[];
configLoader?: () => Promise<{ config: ConfigApi }>;
bindRoutes?(context: { bind: CreateAppRouteBinder }): void;
@@ -303,6 +308,7 @@ export function createApp(options?: {
}
const app = createSpecializedApp({
icons: options?.icons,
config,
features: [...discoveredFeatures, ...providedFeatures],
bindRoutes: options?.bindRoutes,
@@ -330,6 +336,7 @@ export function createApp(options?: {
* @public
*/
export function createSpecializedApp(options?: {
icons?: { [key in string]: IconComponent };
features?: FrontendFeature[];
config?: ConfigApi;
bindRoutes?(context: { bind: CreateAppRouteBinder }): void;
@@ -348,7 +355,12 @@ export function createSpecializedApp(options?: {
});
const appIdentityProxy = new AppIdentityProxy();
const apiHolder = createApiHolder(tree, config, appIdentityProxy);
const apiHolder = createApiHolder(
tree,
config,
appIdentityProxy,
options?.icons,
);
const featureFlagApi = apiHolder.get(featureFlagsApiRef);
if (featureFlagApi) {
@@ -402,6 +414,7 @@ function createApiHolder(
tree: AppTree,
configApi: ConfigApi,
appIdentityProxy: AppIdentityProxy,
icons?: { [key in string]: IconComponent },
): ApiHolder {
const factoryRegistry = new ApiFactoryRegistry();
@@ -470,6 +483,12 @@ function createApiHolder(
factory: () => new DefaultComponentsApi(componentsMap),
});
factoryRegistry.register('static', {
api: iconsApiRef,
deps: {},
factory: () => new DefaultIconsApi({ ...defaultIcons, ...icons }),
});
factoryRegistry.register('static', {
api: appThemeApiRef,
deps: {},