app-api: merge in createApp from core along with icons
Co-authored-by: Juan Lulkin <jmaiz@spotify.com> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -29,7 +29,9 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/components": "^0.1.0",
|
||||
"@backstage/config": "^0.1.3",
|
||||
"@backstage/plugin-api": "^0.1.0",
|
||||
"@backstage/theme": "^0.2.3",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { IconComponent } from '../../icons';
|
||||
import { IconComponent } from '@backstage/plugin-api';
|
||||
import { Observable } from '../../types';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { render, screen } from '@testing-library/react';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { BrowserRouter, Routes } from 'react-router-dom';
|
||||
import { createRoutableExtension } from '../extensions';
|
||||
import { defaultSystemIcons } from '../icons';
|
||||
import { defaultAppIcons } from './icons';
|
||||
import { createPlugin } from '../plugin';
|
||||
import { useRouteRef } from '../routing/hooks';
|
||||
import {
|
||||
@@ -163,7 +163,7 @@ describe('Integration Test', () => {
|
||||
theme: lightTheme,
|
||||
},
|
||||
],
|
||||
icons: defaultSystemIcons,
|
||||
icons: defaultAppIcons,
|
||||
plugins: [],
|
||||
components,
|
||||
bindRoutes: ({ bind }) => {
|
||||
@@ -214,7 +214,7 @@ describe('Integration Test', () => {
|
||||
theme: lightTheme,
|
||||
},
|
||||
],
|
||||
icons: defaultSystemIcons,
|
||||
icons: defaultAppIcons,
|
||||
plugins: [],
|
||||
components,
|
||||
bindRoutes: ({ bind }) => {
|
||||
@@ -257,7 +257,7 @@ describe('Integration Test', () => {
|
||||
theme: lightTheme,
|
||||
},
|
||||
],
|
||||
icons: defaultSystemIcons,
|
||||
icons: defaultAppIcons,
|
||||
plugins: [],
|
||||
components,
|
||||
bindRoutes: ({ bind }) => {
|
||||
|
||||
@@ -23,6 +23,7 @@ import React, {
|
||||
} from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import { useAsync } from 'react-use';
|
||||
import { IconComponent } from '@backstage/plugin-api';
|
||||
import {
|
||||
AnyApiFactory,
|
||||
ApiHolder,
|
||||
@@ -48,7 +49,6 @@ import {
|
||||
routeElementDiscoverer,
|
||||
traverseElementTree,
|
||||
} from '../extensions/traversal';
|
||||
import { IconComponent, IconComponentMap, IconKey } from '../icons';
|
||||
import { BackstagePlugin } from '../plugin';
|
||||
import { AnyRoutes } from '../plugin/types';
|
||||
import { RouteRef, ExternalRouteRef } from '../routing';
|
||||
@@ -103,7 +103,7 @@ export function generateBoundRoutes(
|
||||
|
||||
type FullAppOptions = {
|
||||
apis: Iterable<AnyApiFactory>;
|
||||
icons: IconComponentMap;
|
||||
icons: NonNullable<AppOptions['icons']>;
|
||||
plugins: BackstagePlugin<any, any>[];
|
||||
components: AppComponents;
|
||||
themes: AppTheme[];
|
||||
@@ -156,7 +156,7 @@ class AppContextImpl implements AppContext {
|
||||
return this.app.getPlugins();
|
||||
}
|
||||
|
||||
getSystemIcon(key: IconKey): IconComponent | undefined {
|
||||
getSystemIcon(key: string): IconComponent | undefined {
|
||||
return this.app.getSystemIcon(key);
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ export class PrivateAppImpl implements BackstageApp {
|
||||
private configApi?: ConfigApi;
|
||||
|
||||
private readonly apis: Iterable<AnyApiFactory>;
|
||||
private readonly icons: IconComponentMap;
|
||||
private readonly icons: NonNullable<AppOptions['icons']>;
|
||||
private readonly plugins: BackstagePlugin<any, any>[];
|
||||
private readonly components: AppComponents;
|
||||
private readonly themes: AppTheme[];
|
||||
@@ -213,7 +213,7 @@ export class PrivateAppImpl implements BackstageApp {
|
||||
return this.plugins;
|
||||
}
|
||||
|
||||
getSystemIcon(key: IconKey): IconComponent | undefined {
|
||||
getSystemIcon(key: string): IconComponent | undefined {
|
||||
return this.icons[key];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { defaultConfigLoader } from './createApp';
|
||||
|
||||
(process as any).env = { NODE_ENV: 'test' };
|
||||
const anyEnv = process.env as any;
|
||||
const anyWindow = window as any;
|
||||
|
||||
describe('defaultConfigLoader', () => {
|
||||
afterEach(() => {
|
||||
delete anyEnv.APP_CONFIG;
|
||||
delete anyWindow.__APP_CONFIG__;
|
||||
});
|
||||
|
||||
it('loads static config', async () => {
|
||||
anyEnv.APP_CONFIG = [
|
||||
{ data: { my: 'config' }, context: 'a' },
|
||||
{ data: { my: 'override-config' }, context: 'b' },
|
||||
];
|
||||
|
||||
const configs = await defaultConfigLoader();
|
||||
expect(configs).toEqual([
|
||||
{ data: { my: 'config' }, context: 'a' },
|
||||
{ data: { my: 'override-config' }, context: 'b' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('loads runtime config', async () => {
|
||||
anyEnv.APP_CONFIG = [
|
||||
{ data: { my: 'override-config' }, context: 'a' },
|
||||
{ data: { my: 'config' }, context: 'b' },
|
||||
];
|
||||
|
||||
const configs = await (defaultConfigLoader as any)(
|
||||
'{"my":"runtime-config"}',
|
||||
);
|
||||
expect(configs).toEqual([
|
||||
{ data: { my: 'override-config' }, context: 'a' },
|
||||
{ data: { my: 'config' }, context: 'b' },
|
||||
{ data: { my: 'runtime-config' }, context: 'env' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('fails to load invalid missing config', async () => {
|
||||
await expect(defaultConfigLoader()).rejects.toThrow(
|
||||
'No static configuration provided',
|
||||
);
|
||||
});
|
||||
|
||||
it('fails to load invalid static config', async () => {
|
||||
anyEnv.APP_CONFIG = { my: 'invalid-config' };
|
||||
await expect(defaultConfigLoader()).rejects.toThrow(
|
||||
'Static configuration has invalid format',
|
||||
);
|
||||
});
|
||||
|
||||
it('fails to load bad runtime config', async () => {
|
||||
anyEnv.APP_CONFIG = [{ data: { my: 'config' }, context: 'a' }];
|
||||
|
||||
await expect((defaultConfigLoader as any)('}')).rejects.toThrow(
|
||||
'Failed to load runtime configuration, SyntaxError: Unexpected token } in JSON at position 0',
|
||||
);
|
||||
});
|
||||
|
||||
it('loads config from window.__APP_CONFIG__', async () => {
|
||||
anyEnv.APP_CONFIG = [
|
||||
{ data: { my: 'config' }, context: 'a' },
|
||||
{ data: { my: 'override-config' }, context: 'b' },
|
||||
];
|
||||
const windowConfig = { app: { configKey: 'config-value' } };
|
||||
anyWindow.__APP_CONFIG__ = windowConfig;
|
||||
|
||||
const configs = await defaultConfigLoader();
|
||||
|
||||
expect(configs).toEqual([
|
||||
...anyEnv.APP_CONFIG,
|
||||
{ context: 'window', data: windowConfig },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React from 'react';
|
||||
import { AppOptions, BootErrorPageProps, AppConfigLoader } from './types';
|
||||
import { defaultAppIcons } from './icons';
|
||||
import { BrowserRouter, MemoryRouter } from 'react-router-dom';
|
||||
import LightIcon from '@material-ui/icons/WbSunny';
|
||||
import DarkIcon from '@material-ui/icons/Brightness2';
|
||||
import { ErrorPage, Progress } from '@backstage/components';
|
||||
import { defaultApis } from './defaultApis';
|
||||
import { lightTheme, darkTheme } from '@backstage/theme';
|
||||
import { AppConfig, JsonObject } from '@backstage/config';
|
||||
import { PrivateAppImpl } from './App';
|
||||
|
||||
/**
|
||||
* The default config loader, which expects that config is available at compile-time
|
||||
* in `process.env.APP_CONFIG`. APP_CONFIG should be an array of config objects as
|
||||
* returned by the config loader.
|
||||
*
|
||||
* It will also load runtime config from the __APP_INJECTED_RUNTIME_CONFIG__ string,
|
||||
* which can be rewritten at runtime to contain an additional JSON config object.
|
||||
* If runtime config is present, it will be placed first in the config array, overriding
|
||||
* other config values.
|
||||
*/
|
||||
export const defaultConfigLoader: AppConfigLoader = async (
|
||||
// This string may be replaced at runtime to provide additional config.
|
||||
// It should be replaced by a JSON-serialized config object.
|
||||
// It's a param so we can test it, but at runtime this will always fall back to default.
|
||||
runtimeConfigJson: string = '__APP_INJECTED_RUNTIME_CONFIG__',
|
||||
) => {
|
||||
const appConfig = process.env.APP_CONFIG;
|
||||
if (!appConfig) {
|
||||
throw new Error('No static configuration provided');
|
||||
}
|
||||
if (!Array.isArray(appConfig)) {
|
||||
throw new Error('Static configuration has invalid format');
|
||||
}
|
||||
const configs = (appConfig.slice() as unknown) as AppConfig[];
|
||||
|
||||
// Avoiding this string also being replaced at runtime
|
||||
if (runtimeConfigJson !== '__app_injected_runtime_config__'.toUpperCase()) {
|
||||
try {
|
||||
const data = JSON.parse(runtimeConfigJson) as JsonObject;
|
||||
if (Array.isArray(data)) {
|
||||
configs.push(...data);
|
||||
} else {
|
||||
configs.push({ data, context: 'env' });
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to load runtime configuration, ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
const windowAppConfig = (window as any).__APP_CONFIG__;
|
||||
if (windowAppConfig) {
|
||||
configs.push({
|
||||
context: 'window',
|
||||
data: windowAppConfig,
|
||||
});
|
||||
}
|
||||
return configs;
|
||||
};
|
||||
|
||||
// createApp is defined in core, and not core-api, since we need access
|
||||
// to the components inside core to provide defaults.
|
||||
// The actual implementation of the app class still lives in core-api,
|
||||
// as it needs to be used by dev- and test-utils.
|
||||
|
||||
/**
|
||||
* Creates a new Backstage App.
|
||||
*/
|
||||
export function createApp(options?: AppOptions) {
|
||||
const DefaultNotFoundPage = () => (
|
||||
<ErrorPage status="404" statusMessage="PAGE NOT FOUND" />
|
||||
);
|
||||
const DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => {
|
||||
let message = '';
|
||||
if (step === 'load-config') {
|
||||
message = `The configuration failed to load, someone should have a look at this error: ${error.message}`;
|
||||
}
|
||||
// TODO: figure out a nicer way to handle routing on the error page, when it can be done.
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<ErrorPage status="501" statusMessage={message} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
const apis = options?.apis ?? [];
|
||||
const icons = { ...defaultAppIcons, ...options?.icons };
|
||||
const plugins = options?.plugins ?? [];
|
||||
const components = {
|
||||
NotFoundErrorPage: DefaultNotFoundPage,
|
||||
BootErrorPage: DefaultBootErrorPage,
|
||||
Progress: Progress,
|
||||
Router: BrowserRouter,
|
||||
...options?.components,
|
||||
};
|
||||
const themes = options?.themes ?? [
|
||||
{
|
||||
id: 'light',
|
||||
title: 'Light Theme',
|
||||
variant: 'light',
|
||||
theme: lightTheme,
|
||||
icon: <LightIcon />,
|
||||
},
|
||||
{
|
||||
id: 'dark',
|
||||
title: 'Dark Theme',
|
||||
variant: 'dark',
|
||||
theme: darkTheme,
|
||||
icon: <DarkIcon />,
|
||||
},
|
||||
];
|
||||
const configLoader = options?.configLoader ?? defaultConfigLoader;
|
||||
|
||||
const app = new PrivateAppImpl({
|
||||
apis,
|
||||
icons,
|
||||
plugins,
|
||||
components,
|
||||
themes,
|
||||
configLoader,
|
||||
defaultApis,
|
||||
bindRoutes: options?.bindRoutes,
|
||||
});
|
||||
|
||||
app.verify();
|
||||
|
||||
return app;
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 {
|
||||
alertApiRef,
|
||||
errorApiRef,
|
||||
AlertApiForwarder,
|
||||
ErrorApiForwarder,
|
||||
ErrorAlerter,
|
||||
discoveryApiRef,
|
||||
GoogleAuth,
|
||||
GithubAuth,
|
||||
OAuth2,
|
||||
OktaAuth,
|
||||
GitlabAuth,
|
||||
Auth0Auth,
|
||||
MicrosoftAuth,
|
||||
oauthRequestApiRef,
|
||||
OAuthRequestManager,
|
||||
googleAuthApiRef,
|
||||
githubAuthApiRef,
|
||||
oauth2ApiRef,
|
||||
oktaAuthApiRef,
|
||||
gitlabAuthApiRef,
|
||||
auth0AuthApiRef,
|
||||
microsoftAuthApiRef,
|
||||
storageApiRef,
|
||||
WebStorage,
|
||||
createApiFactory,
|
||||
configApiRef,
|
||||
UrlPatternDiscovery,
|
||||
samlAuthApiRef,
|
||||
SamlAuth,
|
||||
oneloginAuthApiRef,
|
||||
OneLoginAuth,
|
||||
oidcAuthApiRef,
|
||||
} from '../apis';
|
||||
|
||||
import OAuth2Icon from '@material-ui/icons/AcUnit';
|
||||
|
||||
export const defaultApis = [
|
||||
createApiFactory({
|
||||
api: discoveryApiRef,
|
||||
deps: { configApi: configApiRef },
|
||||
factory: ({ configApi }) =>
|
||||
UrlPatternDiscovery.compile(
|
||||
`${configApi.getString('backend.baseUrl')}/api/{{ pluginId }}`,
|
||||
),
|
||||
}),
|
||||
createApiFactory(alertApiRef, new AlertApiForwarder()),
|
||||
createApiFactory({
|
||||
api: errorApiRef,
|
||||
deps: { alertApi: alertApiRef },
|
||||
factory: ({ alertApi }) =>
|
||||
new ErrorAlerter(alertApi, new ErrorApiForwarder()),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: storageApiRef,
|
||||
deps: { errorApi: errorApiRef },
|
||||
factory: ({ errorApi }) => WebStorage.create({ errorApi }),
|
||||
}),
|
||||
createApiFactory(oauthRequestApiRef, new OAuthRequestManager()),
|
||||
createApiFactory({
|
||||
api: googleAuthApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
oauthRequestApi: oauthRequestApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
GoogleAuth.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
}),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: microsoftAuthApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
oauthRequestApi: oauthRequestApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
MicrosoftAuth.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
}),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: githubAuthApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
oauthRequestApi: oauthRequestApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
GithubAuth.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
defaultScopes: ['read:user'],
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
}),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: oktaAuthApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
oauthRequestApi: oauthRequestApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
OktaAuth.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
}),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: gitlabAuthApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
oauthRequestApi: oauthRequestApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
GitlabAuth.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
}),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: auth0AuthApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
oauthRequestApi: oauthRequestApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
Auth0Auth.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
}),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: oauth2ApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
oauthRequestApi: oauthRequestApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
OAuth2.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
}),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: samlAuthApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, configApi }) =>
|
||||
SamlAuth.create({
|
||||
discoveryApi,
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
}),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: oneloginAuthApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
oauthRequestApi: oauthRequestApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
OneLoginAuth.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
}),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: oidcAuthApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
oauthRequestApi: oauthRequestApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
OAuth2.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
provider: {
|
||||
id: 'oidc',
|
||||
title: 'Your Identity Provider',
|
||||
icon: OAuth2Icon,
|
||||
},
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
}),
|
||||
}),
|
||||
];
|
||||
@@ -14,10 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentType } from 'react';
|
||||
import { SvgIconProps } from '@material-ui/core';
|
||||
import {
|
||||
BrokenImageIcon,
|
||||
ChatIcon,
|
||||
DashboardIcon,
|
||||
EmailIcon,
|
||||
GitHubIcon,
|
||||
GroupIcon,
|
||||
HelpIcon,
|
||||
UserIcon,
|
||||
WarningIcon,
|
||||
} from '@backstage/components';
|
||||
import { IconComponent } from '@backstage/plugin-api';
|
||||
|
||||
export type SystemIconKey =
|
||||
type AppIconsKey =
|
||||
| 'brokenImage'
|
||||
| 'chat'
|
||||
| 'dashboard'
|
||||
@@ -28,6 +38,16 @@ export type SystemIconKey =
|
||||
| 'user'
|
||||
| 'warning';
|
||||
|
||||
export type IconComponent = ComponentType<SvgIconProps>;
|
||||
export type IconKey = SystemIconKey | string;
|
||||
export type IconComponentMap = { [key in IconKey]: IconComponent };
|
||||
export type AppIcons = { [key in AppIconsKey]: IconComponent };
|
||||
|
||||
export const defaultAppIcons: AppIcons = {
|
||||
brokenImage: BrokenImageIcon,
|
||||
chat: ChatIcon,
|
||||
dashboard: DashboardIcon,
|
||||
email: EmailIcon,
|
||||
github: GitHubIcon,
|
||||
group: GroupIcon,
|
||||
help: HelpIcon,
|
||||
user: UserIcon,
|
||||
warning: WarningIcon,
|
||||
};
|
||||
@@ -15,13 +15,14 @@
|
||||
*/
|
||||
|
||||
import { ComponentType } from 'react';
|
||||
import { IconComponent, IconComponentMap, IconKey } from '../icons';
|
||||
import { AnyExternalRoutes, BackstagePlugin } from '../plugin/types';
|
||||
import { ExternalRouteRef, RouteRef } from '../routing';
|
||||
import { AnyApiFactory } from '../apis';
|
||||
import { AppTheme, ProfileInfo } from '../apis/definitions';
|
||||
import { AppConfig } from '@backstage/config';
|
||||
import { IconComponent } from '@backstage/plugin-api';
|
||||
import { SubRouteRef } from '../routing/types';
|
||||
import { AppIcons } from './icons';
|
||||
|
||||
export type BootErrorPageProps = {
|
||||
step: 'load-config';
|
||||
@@ -125,7 +126,7 @@ export type AppOptions = {
|
||||
/**
|
||||
* Supply icons to override the default ones.
|
||||
*/
|
||||
icons?: IconComponentMap;
|
||||
icons?: AppIcons & { [key in string]: IconComponent };
|
||||
|
||||
/**
|
||||
* A list of all plugins to include in the app.
|
||||
@@ -202,7 +203,7 @@ export type BackstageApp = {
|
||||
/**
|
||||
* Get a common or custom icon for this app.
|
||||
*/
|
||||
getSystemIcon(key: IconKey): IconComponent | undefined;
|
||||
getSystemIcon(key: string): IconComponent | undefined;
|
||||
|
||||
/**
|
||||
* Provider component that should wrap the Router created with getRouter()
|
||||
@@ -233,7 +234,7 @@ export type AppContext = {
|
||||
/**
|
||||
* Get a common or custom icon for this app.
|
||||
*/
|
||||
getSystemIcon(key: IconKey): IconComponent | undefined;
|
||||
getSystemIcon(key: string): IconComponent | undefined;
|
||||
|
||||
/**
|
||||
* Get the components registered for various purposes in the app.
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { SvgIconProps } from '@material-ui/core';
|
||||
import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage';
|
||||
import MuiChatIcon from '@material-ui/icons/Chat';
|
||||
import MuiDashboardIcon from '@material-ui/icons/Dashboard';
|
||||
import MuiEmailIcon from '@material-ui/icons/Email';
|
||||
import MuiGitHubIcon from '@material-ui/icons/GitHub';
|
||||
import MuiHelpIcon from '@material-ui/icons/Help';
|
||||
import MuiPeopleIcon from '@material-ui/icons/People';
|
||||
import MuiPersonIcon from '@material-ui/icons/Person';
|
||||
import MuiWarningIcon from '@material-ui/icons/Warning';
|
||||
import React from 'react';
|
||||
import { useApp } from '../app/AppContext';
|
||||
import { IconComponent, IconComponentMap, SystemIconKey } from './types';
|
||||
|
||||
export const defaultSystemIcons: IconComponentMap = {
|
||||
brokenImage: MuiBrokenImageIcon,
|
||||
chat: MuiChatIcon,
|
||||
dashboard: MuiDashboardIcon,
|
||||
email: MuiEmailIcon,
|
||||
github: MuiGitHubIcon,
|
||||
group: MuiPeopleIcon,
|
||||
help: MuiHelpIcon,
|
||||
user: MuiPersonIcon,
|
||||
warning: MuiWarningIcon,
|
||||
};
|
||||
|
||||
const overridableSystemIcon = (key: SystemIconKey): IconComponent => {
|
||||
const Component = (props: SvgIconProps) => {
|
||||
const app = useApp();
|
||||
const Icon = app.getSystemIcon(key);
|
||||
return Icon ? <Icon {...props} /> : <MuiBrokenImageIcon {...props} />;
|
||||
};
|
||||
return Component;
|
||||
};
|
||||
|
||||
export const BrokenImageIcon = overridableSystemIcon('brokenImage');
|
||||
export const ChatIcon = overridableSystemIcon('chat');
|
||||
export const DashboardIcon = overridableSystemIcon('dashboard');
|
||||
export const EmailIcon = overridableSystemIcon('email');
|
||||
export const GitHubIcon = overridableSystemIcon('github');
|
||||
export const GroupIcon = overridableSystemIcon('group');
|
||||
export const HelpIcon = overridableSystemIcon('help');
|
||||
export const UserIcon = overridableSystemIcon('user');
|
||||
export const WarningIcon = overridableSystemIcon('warning');
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 * from './icons';
|
||||
export * from './types';
|
||||
@@ -14,7 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './public';
|
||||
import * as privateExports from './private';
|
||||
|
||||
export default privateExports;
|
||||
export * from './apis';
|
||||
export * from './app';
|
||||
export * from './extensions';
|
||||
export * from './plugin';
|
||||
export * from './routing';
|
||||
export * from './types';
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { PrivateAppImpl } from './app/App';
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 * from './apis';
|
||||
export * from './app';
|
||||
export * from './extensions';
|
||||
export * from './icons';
|
||||
export * from './plugin';
|
||||
export * from './routing';
|
||||
export * from './types';
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
ParamKeys,
|
||||
OptionalParams,
|
||||
} from './types';
|
||||
import { IconComponent } from '../icons';
|
||||
import { IconComponent } from '@backstage/plugin-api';
|
||||
|
||||
// TODO(Rugvip): Remove this in the next breaking release, it's exported but unused
|
||||
export type RouteRefConfig<Params extends AnyParams> = {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { IconComponent } from '../icons';
|
||||
import { IconComponent } from '@backstage/plugin-api';
|
||||
import { getOrCreateGlobalSingleton } from '../lib/globalObject';
|
||||
|
||||
export type AnyParams = { [param in string]: string } | undefined;
|
||||
|
||||
Reference in New Issue
Block a user