From 4105a78f984875d913202f44104638f5a8372808 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 2 Mar 2026 19:07:03 +0000 Subject: [PATCH] Style improvements on PluginHeader Signed-off-by: Charles de Dreuille --- .changeset/odd-laws-attack.md | 7 ++ .storybook/themes/spotify.css | 24 ++----- packages/ui/report.api.md | 8 ++- .../PluginHeader/PluginHeader.module.css | 23 ------- .../components/PluginHeader/PluginHeader.tsx | 41 +++++++++--- .../PluginHeader/PluginHeaderToolbar.tsx | 66 ------------------- .../src/components/PluginHeader/definition.ts | 38 +++-------- .../ui/src/components/PluginHeader/types.ts | 21 ------ 8 files changed, 61 insertions(+), 167 deletions(-) create mode 100644 .changeset/odd-laws-attack.md delete mode 100644 packages/ui/src/components/PluginHeader/PluginHeaderToolbar.tsx diff --git a/.changeset/odd-laws-attack.md b/.changeset/odd-laws-attack.md new file mode 100644 index 0000000000..034cb10be3 --- /dev/null +++ b/.changeset/odd-laws-attack.md @@ -0,0 +1,7 @@ +--- +'@backstage/ui': patch +--- + +Merged the internal `PluginHeaderToolbar` component into `PluginHeader`, removing the separate component and its associated types (`PluginHeaderToolbarOwnProps`, `PluginHeaderToolbarProps`) and definition (`PluginHeaderToolbarDefinition`). This is an internal refactor with no changes to the public API of `PluginHeader`. + +**Affected components:** PluginHeader diff --git a/.storybook/themes/spotify.css b/.storybook/themes/spotify.css index 89b71c48d3..3aa7580748 100644 --- a/.storybook/themes/spotify.css +++ b/.storybook/themes/spotify.css @@ -183,26 +183,16 @@ font-weight: var(--bui-font-weight-regular); } - .bui-HeaderToolbar { - padding-top: var(--bui-space-2); - padding-inline: var(--bui-space-2); - } - - .bui-HeaderToolbarWrapper { - border-radius: var(--bui-radius-3); - padding-inline: var(--bui-space-3); + .bui-PluginHeaderToolbarWrapper { + padding: 0; border: none; + height: 32px; } - .bui-HeaderToolbarControls { - right: calc(var(--bui-space-3) - 1px); - } - - .bui-HeaderTabsWrapper { - margin-top: var(--bui-space-2); - margin-inline: var(--bui-space-2); - border-radius: var(--bui-radius-3); - padding-inline: var(--bui-space-1); + .bui-PluginHeaderTabsWrapper { + margin: 0; + padding: 0; + margin-left: -8px; border: none; } diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index c77d044d46..572b6701d7 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -1745,7 +1745,13 @@ export const PluginHeaderDefinition: { }; readonly classNames: { readonly root: 'bui-PluginHeader'; - readonly tabsWrapper: 'bui-PluginHeaderTabsWrapper'; + readonly toolbar: 'bui-PluginHeaderToolbar'; + readonly toolbarWrapper: 'bui-PluginHeaderToolbarWrapper'; + readonly toolbarContent: 'bui-PluginHeaderToolbarContent'; + readonly toolbarControls: 'bui-PluginHeaderToolbarControls'; + readonly toolbarIcon: 'bui-PluginHeaderToolbarIcon'; + readonly toolbarName: 'bui-PluginHeaderToolbarName'; + readonly tabs: 'bui-PluginHeaderTabsWrapper'; }; readonly propDefs: { readonly icon: {}; diff --git a/packages/ui/src/components/PluginHeader/PluginHeader.module.css b/packages/ui/src/components/PluginHeader/PluginHeader.module.css index 037fd00722..9b6cc7d77d 100644 --- a/packages/ui/src/components/PluginHeader/PluginHeader.module.css +++ b/packages/ui/src/components/PluginHeader/PluginHeader.module.css @@ -17,26 +17,7 @@ @layer tokens, base, components, utilities; @layer components { - .bui-PluginHeader { - display: block; - } - - .bui-PluginHeaderToolbar { - &::before { - content: ''; - position: absolute; - top: 0; - left: 0px; - right: 0px; - height: 16px; - background-color: var(--bui-bg-app); - z-index: 0; - } - } - .bui-PluginHeaderToolbarWrapper { - position: relative; - z-index: 1; display: flex; flex-direction: row; align-items: center; @@ -77,10 +58,6 @@ } .bui-PluginHeaderToolbarControls { - position: absolute; - right: var(--bui-space-5); - top: 50%; - transform: translateY(-50%); display: flex; flex-direction: row; align-items: center; diff --git a/packages/ui/src/components/PluginHeader/PluginHeader.tsx b/packages/ui/src/components/PluginHeader/PluginHeader.tsx index ebdfee490d..6a6dee3016 100644 --- a/packages/ui/src/components/PluginHeader/PluginHeader.tsx +++ b/packages/ui/src/components/PluginHeader/PluginHeader.tsx @@ -15,13 +15,15 @@ */ import type { PluginHeaderProps } from './types'; -import { PluginHeaderToolbar } from './PluginHeaderToolbar'; import { Tabs, TabList, Tab } from '../Tabs'; import { useDefinition } from '../../hooks/useDefinition'; import { PluginHeaderDefinition } from './definition'; import { type NavigateOptions } from 'react-router-dom'; import { useRef } from 'react'; import { useIsomorphicLayoutEffect } from '../../hooks/useIsomorphicLayoutEffect'; +import { Link } from 'react-aria-components'; +import { RiShapesLine } from '@remixicon/react'; +import { Text } from '../Text'; declare module 'react-aria-components' { interface RouterConfig { @@ -49,6 +51,9 @@ export const PluginHeader = (props: PluginHeaderProps) => { const hasTabs = tabs && tabs.length > 0; const headerRef = useRef(null); + const toolbarWrapperRef = useRef(null); + const toolbarContentRef = useRef(null); + const toolbarControlsRef = useRef(null); useIsomorphicLayoutEffect(() => { const el = headerRef.current; @@ -82,17 +87,35 @@ export const PluginHeader = (props: PluginHeaderProps) => { }; }, []); + const titleContent = ( + <> +
{icon || }
+ {title || 'Your plugin'} + + ); + return (
- +
+
+
+ + {titleLink ? ( + + {titleContent} + + ) : ( +
{titleContent}
+ )} +
+
+
+ {customActions} +
+
+
{tabs && ( -
+
{tabs?.map(tab => ( diff --git a/packages/ui/src/components/PluginHeader/PluginHeaderToolbar.tsx b/packages/ui/src/components/PluginHeader/PluginHeaderToolbar.tsx deleted file mode 100644 index fe586e1a4d..0000000000 --- a/packages/ui/src/components/PluginHeader/PluginHeaderToolbar.tsx +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2025 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 { Link } from 'react-aria-components'; -import { useDefinition } from '../../hooks/useDefinition'; -import { PluginHeaderToolbarDefinition } from './definition'; -import { useRef } from 'react'; -import { RiShapesLine } from '@remixicon/react'; -import type { PluginHeaderToolbarProps } from './types'; -import { Text } from '../Text'; - -/** - * A component that renders the toolbar section of a plugin header. - * - * @internal - */ -export const PluginHeaderToolbar = (props: PluginHeaderToolbarProps) => { - const { ownProps } = useDefinition(PluginHeaderToolbarDefinition, props); - const { classes, icon, title, titleLink, customActions, hasTabs } = ownProps; - - // Refs for collision detection - const toolbarWrapperRef = useRef(null); - const toolbarContentRef = useRef(null); - const toolbarControlsRef = useRef(null); - - const titleContent = ( - <> -
{icon || }
- {title || 'Your plugin'} - - ); - - return ( -
-
-
- - {titleLink ? ( - - {titleContent} - - ) : ( -
{titleContent}
- )} -
-
-
- {customActions} -
-
-
- ); -}; diff --git a/packages/ui/src/components/PluginHeader/definition.ts b/packages/ui/src/components/PluginHeader/definition.ts index 176ceef352..c9a35aa956 100644 --- a/packages/ui/src/components/PluginHeader/definition.ts +++ b/packages/ui/src/components/PluginHeader/definition.ts @@ -15,10 +15,7 @@ */ import { defineComponent } from '../../hooks/useDefinition'; -import type { - PluginHeaderOwnProps, - PluginHeaderToolbarOwnProps, -} from './types'; +import type { PluginHeaderOwnProps } from './types'; import styles from './PluginHeader.module.css'; /** @@ -29,7 +26,13 @@ export const PluginHeaderDefinition = defineComponent()({ styles, classNames: { root: 'bui-PluginHeader', - tabsWrapper: 'bui-PluginHeaderTabsWrapper', + toolbar: 'bui-PluginHeaderToolbar', + toolbarWrapper: 'bui-PluginHeaderToolbarWrapper', + toolbarContent: 'bui-PluginHeaderToolbarContent', + toolbarControls: 'bui-PluginHeaderToolbarControls', + toolbarIcon: 'bui-PluginHeaderToolbarIcon', + toolbarName: 'bui-PluginHeaderToolbarName', + tabs: 'bui-PluginHeaderTabsWrapper', }, propDefs: { icon: {}, @@ -41,28 +44,3 @@ export const PluginHeaderDefinition = defineComponent()({ className: {}, }, }); - -/** - * Component definition for PluginHeaderToolbar - * @internal - */ -export const PluginHeaderToolbarDefinition = - defineComponent()({ - styles, - classNames: { - root: 'bui-PluginHeaderToolbar', - wrapper: 'bui-PluginHeaderToolbarWrapper', - content: 'bui-PluginHeaderToolbarContent', - controls: 'bui-PluginHeaderToolbarControls', - icon: 'bui-PluginHeaderToolbarIcon', - name: 'bui-PluginHeaderToolbarName', - }, - propDefs: { - icon: {}, - title: {}, - titleLink: {}, - customActions: {}, - hasTabs: {}, - className: {}, - }, - }); diff --git a/packages/ui/src/components/PluginHeader/types.ts b/packages/ui/src/components/PluginHeader/types.ts index f57fe82174..9048b33699 100644 --- a/packages/ui/src/components/PluginHeader/types.ts +++ b/packages/ui/src/components/PluginHeader/types.ts @@ -55,24 +55,3 @@ export interface HeaderTab { */ matchStrategy?: TabMatchStrategy; } - -/** - * Own props for the PluginHeaderToolbar component. - * - * @internal - */ -export interface PluginHeaderToolbarOwnProps { - icon?: PluginHeaderOwnProps['icon']; - title?: PluginHeaderOwnProps['title']; - titleLink?: PluginHeaderOwnProps['titleLink']; - customActions?: PluginHeaderOwnProps['customActions']; - hasTabs?: boolean; - className?: string; -} - -/** - * Props for the PluginHeaderToolbar component. - * - * @internal - */ -export interface PluginHeaderToolbarProps extends PluginHeaderToolbarOwnProps {}