From 0562a7011fac67bae23273ea9a0734ac49b93d83 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 13 Feb 2024 09:57:17 +0100 Subject: [PATCH 1/9] feat(core-compat-api): create system icon abstraction Signed-off-by: Camila Belo --- .../src/components/SystemIcon.tsx | 52 +++++++++++++++++++ .../core-compat-api/src/components/index.ts | 17 ++++++ packages/core-compat-api/src/index.ts | 2 + 3 files changed, 71 insertions(+) create mode 100644 packages/core-compat-api/src/components/SystemIcon.tsx create mode 100644 packages/core-compat-api/src/components/index.ts diff --git a/packages/core-compat-api/src/components/SystemIcon.tsx b/packages/core-compat-api/src/components/SystemIcon.tsx new file mode 100644 index 0000000000..00c20da5db --- /dev/null +++ b/packages/core-compat-api/src/components/SystemIcon.tsx @@ -0,0 +1,52 @@ +/* + * Copyright 2024 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 { useApp } from '@backstage/core-plugin-api'; +import React from 'react'; +import { compatWrapper } from '../compatWrapper'; + +/** + * @public + * Props for the System Icon component. + */ +export type SystemIconProps = { + // The id of the system icon to render. + id: string; + // An optional fallback element to render when the system icon is not found. + fallback?: JSX.Element; +}; + +function SystemIcon(props: SystemIconProps) { + const { id, fallback = null } = props; + const app = useApp(); + const Component = app.getSystemIcon(id); + return Component ? : fallback; +} + +/** + * @public + * SystemIcon is a component that renders a system icon by its id. + * @example + * Rendering the "kind:api" icon: + * ```tsx + * + * ``` + */ +function CompatSystemIcon(props: SystemIconProps) { + return compatWrapper(); +} + +export { CompatSystemIcon as SystemIcon }; diff --git a/packages/core-compat-api/src/components/index.ts b/packages/core-compat-api/src/components/index.ts new file mode 100644 index 0000000000..e02ab727c6 --- /dev/null +++ b/packages/core-compat-api/src/components/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2024 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 { SystemIcon, type SystemIconProps } from './SystemIcon'; diff --git a/packages/core-compat-api/src/index.ts b/packages/core-compat-api/src/index.ts index 88e1892eac..3da227e554 100644 --- a/packages/core-compat-api/src/index.ts +++ b/packages/core-compat-api/src/index.ts @@ -17,6 +17,8 @@ export * from './compatWrapper'; export * from './apis'; +export * from './components'; + export { convertLegacyApp } from './convertLegacyApp'; export { convertLegacyRouteRef, From 76173663b7100bed891823f4dcfd2b3887641eec Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 13 Feb 2024 11:19:59 +0100 Subject: [PATCH 2/9] refactor(api-docs): use compat system icon Signed-off-by: Camila Belo --- plugins/api-docs/src/alpha.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/plugins/api-docs/src/alpha.tsx b/plugins/api-docs/src/alpha.tsx index 38b8db89c6..4835cd17ed 100644 --- a/plugins/api-docs/src/alpha.tsx +++ b/plugins/api-docs/src/alpha.tsx @@ -27,10 +27,10 @@ import { } from '@backstage/frontend-plugin-api'; import { + SystemIcon, compatWrapper, convertLegacyRouteRef, } from '@backstage/core-compat-api'; -import { useApp } from '@backstage/core-plugin-api'; import { createEntityCardExtension, @@ -46,16 +46,10 @@ import { defaultDefinitionWidgets } from './components/ApiDefinitionCard'; import { rootRoute, registerComponentRouteRef } from './routes'; import { apiDocsConfigRef } from './config'; -function ApiIcon() { - const app = useApp(); - const KindApiSystemIcon = app.getSystemIcon('kind:api')!; - return ; -} - const apiDocsNavItem = createNavItemExtension({ title: 'APIs', routeRef: convertLegacyRouteRef(rootRoute), - icon: () => compatWrapper(), + icon: () => , }); const apiDocsConfigApi = createApiExtension({ From f25e9ff96bbc087324d8887165704c850b974313 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 13 Feb 2024 11:21:05 +0100 Subject: [PATCH 3/9] docs(core-compat-api): update api reports Signed-off-by: Camila Belo --- packages/core-compat-api/api-report.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/core-compat-api/api-report.md b/packages/core-compat-api/api-report.md index be80ca50e9..828d3d9d6a 100644 --- a/packages/core-compat-api/api-report.md +++ b/packages/core-compat-api/api-report.md @@ -69,6 +69,15 @@ export class NoOpAnalyticsApi implements AnalyticsApi, AnalyticsApi_2 { captureEvent(_event: AnalyticsEvent | AnalyticsEvent_2): void; } +// @public +export function SystemIcon(props: SystemIconProps): React_2.JSX.Element; + +// @public +export type SystemIconProps = { + id: string; + fallback?: JSX.Element; +}; + // @public export type ToNewRouteRef = T extends RouteRef From 7854120d054f655aab84a5148a142e467527c1e4 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 13 Feb 2024 13:14:41 +0100 Subject: [PATCH 4/9] docs: create changeset files Signed-off-by: Camila Belo --- .changeset/friendly-news-sin.md | 5 +++++ .changeset/red-taxis-swim.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/friendly-news-sin.md create mode 100644 .changeset/red-taxis-swim.md diff --git a/.changeset/friendly-news-sin.md b/.changeset/friendly-news-sin.md new file mode 100644 index 0000000000..ca337370af --- /dev/null +++ b/.changeset/friendly-news-sin.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-compat-api': patch +--- + +Create an abstraction to consume legacy system icons in new system extensions. diff --git a/.changeset/red-taxis-swim.md b/.changeset/red-taxis-swim.md new file mode 100644 index 0000000000..7cd2428fac --- /dev/null +++ b/.changeset/red-taxis-swim.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Use the system icon compatibility component in the navigation item extension. From 77dc92eecc3a2d3863446273e376686a4e4acad9 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 15 Feb 2024 14:30:34 +0100 Subject: [PATCH 5/9] refactor(core-compat-api): apply review suggestions Signed-off-by: Camila Belo --- packages/core-compat-api/api-report.md | 8 +-- packages/core-compat-api/package.json | 1 + .../src/components/SystemIcon.test.tsx | 41 +++++++++++++++ .../src/components/SystemIcon.tsx | 52 ++++++++++++++----- plugins/api-docs/src/alpha.tsx | 2 +- yarn.lock | 1 + 6 files changed, 88 insertions(+), 17 deletions(-) create mode 100644 packages/core-compat-api/src/components/SystemIcon.test.tsx diff --git a/packages/core-compat-api/api-report.md b/packages/core-compat-api/api-report.md index 828d3d9d6a..82f13f1482 100644 --- a/packages/core-compat-api/api-report.md +++ b/packages/core-compat-api/api-report.md @@ -8,9 +8,11 @@ import { AnalyticsApi as AnalyticsApi_2 } from '@backstage/frontend-plugin-api'; import { AnalyticsEvent } from '@backstage/core-plugin-api'; import { AnalyticsEvent as AnalyticsEvent_2 } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/core-plugin-api'; +import { ComponentProps } from 'react'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { ExternalRouteRef as ExternalRouteRef_2 } from '@backstage/frontend-plugin-api'; import { FrontendFeature } from '@backstage/frontend-plugin-api'; +import { IconComponent } from '@backstage/core-plugin-api'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; @@ -73,9 +75,9 @@ export class NoOpAnalyticsApi implements AnalyticsApi, AnalyticsApi_2 { export function SystemIcon(props: SystemIconProps): React_2.JSX.Element; // @public -export type SystemIconProps = { - id: string; - fallback?: JSX.Element; +export type SystemIconProps = ComponentProps & { + keys: string | string[]; + Fallback?: IconComponent; }; // @public diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index ee70bc8089..21bf8ede49 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -45,6 +45,7 @@ "@backstage/plugin-catalog": "workspace:^", "@backstage/plugin-puppetdb": "workspace:^", "@backstage/plugin-stackstorm": "workspace:^", + "@backstage/test-utils": "workspace:^", "@oriflame/backstage-plugin-score-card": "^0.8.0", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^14.0.0" diff --git a/packages/core-compat-api/src/components/SystemIcon.test.tsx b/packages/core-compat-api/src/components/SystemIcon.test.tsx new file mode 100644 index 0000000000..ee3a20308d --- /dev/null +++ b/packages/core-compat-api/src/components/SystemIcon.test.tsx @@ -0,0 +1,41 @@ +/* + * Copyright 2024 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 React from 'react'; +import { screen } from '@testing-library/react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { SystemIcon } from './SystemIcon'; + +describe('SystemIcon', () => { + it('should render the correct system icon', async () => { + const { container } = await renderInTestApp(); + expect(container.querySelector('svg')).toBeDefined(); + }); + + it('should render the first found ico when multiple keys are provided', async () => { + const { container } = await renderInTestApp( + , + ); + expect(container.querySelector('svg')).toBeDefined(); + }); + + it('should render the fallback component when no system icon is found', async () => { + await renderInTestApp( +
Fallback Icon
} />, + ); + expect(screen.getByText('Fallback Icon')).toBeInTheDocument(); + }); +}); diff --git a/packages/core-compat-api/src/components/SystemIcon.tsx b/packages/core-compat-api/src/components/SystemIcon.tsx index 00c20da5db..7aa7110a97 100644 --- a/packages/core-compat-api/src/components/SystemIcon.tsx +++ b/packages/core-compat-api/src/components/SystemIcon.tsx @@ -14,26 +14,30 @@ * limitations under the License. */ -import { useApp } from '@backstage/core-plugin-api'; -import React from 'react'; +import React, { ComponentProps } from 'react'; +import { useApp, IconComponent } from '@backstage/core-plugin-api'; import { compatWrapper } from '../compatWrapper'; /** * @public - * Props for the System Icon component. + * Props for the SystemIcon component. */ -export type SystemIconProps = { - // The id of the system icon to render. - id: string; - // An optional fallback element to render when the system icon is not found. - fallback?: JSX.Element; +export type SystemIconProps = ComponentProps & { + // The id of the system icon to render, if provided as an array, the first icon found will be rendered. + keys: string | string[]; + // An optional fallback icon component to render when the system icon is not found. + // Default to () => null. + Fallback?: IconComponent; }; function SystemIcon(props: SystemIconProps) { - const { id, fallback = null } = props; + const { keys, Fallback = () => null, ...rest } = props; const app = useApp(); - const Component = app.getSystemIcon(id); - return Component ? : fallback; + for (const key of Array.isArray(keys) ? keys : [keys]) { + const Icon = app.getSystemIcon(key); + if (Icon) return ; + } + return ; } /** @@ -42,11 +46,33 @@ function SystemIcon(props: SystemIconProps) { * @example * Rendering the "kind:api" icon: * ```tsx - * + * + * ``` + * @example + * Providing multiple icon ids: + * ```tsx + * + * ``` + * @example + * Customizing the fallback icon: + * ```tsx + * + * ``` + * @example + * Customizing the icon font size: + * ```tsx + * * ``` */ function CompatSystemIcon(props: SystemIconProps) { - return compatWrapper(); + try { + // Check if the app context is available + useApp(); + return ; + } catch { + // Fallback to the compat wrapper if the app context is not available + return compatWrapper(); + } } export { CompatSystemIcon as SystemIcon }; diff --git a/plugins/api-docs/src/alpha.tsx b/plugins/api-docs/src/alpha.tsx index 4835cd17ed..fd28a59893 100644 --- a/plugins/api-docs/src/alpha.tsx +++ b/plugins/api-docs/src/alpha.tsx @@ -49,7 +49,7 @@ import { apiDocsConfigRef } from './config'; const apiDocsNavItem = createNavItemExtension({ title: 'APIs', routeRef: convertLegacyRouteRef(rootRoute), - icon: () => , + icon: () => , }); const apiDocsConfigApi = createApiExtension({ diff --git a/yarn.lock b/yarn.lock index 06edaceef5..c8deda0b1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3818,6 +3818,7 @@ __metadata: "@backstage/plugin-catalog": "workspace:^" "@backstage/plugin-puppetdb": "workspace:^" "@backstage/plugin-stackstorm": "workspace:^" + "@backstage/test-utils": "workspace:^" "@backstage/version-bridge": "workspace:^" "@oriflame/backstage-plugin-score-card": ^0.8.0 "@testing-library/jest-dom": ^6.0.0 From e4a8455a18fc0aad6d80dded70ca5c63c9d5f4dc Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 22 Feb 2024 16:10:39 +0100 Subject: [PATCH 6/9] Update packages/core-compat-api/src/components/SystemIcon.test.tsx Co-authored-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Signed-off-by: Camila Belo --- packages/core-compat-api/src/components/SystemIcon.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-compat-api/src/components/SystemIcon.test.tsx b/packages/core-compat-api/src/components/SystemIcon.test.tsx index ee3a20308d..12b8f41ea5 100644 --- a/packages/core-compat-api/src/components/SystemIcon.test.tsx +++ b/packages/core-compat-api/src/components/SystemIcon.test.tsx @@ -25,7 +25,7 @@ describe('SystemIcon', () => { expect(container.querySelector('svg')).toBeDefined(); }); - it('should render the first found ico when multiple keys are provided', async () => { + it('should render the first found icon when multiple keys are provided', async () => { const { container } = await renderInTestApp( , ); From f8b8e2fe6f0adcbba3c8eb43192e671df1473e67 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 26 Feb 2024 18:48:05 +0100 Subject: [PATCH 7/9] refactor: move to core components and rename it Signed-off-by: Camila Belo --- .changeset/friendly-news-sin.md | 4 +- .changeset/red-taxis-swim.md | 2 +- packages/core-compat-api/api-report.md | 11 --- packages/core-compat-api/package.json | 1 - .../src/components/SystemIcon.tsx | 78 ------------------- .../core-compat-api/src/components/index.ts | 17 ---- packages/core-compat-api/src/index.ts | 2 - packages/core-components/api-report.md | 14 +++- .../src/icons/icons.test.tsx} | 26 +++---- packages/core-components/src/icons/icons.tsx | 53 +++++++++---- plugins/api-docs/src/alpha.tsx | 4 +- yarn.lock | 1 - 12 files changed, 66 insertions(+), 147 deletions(-) delete mode 100644 packages/core-compat-api/src/components/SystemIcon.tsx delete mode 100644 packages/core-compat-api/src/components/index.ts rename packages/{core-compat-api/src/components/SystemIcon.test.tsx => core-components/src/icons/icons.test.tsx} (59%) diff --git a/.changeset/friendly-news-sin.md b/.changeset/friendly-news-sin.md index ca337370af..29843c54fb 100644 --- a/.changeset/friendly-news-sin.md +++ b/.changeset/friendly-news-sin.md @@ -1,5 +1,5 @@ --- -'@backstage/core-compat-api': patch +'@backstage/core-components': minor --- -Create an abstraction to consume legacy system icons in new system extensions. +Create a component abstraction to consume system icons. diff --git a/.changeset/red-taxis-swim.md b/.changeset/red-taxis-swim.md index 7cd2428fac..13c758d552 100644 --- a/.changeset/red-taxis-swim.md +++ b/.changeset/red-taxis-swim.md @@ -2,4 +2,4 @@ '@backstage/plugin-api-docs': patch --- -Use the system icon compatibility component in the navigation item extension. +Use the `AppIcon` component in the navigation item extension. diff --git a/packages/core-compat-api/api-report.md b/packages/core-compat-api/api-report.md index 82f13f1482..be80ca50e9 100644 --- a/packages/core-compat-api/api-report.md +++ b/packages/core-compat-api/api-report.md @@ -8,11 +8,9 @@ import { AnalyticsApi as AnalyticsApi_2 } from '@backstage/frontend-plugin-api'; import { AnalyticsEvent } from '@backstage/core-plugin-api'; import { AnalyticsEvent as AnalyticsEvent_2 } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/core-plugin-api'; -import { ComponentProps } from 'react'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { ExternalRouteRef as ExternalRouteRef_2 } from '@backstage/frontend-plugin-api'; import { FrontendFeature } from '@backstage/frontend-plugin-api'; -import { IconComponent } from '@backstage/core-plugin-api'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; @@ -71,15 +69,6 @@ export class NoOpAnalyticsApi implements AnalyticsApi, AnalyticsApi_2 { captureEvent(_event: AnalyticsEvent | AnalyticsEvent_2): void; } -// @public -export function SystemIcon(props: SystemIconProps): React_2.JSX.Element; - -// @public -export type SystemIconProps = ComponentProps & { - keys: string | string[]; - Fallback?: IconComponent; -}; - // @public export type ToNewRouteRef = T extends RouteRef diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 21bf8ede49..ee70bc8089 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -45,7 +45,6 @@ "@backstage/plugin-catalog": "workspace:^", "@backstage/plugin-puppetdb": "workspace:^", "@backstage/plugin-stackstorm": "workspace:^", - "@backstage/test-utils": "workspace:^", "@oriflame/backstage-plugin-score-card": "^0.8.0", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^14.0.0" diff --git a/packages/core-compat-api/src/components/SystemIcon.tsx b/packages/core-compat-api/src/components/SystemIcon.tsx deleted file mode 100644 index 7aa7110a97..0000000000 --- a/packages/core-compat-api/src/components/SystemIcon.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2024 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 React, { ComponentProps } from 'react'; -import { useApp, IconComponent } from '@backstage/core-plugin-api'; -import { compatWrapper } from '../compatWrapper'; - -/** - * @public - * Props for the SystemIcon component. - */ -export type SystemIconProps = ComponentProps & { - // The id of the system icon to render, if provided as an array, the first icon found will be rendered. - keys: string | string[]; - // An optional fallback icon component to render when the system icon is not found. - // Default to () => null. - Fallback?: IconComponent; -}; - -function SystemIcon(props: SystemIconProps) { - const { keys, Fallback = () => null, ...rest } = props; - const app = useApp(); - for (const key of Array.isArray(keys) ? keys : [keys]) { - const Icon = app.getSystemIcon(key); - if (Icon) return ; - } - return ; -} - -/** - * @public - * SystemIcon is a component that renders a system icon by its id. - * @example - * Rendering the "kind:api" icon: - * ```tsx - * - * ``` - * @example - * Providing multiple icon ids: - * ```tsx - * - * ``` - * @example - * Customizing the fallback icon: - * ```tsx - * - * ``` - * @example - * Customizing the icon font size: - * ```tsx - * - * ``` - */ -function CompatSystemIcon(props: SystemIconProps) { - try { - // Check if the app context is available - useApp(); - return ; - } catch { - // Fallback to the compat wrapper if the app context is not available - return compatWrapper(); - } -} - -export { CompatSystemIcon as SystemIcon }; diff --git a/packages/core-compat-api/src/components/index.ts b/packages/core-compat-api/src/components/index.ts deleted file mode 100644 index e02ab727c6..0000000000 --- a/packages/core-compat-api/src/components/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2024 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 { SystemIcon, type SystemIconProps } from './SystemIcon'; diff --git a/packages/core-compat-api/src/index.ts b/packages/core-compat-api/src/index.ts index 3da227e554..88e1892eac 100644 --- a/packages/core-compat-api/src/index.ts +++ b/packages/core-compat-api/src/index.ts @@ -17,8 +17,6 @@ export * from './compatWrapper'; export * from './apis'; -export * from './components'; - export { convertLegacyApp } from './convertLegacyApp'; export { convertLegacyRouteRef, diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 27b9c62653..7641386176 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -66,6 +66,15 @@ export type AlertDisplayProps = { transientTimeoutMs?: number; }; +// @public +export function AppIcon(props: AppIconProps): React_2.JSX.Element; + +// @public +export type AppIconProps = IconComponentProps & { + id: string; + Fallback?: IconComponent; +}; + // @public export const AutoLogout: (props: AutoLogoutProps) => JSX.Element | null; @@ -130,8 +139,6 @@ export type BreadcrumbsClickableTextClassKey = 'root'; // @public (undocumented) export type BreadcrumbsStyledBoxClassKey = 'root'; -// Warning: (ae-forgotten-export) The symbol "IconComponentProps" needs to be exported by the entry point index.d.ts -// // @public export function BrokenImageIcon(props: IconComponentProps): React_2.JSX.Element; @@ -541,6 +548,9 @@ export type HorizontalScrollGridClassKey = | 'buttonLeft' | 'buttonRight'; +// @public +export type IconComponentProps = ComponentProps; + // @public (undocumented) export function IconLinkVertical({ color, diff --git a/packages/core-compat-api/src/components/SystemIcon.test.tsx b/packages/core-components/src/icons/icons.test.tsx similarity index 59% rename from packages/core-compat-api/src/components/SystemIcon.test.tsx rename to packages/core-components/src/icons/icons.test.tsx index 12b8f41ea5..7cd54f37d0 100644 --- a/packages/core-compat-api/src/components/SystemIcon.test.tsx +++ b/packages/core-components/src/icons/icons.test.tsx @@ -17,24 +17,24 @@ import React from 'react'; import { screen } from '@testing-library/react'; import { renderInTestApp } from '@backstage/test-utils'; -import { SystemIcon } from './SystemIcon'; +import { AppIcon } from './icons'; -describe('SystemIcon', () => { +describe('AppIcon', () => { it('should render the correct system icon', async () => { - const { container } = await renderInTestApp(); - expect(container.querySelector('svg')).toBeDefined(); + await renderInTestApp(); + expect(screen.getByTestId('Api Icon')).toBeDefined(); }); - it('should render the first found icon when multiple keys are provided', async () => { - const { container } = await renderInTestApp( - , - ); - expect(container.querySelector('svg')).toBeDefined(); - }); - - it('should render the fallback component when no system icon is found', async () => { + it('should render the default fallback component', async () => { await renderInTestApp( -
Fallback Icon
} />, + , + ); + expect(screen.getByTestId('Fallback Icon')).toBeDefined(); + }); + + it('should render the custom fallback component', async () => { + await renderInTestApp( +
Fallback Icon
} />, ); expect(screen.getByText('Fallback Icon')).toBeInTheDocument(); }); diff --git a/packages/core-components/src/icons/icons.tsx b/packages/core-components/src/icons/icons.tsx index 8d22af4664..ca3b6fa76a 100644 --- a/packages/core-components/src/icons/icons.tsx +++ b/packages/core-components/src/icons/icons.tsx @@ -18,61 +18,80 @@ import { IconComponent, useApp } from '@backstage/core-plugin-api'; import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage'; import React, { ComponentProps } from 'react'; -type IconComponentProps = ComponentProps; +/** + * @public + * Props for the {@link @backstage/core-plugin-api#IconComponent} component. + */ +export type IconComponentProps = ComponentProps; -function useSystemIcon(key: string, props: IconComponentProps) { +/** + * @public + * Props for the {@link AppIcon} component. + */ +export type AppIconProps = IconComponentProps & { + // The key of the system icon to render. + id: string; + // An optional fallback icon component to render when the system icon is not found. + // Default to () => null. + Fallback?: IconComponent; +}; + +/** + * @public + * A component that renders a system icon by its id. + */ +export function AppIcon(props: AppIconProps) { + const { id: key, Fallback = MuiBrokenImageIcon, ...rest } = props; const app = useApp(); - const Icon = app.getSystemIcon(key); - return Icon ? : ; + const Icon = app.getSystemIcon(key) ?? Fallback; + return ; } // Should match the list of overridable system icon keys in @backstage/core-app-api /** * Broken Image Icon - * * @public - * */ export function BrokenImageIcon(props: IconComponentProps) { - return useSystemIcon('brokenImage', props); + return ; } /** @public */ export function CatalogIcon(props: IconComponentProps) { - return useSystemIcon('catalog', props); + return ; } /** @public */ export function ChatIcon(props: IconComponentProps) { - return useSystemIcon('chat', props); + return ; } /** @public */ export function DashboardIcon(props: IconComponentProps) { - return useSystemIcon('dashboard', props); + return ; } /** @public */ export function DocsIcon(props: IconComponentProps) { - return useSystemIcon('docs', props); + return ; } /** @public */ export function EmailIcon(props: IconComponentProps) { - return useSystemIcon('email', props); + return ; } /** @public */ export function GitHubIcon(props: IconComponentProps) { - return useSystemIcon('github', props); + return ; } /** @public */ export function GroupIcon(props: IconComponentProps) { - return useSystemIcon('group', props); + return ; } /** @public */ export function HelpIcon(props: IconComponentProps) { - return useSystemIcon('help', props); + return ; } /** @public */ export function UserIcon(props: IconComponentProps) { - return useSystemIcon('user', props); + return ; } /** @public */ export function WarningIcon(props: IconComponentProps) { - return useSystemIcon('warning', props); + return ; } diff --git a/plugins/api-docs/src/alpha.tsx b/plugins/api-docs/src/alpha.tsx index fd28a59893..29bb0bcfa9 100644 --- a/plugins/api-docs/src/alpha.tsx +++ b/plugins/api-docs/src/alpha.tsx @@ -27,7 +27,6 @@ import { } from '@backstage/frontend-plugin-api'; import { - SystemIcon, compatWrapper, convertLegacyRouteRef, } from '@backstage/core-compat-api'; @@ -45,11 +44,12 @@ import { import { defaultDefinitionWidgets } from './components/ApiDefinitionCard'; import { rootRoute, registerComponentRouteRef } from './routes'; import { apiDocsConfigRef } from './config'; +import { AppIcon } from '@backstage/core-components'; const apiDocsNavItem = createNavItemExtension({ title: 'APIs', routeRef: convertLegacyRouteRef(rootRoute), - icon: () => , + icon: () => , }); const apiDocsConfigApi = createApiExtension({ diff --git a/yarn.lock b/yarn.lock index c8deda0b1d..06edaceef5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3818,7 +3818,6 @@ __metadata: "@backstage/plugin-catalog": "workspace:^" "@backstage/plugin-puppetdb": "workspace:^" "@backstage/plugin-stackstorm": "workspace:^" - "@backstage/test-utils": "workspace:^" "@backstage/version-bridge": "workspace:^" "@oriflame/backstage-plugin-score-card": ^0.8.0 "@testing-library/jest-dom": ^6.0.0 From 789986094e53c5b2856cd6fb8cd44673ac4c132f Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 27 Feb 2024 09:57:34 +0100 Subject: [PATCH 8/9] fix(api-docs): wrap nav icon with compat wrapper Signed-off-by: Camila Belo --- plugins/api-docs/src/alpha.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/api-docs/src/alpha.tsx b/plugins/api-docs/src/alpha.tsx index 29bb0bcfa9..8cb43e9108 100644 --- a/plugins/api-docs/src/alpha.tsx +++ b/plugins/api-docs/src/alpha.tsx @@ -49,7 +49,7 @@ import { AppIcon } from '@backstage/core-components'; const apiDocsNavItem = createNavItemExtension({ title: 'APIs', routeRef: convertLegacyRouteRef(rootRoute), - icon: () => , + icon: () => compatWrapper(), }); const apiDocsConfigApi = createApiExtension({ From d42a5529292035003349eff2266fc44eed46c117 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 27 Feb 2024 10:00:53 +0100 Subject: [PATCH 9/9] fix: update core components changeset Signed-off-by: Camila Belo --- .changeset/friendly-news-sin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/friendly-news-sin.md b/.changeset/friendly-news-sin.md index 29843c54fb..af64190f9c 100644 --- a/.changeset/friendly-news-sin.md +++ b/.changeset/friendly-news-sin.md @@ -1,5 +1,5 @@ --- -'@backstage/core-components': minor +'@backstage/core-components': patch --- Create a component abstraction to consume system icons.