From f8287e5340b1affeda54975ff44ca60cabdc990a Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Mon, 18 Jul 2022 08:10:16 -0500 Subject: [PATCH] Added UserSettingsAvailableIconsTable and related documentation Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- .../add-icons-links-example.png | Bin 0 -> 6342 bytes docs/getting-started/app-custom-theme.md | 51 ++++++++++++ .../core-app-api/src/app/AppContext.test.tsx | 1 + packages/core-app-api/src/app/AppManager.tsx | 9 +++ packages/core-app-api/src/app/types.ts | 5 ++ .../src/routing/RoutingProvider.test.tsx | 1 + packages/core-plugin-api/api-report.md | 7 ++ .../UserSettingsAvailableIconsTable.tsx | 73 ++++++++++++++++++ .../General/UserSettingsGeneral.tsx | 4 + 9 files changed, 151 insertions(+) create mode 100644 docs/assets/getting-started/add-icons-links-example.png create mode 100644 plugins/user-settings/src/components/General/UserSettingsAvailableIconsTable.tsx diff --git a/docs/assets/getting-started/add-icons-links-example.png b/docs/assets/getting-started/add-icons-links-example.png new file mode 100644 index 0000000000000000000000000000000000000000..dfff84b79cf18ae32d48dc8fccc8a7f432963d24 GIT binary patch literal 6342 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 WIX4@k#%lK_*(fc2O_I9xjsF5S|KEB5 literal 0 HcmV?d00001 diff --git a/docs/getting-started/app-custom-theme.md b/docs/getting-started/app-custom-theme.md index 9ca82d01a1..fc39412d61 100644 --- a/docs/getting-started/app-custom-theme.md +++ b/docs/getting-started/app-custom-theme.md @@ -338,6 +338,57 @@ const app = createApp({ [...] ``` +## Adding Icons + +You can add more icons, if the [default icons](https://github.com/backstage/backstage/blob/8d83f5cb4fa0544b0f9160ac22bd5f0f1fe285c6/packages/app-defaults/src/defaults/icons.tsx#L38) do not fit your needs, so that they can be used in other places like for Links in your entities. For this example we'll be using icons from[Material UI](https://v4.mui.com/components/material-icons/) and specifically the `AlarmIcon`. Here's how to do that: + +1. First you will want to open your `App.tsx` in `/packages/app/src` +2. Then you want to import your icon, add this to the rest of your imports: `import AlarmIcon from '@material-ui/icons/Alarm';` +3. Next you want to add the icon like this to your `createApp`: + + ```diff + const app = createApp({ + apis: ..., + plugins: ..., + + icons: { + + alert: AlarmIcon, + + }, + themes: ..., + components: ..., + }); + ``` + +4. Now we can reference `alert` for our icon in our entity links like this: + + ```yaml + apiVersion: backstage.io/v1alpha1 + kind: Component + metadata: + name: artist-lookup + description: Artist Lookup + links: + - url: https://example.com/alert + title: Alerts + icon: alert + ``` + + And this is the result: + + ![Example Link with Alert icon](../assets/getting-started/add-icons-links-example.png) + + Another way you can use these icons is from the `AppContext` like this: + + ```ts + import { useApp } from '@backstage/core-plugin-api'; + + const app = useApp(); + const alertIcon = app.getSystemIcon('alert'); + ``` + + You might want to use this method if you have an icon you want to use in several locations. + +Note: If the icon is not available as one of the default icons or one you've added then it will fall back to Material UI's `LanguageIcon` + ## Custom Homepage In addition to a custom theme, a custom logo, you can also customize the diff --git a/packages/core-app-api/src/app/AppContext.test.tsx b/packages/core-app-api/src/app/AppContext.test.tsx index bc67ee3a9e..a27b7c6f35 100644 --- a/packages/core-app-api/src/app/AppContext.test.tsx +++ b/packages/core-app-api/src/app/AppContext.test.tsx @@ -36,6 +36,7 @@ describe('v1 consumer', () => { getPlugins: jest.fn(), getComponents: jest.fn(), getSystemIcon: jest.fn(), + getSystemIcons: jest.fn(), }; const renderedHook = renderHook(() => useMockAppV1(), { diff --git a/packages/core-app-api/src/app/AppManager.tsx b/packages/core-app-api/src/app/AppManager.tsx index 72c69f7da4..41c57afb98 100644 --- a/packages/core-app-api/src/app/AppManager.tsx +++ b/packages/core-app-api/src/app/AppManager.tsx @@ -71,6 +71,7 @@ import { AppComponents, AppConfigLoader, AppContext, + AppIcons, AppOptions, BackstageApp, SignInPageProps, @@ -153,6 +154,10 @@ class AppContextImpl implements AppContext { return this.app.getSystemIcon(key); } + getSystemIcons(): AppIcons & { [key in string]: IconComponent } { + return this.app.getSystemIcons(); + } + getComponents(): AppComponents { return this.app.getComponents(); } @@ -194,6 +199,10 @@ export class AppManager implements BackstageApp { return this.icons[key]; } + getSystemIcons(): AppIcons & { [key in string]: IconComponent } { + return this.icons; + } + getComponents(): AppComponents { return this.components; } diff --git a/packages/core-app-api/src/app/types.ts b/packages/core-app-api/src/app/types.ts index 979fb0496e..b196e2cf90 100644 --- a/packages/core-app-api/src/app/types.ts +++ b/packages/core-app-api/src/app/types.ts @@ -328,6 +328,11 @@ export type AppContext = { */ getSystemIcon(key: string): IconComponent | undefined; + /** + * Get a list of common and custom icons for this app. + */ + getSystemIcons(): AppIcons & { [key in string]: IconComponent }; + /** * Get the components registered for various purposes in the app. */ diff --git a/packages/core-app-api/src/routing/RoutingProvider.test.tsx b/packages/core-app-api/src/routing/RoutingProvider.test.tsx index dec92072fc..c559c0fff0 100644 --- a/packages/core-app-api/src/routing/RoutingProvider.test.tsx +++ b/packages/core-app-api/src/routing/RoutingProvider.test.tsx @@ -124,6 +124,7 @@ const Extension5 = plugin.provide( const mockContext = { getComponents: () => ({ Progress: () => null } as any), getSystemIcon: jest.fn(), + getSystemIcons: jest.fn(), getPlugins: jest.fn(), }; diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index 49e0be3800..e0686596aa 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -149,6 +149,9 @@ export type AppComponents = { export type AppContext = { getPlugins(): BackstagePlugin_2[]; getSystemIcon(key: string): IconComponent_2 | undefined; + getSystemIcons(): AppIcons & { + [key in string]: IconComponent_2; + }; getComponents(): AppComponents; }; @@ -780,4 +783,8 @@ export function withApis(apis: TypesToApiRefs):

( (props: React_2.PropsWithChildren>): JSX.Element; displayName: string; }; + +// Warnings were encountered during analysis: +// +// /backstage/dist-types/packages/core-app-api/src/app/types.d.ts:277:5 - (ae-forgotten-export) The symbol "AppIcons" needs to be exported by the entry point index.d.ts ``` diff --git a/plugins/user-settings/src/components/General/UserSettingsAvailableIconsTable.tsx b/plugins/user-settings/src/components/General/UserSettingsAvailableIconsTable.tsx new file mode 100644 index 0000000000..d0e6034c66 --- /dev/null +++ b/plugins/user-settings/src/components/General/UserSettingsAvailableIconsTable.tsx @@ -0,0 +1,73 @@ +/* + * Copyright 2022 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, useApp } from '@backstage/core-plugin-api'; +import { Table, TableColumn } from '@backstage/core-components'; + +import { Box } from '@material-ui/core'; +import LanguageIcon from '@material-ui/icons/Language'; +import React from 'react'; + +type SystemIcon = { + key: string; + icon: IconComponent; +}; + +const columns: TableColumn[] = [ + { + title: 'Icon', + field: 'icon', + width: 'auto', + render: (row: Partial) => ( + + {row.icon ? : } + + ), + }, + { + title: 'Key', + field: 'key', + width: 'auto', + defaultSort: 'asc', + }, +]; + +export const UserSettingsAvailableIconsTable = () => { + const app = useApp(); + const systemIcons = app.getSystemIcons(); + const systemIconList: SystemIcon[] = []; + for (const icon in systemIcons) { + if (Object.prototype.hasOwnProperty.call(systemIcons, icon)) { + const sysIcon = { + key: icon, + icon: systemIcons[icon], + }; + systemIconList.push(sysIcon); + } + } + + return ( + + ); +}; diff --git a/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx b/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx index db7fdd7be6..e3a9567164 100644 --- a/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx @@ -18,6 +18,7 @@ import React from 'react'; import { UserSettingsProfileCard } from './UserSettingsProfileCard'; import { UserSettingsAppearanceCard } from './UserSettingsAppearanceCard'; import { UserSettingsIdentityCard } from './UserSettingsIdentityCard'; +import { UserSettingsAvailableIconsTable } from './UserSettingsAvailableIconsTable'; export const UserSettingsGeneral = () => { return ( @@ -31,6 +32,9 @@ export const UserSettingsGeneral = () => { + + + ); };