From 5eb6b8a7bcfb58b5e33e87dd2e0a3fd84e429ce1 Mon Sep 17 00:00:00 2001 From: Craig Tracey Date: Thu, 26 Oct 2023 18:57:55 -0400 Subject: [PATCH] Add core nav logo extension Customizing the look and feel of Backstage sometimes requires altering the Core Nav logos. This feature introduces a createNavLogoExtension so that users may utilize their own logos. Signed-off-by: Craig Tracey --- .changeset/brave-parents-stare.md | 6 +++ .../src/extensions/CoreNav.tsx | 18 ++++++-- packages/frontend-plugin-api/api-report.md | 7 +++ .../createNavLogoExtension.test.tsx | 44 +++++++++++++++++++ .../src/extensions/createNavLogoExtension.tsx | 44 +++++++++++++++++++ .../src/wiring/coreExtensionData.ts | 7 +++ .../frontend-plugin-api/src/wiring/index.ts | 6 ++- 7 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 .changeset/brave-parents-stare.md create mode 100644 packages/frontend-plugin-api/src/extensions/createNavLogoExtension.test.tsx create mode 100644 packages/frontend-plugin-api/src/extensions/createNavLogoExtension.tsx diff --git a/.changeset/brave-parents-stare.md b/.changeset/brave-parents-stare.md new file mode 100644 index 0000000000..bb98799456 --- /dev/null +++ b/.changeset/brave-parents-stare.md @@ -0,0 +1,6 @@ +--- +'@backstage/frontend-plugin-api': patch +'@backstage/frontend-app-api': patch +--- + +Added the nav logo extension for customization of sidebar logo diff --git a/packages/frontend-app-api/src/extensions/CoreNav.tsx b/packages/frontend-app-api/src/extensions/CoreNav.tsx index 37ce50276b..8d0d4ea5de 100644 --- a/packages/frontend-app-api/src/extensions/CoreNav.tsx +++ b/packages/frontend-app-api/src/extensions/CoreNav.tsx @@ -19,6 +19,7 @@ import { createExtension, coreExtensionData, createExtensionInput, + LogoElements, NavTarget, useRouteRef, } from '@backstage/frontend-plugin-api'; @@ -51,14 +52,16 @@ const useSidebarLogoStyles = makeStyles({ }, }); -const SidebarLogo = () => { +const SidebarLogo = (props: LogoElements) => { const classes = useSidebarLogoStyles(); const { isOpen } = useSidebarOpenState(); return (
- {isOpen ? : } + {isOpen + ? props?.logoFull ?? + : props?.logoIcon ?? }
); @@ -78,6 +81,15 @@ export const CoreNav = createExtension({ items: createExtensionInput({ target: coreExtensionData.navTarget, }), + logos: createExtensionInput( + { + elements: coreExtensionData.logoElements, + }, + { + singleton: true, + optional: true, + }, + ), }, output: { element: coreExtensionData.reactElement, @@ -86,7 +98,7 @@ export const CoreNav = createExtension({ return { element: ( - + {inputs.items.map((item, index) => ( diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index dc3de16065..6d4b327dd5 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -262,6 +262,7 @@ export const coreExtensionData: { routeRef: ConfigurableExtensionDataRef, {}>; navTarget: ConfigurableExtensionDataRef; theme: ConfigurableExtensionDataRef; + logoElements: ConfigurableExtensionDataRef; }; // @public (undocumented) @@ -641,6 +642,12 @@ export { IdentityApi }; export { identityApiRef }; +// @public (undocumented) +export type LogoElements = { + logoIcon?: JSX_2.Element; + logoFull?: JSX_2.Element; +}; + export { microsoftAuthApiRef }; // @public (undocumented) diff --git a/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.test.tsx b/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.test.tsx new file mode 100644 index 0000000000..93efadb0e1 --- /dev/null +++ b/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.test.tsx @@ -0,0 +1,44 @@ +/* + * 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 React from 'react'; +import { createNavLogoExtension } from './createNavLogoExtension'; + +jest.mock('@backstage/core-plugin-api', () => ({ + ...jest.requireActual('@backstage/core-plugin-api'), +})); + +describe('createNavLogoExtension', () => { + it('creates the extension properly', () => { + expect( + createNavLogoExtension({ + id: 'test', + logoFull:
Logo Full
, + logoIcon:
Logo Icon
, + }), + ).toEqual({ + $$type: '@backstage/Extension', + id: 'test', + attachTo: { id: 'core.nav', input: 'logos' }, + disabled: false, + inputs: {}, + output: { + logos: expect.anything(), + }, + factory: expect.any(Function), + }); + }); +}); diff --git a/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.tsx b/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.tsx new file mode 100644 index 0000000000..5cfff81a9d --- /dev/null +++ b/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.tsx @@ -0,0 +1,44 @@ +/* + * 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 { coreExtensionData, createExtension } from '../wiring'; + +/** + * Helper for creating extensions for a nav logos. + * @public + */ +export function createNavLogoExtension(options: { + id: string; + logoIcon: JSX.Element; + logoFull: JSX.Element; +}) { + const { id, logoIcon, logoFull } = options; + return createExtension({ + id, + attachTo: { id: 'core.nav', input: 'logos' }, + output: { + logos: coreExtensionData.logoElements, + }, + factory: () => { + return { + logos: { + logoIcon, + logoFull, + }, + }; + }, + }); +} diff --git a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts index 023eab57e1..9ffdb835ba 100644 --- a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts +++ b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts @@ -30,6 +30,12 @@ export type NavTarget = { routeRef: RouteRef; }; +/** @public */ +export type LogoElements = { + logoIcon?: JSX.Element; + logoFull?: JSX.Element; +}; + /** @public */ export const coreExtensionData = { reactElement: createExtensionDataRef('core.reactElement'), @@ -38,4 +44,5 @@ export const coreExtensionData = { routeRef: createExtensionDataRef('core.routing.ref'), navTarget: createExtensionDataRef('core.nav.target'), theme: createExtensionDataRef('core.theme'), + logoElements: createExtensionDataRef('core.logos'), }; diff --git a/packages/frontend-plugin-api/src/wiring/index.ts b/packages/frontend-plugin-api/src/wiring/index.ts index ec685d6c81..19e11185e9 100644 --- a/packages/frontend-plugin-api/src/wiring/index.ts +++ b/packages/frontend-plugin-api/src/wiring/index.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -export { coreExtensionData, type NavTarget } from './coreExtensionData'; +export { + coreExtensionData, + type LogoElements, + type NavTarget, +} from './coreExtensionData'; export { createExtension, type Extension,