From 0ec3c0e50e9225291f5ba095e8ee0e50f807cf42 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 17 Feb 2026 11:25:11 +0100 Subject: [PATCH] ui: rename Header to PluginHeader Renamed the Header component to PluginHeader for clarity, along with all associated exports (HeaderProps, HeaderDefinition) and CSS class names. Updated docs-ui documentation to match. Signed-off-by: Patrik Oldsberg --- .changeset/rename-header-to-plugin-header.md | 27 +++++++ .../{header => plugin-header}/components.tsx | 8 +-- .../{header => plugin-header}/page.mdx | 14 ++-- .../props-definition.tsx | 0 .../{header => plugin-header}/snippets.ts | 12 ++-- docs-ui/src/utils/changelogs/v0.9.0.ts | 13 +++- docs-ui/src/utils/data.ts | 4 +- docs-ui/src/utils/types.ts | 2 +- packages/ui/report.api.md | 70 +++++++++---------- .../components/FullPage/FullPage.stories.tsx | 10 +-- .../HeaderPage/HeaderPage.stories.tsx | 2 +- .../ui/src/components/HeaderPage/types.ts | 2 +- .../PluginHeader.module.css} | 16 ++--- .../PluginHeader.stories.tsx} | 28 ++++---- .../PluginHeader.tsx} | 17 ++--- .../PluginHeaderToolbar.tsx} | 12 ++-- .../{Header => PluginHeader}/definition.ts | 20 +++--- .../{Header => PluginHeader}/index.tsx | 6 +- .../{Header => PluginHeader}/types.ts | 16 ++--- .../SearchField/SearchField.stories.tsx | 6 +- packages/ui/src/definitions.ts | 2 +- packages/ui/src/index.ts | 2 +- 22 files changed, 162 insertions(+), 127 deletions(-) create mode 100644 .changeset/rename-header-to-plugin-header.md rename docs-ui/src/app/components/{header => plugin-header}/components.tsx (90%) rename docs-ui/src/app/components/{header => plugin-header}/page.mdx (81%) rename docs-ui/src/app/components/{header => plugin-header}/props-definition.tsx (100%) rename docs-ui/src/app/components/{header => plugin-header}/snippets.ts (84%) rename packages/ui/src/components/{Header/Header.module.css => PluginHeader/PluginHeader.module.css} (88%) rename packages/ui/src/components/{Header/Header.stories.tsx => PluginHeader/PluginHeader.stories.tsx} (96%) rename packages/ui/src/components/{Header/Header.tsx => PluginHeader/PluginHeader.tsx} (86%) rename packages/ui/src/components/{Header/HeaderToolbar.tsx => PluginHeader/PluginHeaderToolbar.tsx} (88%) rename packages/ui/src/components/{Header => PluginHeader}/definition.ts (61%) rename packages/ui/src/components/{Header => PluginHeader}/index.tsx (78%) rename packages/ui/src/components/{Header => PluginHeader}/types.ts (80%) diff --git a/.changeset/rename-header-to-plugin-header.md b/.changeset/rename-header-to-plugin-header.md new file mode 100644 index 0000000000..5e4bfcbb02 --- /dev/null +++ b/.changeset/rename-header-to-plugin-header.md @@ -0,0 +1,27 @@ +--- +'@backstage/ui': minor +--- + +**BREAKING**: Renamed the `Header` component to `PluginHeader` for clarity. + +The following exports have been renamed: + +- `Header` → `PluginHeader` +- `HeaderProps` → `PluginHeaderProps` +- `HeaderDefinition` → `PluginHeaderDefinition` + +The `HeaderTab` type is unchanged as it is shared with `HeaderPage`. + +CSS class names have been updated from `bui-Header*` to `bui-PluginHeader*`. + +**Migration:** + +```diff +-import { Header, HeaderDefinition } from '@backstage/ui'; ++import { PluginHeader, PluginHeaderDefinition } from '@backstage/ui'; + +-
++ +``` + +**Affected components:** plugin-header diff --git a/docs-ui/src/app/components/header/components.tsx b/docs-ui/src/app/components/plugin-header/components.tsx similarity index 90% rename from docs-ui/src/app/components/header/components.tsx rename to docs-ui/src/app/components/plugin-header/components.tsx index 80a4d1dd6e..b3d831e718 100644 --- a/docs-ui/src/app/components/header/components.tsx +++ b/docs-ui/src/app/components/plugin-header/components.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Header } from '../../../../../packages/ui/src/components/Header/Header'; +import { PluginHeader } from '../../../../../packages/ui/src/components/PluginHeader/PluginHeader'; import { HeaderPage } from '../../../../../packages/ui/src/components/HeaderPage/HeaderPage'; import { ButtonIcon } from '../../../../../packages/ui/src/components/ButtonIcon/ButtonIcon'; import { Button } from '../../../../../packages/ui/src/components/Button/Button'; @@ -27,7 +27,7 @@ const tabs2 = [ export const WithAllOptionsAndTabs = () => ( -
( export const WithAllOptions = () => ( -
( export const WithHeaderPage = () => ( <> -
+ @@ -36,22 +36,22 @@ import { ChangelogComponent } from '@/components/ChangelogComponent'; ## Examples -### Simple header +### Simple plugin header } code={simple} open /> -### Header with tabs +### Plugin header with tabs Tabs use React Router and highlight automatically based on the current route. } code={withTabs} open /> -### Header with HeaderPage +### Plugin header with HeaderPage Combine with [HeaderPage](/components/header-page) for multi-level navigation. } code={withHeaderPage} open /> - + - + diff --git a/docs-ui/src/app/components/header/props-definition.tsx b/docs-ui/src/app/components/plugin-header/props-definition.tsx similarity index 100% rename from docs-ui/src/app/components/header/props-definition.tsx rename to docs-ui/src/app/components/plugin-header/props-definition.tsx diff --git a/docs-ui/src/app/components/header/snippets.ts b/docs-ui/src/app/components/plugin-header/snippets.ts similarity index 84% rename from docs-ui/src/app/components/header/snippets.ts rename to docs-ui/src/app/components/plugin-header/snippets.ts index b83f1b68b2..4e9518af73 100644 --- a/docs-ui/src/app/components/header/snippets.ts +++ b/docs-ui/src/app/components/plugin-header/snippets.ts @@ -1,8 +1,8 @@ -export const usage = `import { Header } from '@backstage/ui'; +export const usage = `import { PluginHeader } from '@backstage/ui'; -
`; +`; -export const defaultSnippet = `
`; -export const simple = `
`; -export const withTabs = `
`; -export const withHeaderPage = `
JSX_2.Element; - -// @public -export const HeaderDefinition: { - readonly classNames: { - readonly root: 'bui-Header'; - readonly toolbar: 'bui-HeaderToolbar'; - readonly toolbarWrapper: 'bui-HeaderToolbarWrapper'; - readonly toolbarContent: 'bui-HeaderToolbarContent'; - readonly toolbarControls: 'bui-HeaderToolbarControls'; - readonly toolbarIcon: 'bui-HeaderToolbarIcon'; - readonly toolbarName: 'bui-HeaderToolbarName'; - readonly tabsWrapper: 'bui-HeaderTabsWrapper'; - }; -}; - // @public export const HeaderPage: (props: HeaderPageProps) => JSX_2.Element; @@ -1206,24 +1189,6 @@ export interface HeaderPageProps { title?: string; } -// @public -export interface HeaderProps { - // (undocumented) - className?: string; - // (undocumented) - customActions?: React.ReactNode; - // (undocumented) - icon?: React.ReactNode; - // (undocumented) - onTabSelectionChange?: TabsProps_2['onSelectionChange']; - // (undocumented) - tabs?: HeaderTab[]; - // (undocumented) - title?: string; - // (undocumented) - titleLink?: string; -} - // @public export interface HeaderTab { // (undocumented) @@ -1574,6 +1539,41 @@ export interface PasswordFieldProps size?: 'small' | 'medium' | Partial>; } +// @public +export const PluginHeader: (props: PluginHeaderProps) => JSX_2.Element; + +// @public +export const PluginHeaderDefinition: { + readonly classNames: { + readonly root: 'bui-PluginHeader'; + 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 tabsWrapper: 'bui-PluginHeaderTabsWrapper'; + }; +}; + +// @public +export interface PluginHeaderProps { + // (undocumented) + className?: string; + // (undocumented) + customActions?: React.ReactNode; + // (undocumented) + icon?: React.ReactNode; + // (undocumented) + onTabSelectionChange?: TabsProps_2['onSelectionChange']; + // (undocumented) + tabs?: HeaderTab[]; + // (undocumented) + title?: string; + // (undocumented) + titleLink?: string; +} + // @public export const Popover: ForwardRefExoticComponent< PopoverProps & RefAttributes diff --git a/packages/ui/src/components/FullPage/FullPage.stories.tsx b/packages/ui/src/components/FullPage/FullPage.stories.tsx index 8ac1b731e9..05169ca8ea 100644 --- a/packages/ui/src/components/FullPage/FullPage.stories.tsx +++ b/packages/ui/src/components/FullPage/FullPage.stories.tsx @@ -17,10 +17,10 @@ import preview from '../../../../../.storybook/preview'; import type { StoryFn } from '@storybook/react-vite'; import { FullPage } from './FullPage'; -import { Header } from '../Header'; +import { PluginHeader } from '../PluginHeader'; import { Container } from '../Container'; import { Text } from '../Text'; -import type { HeaderTab } from '../Header/types'; +import type { HeaderTab } from '../PluginHeader/types'; import { MemoryRouter } from 'react-router-dom'; const meta = preview.meta({ @@ -57,7 +57,7 @@ export const Default = meta.story({ decorators: [withRouter], render: () => ( <> -
+ @@ -73,7 +73,7 @@ export const WithScrollableContent = meta.story({ decorators: [withRouter], render: () => ( <> -
+ @@ -94,7 +94,7 @@ export const WithTabs = meta.story({ decorators: [withRouter], render: () => ( <> -
+ diff --git a/packages/ui/src/components/HeaderPage/HeaderPage.stories.tsx b/packages/ui/src/components/HeaderPage/HeaderPage.stories.tsx index 8242b5cd6a..0c027477e3 100644 --- a/packages/ui/src/components/HeaderPage/HeaderPage.stories.tsx +++ b/packages/ui/src/components/HeaderPage/HeaderPage.stories.tsx @@ -17,7 +17,7 @@ import preview from '../../../../../.storybook/preview'; import type { StoryFn } from '@storybook/react-vite'; import { HeaderPage } from './HeaderPage'; -import type { HeaderTab } from '../Header/types'; +import type { HeaderTab } from '../PluginHeader/types'; import { MemoryRouter } from 'react-router-dom'; import { Button, diff --git a/packages/ui/src/components/HeaderPage/types.ts b/packages/ui/src/components/HeaderPage/types.ts index 1271461989..72d365968e 100644 --- a/packages/ui/src/components/HeaderPage/types.ts +++ b/packages/ui/src/components/HeaderPage/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { HeaderTab } from '../Header/types'; +import type { HeaderTab } from '../PluginHeader/types'; /** * Props for the main HeaderPage component. diff --git a/packages/ui/src/components/Header/Header.module.css b/packages/ui/src/components/PluginHeader/PluginHeader.module.css similarity index 88% rename from packages/ui/src/components/Header/Header.module.css rename to packages/ui/src/components/PluginHeader/PluginHeader.module.css index f945b70bf0..037fd00722 100644 --- a/packages/ui/src/components/Header/Header.module.css +++ b/packages/ui/src/components/PluginHeader/PluginHeader.module.css @@ -17,11 +17,11 @@ @layer tokens, base, components, utilities; @layer components { - .bui-Header { + .bui-PluginHeader { display: block; } - .bui-HeaderToolbar { + .bui-PluginHeaderToolbar { &::before { content: ''; position: absolute; @@ -34,7 +34,7 @@ } } - .bui-HeaderToolbarWrapper { + .bui-PluginHeaderToolbarWrapper { position: relative; z-index: 1; display: flex; @@ -48,14 +48,14 @@ height: 52px; } - .bui-HeaderToolbarContent { + .bui-PluginHeaderToolbarContent { display: flex; flex-direction: row; align-items: center; gap: var(--bui-space-2); } - .bui-HeaderToolbarName { + .bui-PluginHeaderToolbarName { display: flex; flex-direction: row; align-items: center; @@ -65,7 +65,7 @@ flex-shrink: 0; } - .bui-HeaderToolbarIcon { + .bui-PluginHeaderToolbarIcon { width: 16px; height: 16px; color: var(--bui-fg-primary); @@ -76,7 +76,7 @@ } } - .bui-HeaderToolbarControls { + .bui-PluginHeaderToolbarControls { position: absolute; right: var(--bui-space-5); top: 50%; @@ -87,7 +87,7 @@ gap: var(--bui-space-2); } - .bui-HeaderTabsWrapper { + .bui-PluginHeaderTabsWrapper { padding-inline: var(--bui-space-3); border-bottom: 1px solid var(--bui-border-1); background-color: var(--bui-bg-neutral-1); diff --git a/packages/ui/src/components/Header/Header.stories.tsx b/packages/ui/src/components/PluginHeader/PluginHeader.stories.tsx similarity index 96% rename from packages/ui/src/components/Header/Header.stories.tsx rename to packages/ui/src/components/PluginHeader/PluginHeader.stories.tsx index d48c1a7ceb..91cb69b2a8 100644 --- a/packages/ui/src/components/Header/Header.stories.tsx +++ b/packages/ui/src/components/PluginHeader/PluginHeader.stories.tsx @@ -16,7 +16,7 @@ import preview from '../../../../../.storybook/preview'; import type { StoryFn } from '@storybook/react-vite'; -import { Header } from './Header'; +import { PluginHeader } from './PluginHeader'; import type { HeaderTab } from './types'; import { Button, @@ -38,8 +38,8 @@ import { import { HeaderPageBreadcrumb } from '../HeaderPage/types'; const meta = preview.meta({ - title: 'Backstage UI/Header', - component: Header, + title: 'Backstage UI/PluginHeader', + component: PluginHeader, parameters: { layout: 'fullscreen', }, @@ -213,7 +213,7 @@ export const WithCustomActions = meta.story({ args: {}, decorators: [withRouter], render: args => ( -
@@ -253,7 +253,7 @@ export const WithHeaderPage = meta.story({ decorators: [withRouter], render: args => ( <> -
@@ -277,7 +277,7 @@ export const WithLayout = meta.story({ decorators: layoutDecorator, render: args => ( <> -
+ ( <> -
+ ), @@ -306,7 +306,7 @@ export const WithEverything = meta.story({ decorators: layoutDecorator, render: args => ( <> -
@@ -336,7 +336,7 @@ export const WithMockedURLCampaigns = meta.story({ }, render: args => ( -
+ Current URL is mocked to be: /campaigns @@ -356,7 +356,7 @@ export const WithMockedURLIntegrations = meta.story({ }, render: args => ( -
+ Current URL is mocked to be: /integrations @@ -376,7 +376,7 @@ export const WithMockedURLNoMatch = meta.story({ }, render: args => ( -
+ Current URL is mocked to be: /some-other-page @@ -424,7 +424,7 @@ export const WithTabsMatchingStrategies = meta.story({ }, render: args => ( -
+ Current URL: /mentorship/events @@ -477,7 +477,7 @@ export const WithTabsExactMatching = meta.story({ }, render: args => ( -
+ Current URL: /mentorship/events @@ -519,7 +519,7 @@ export const WithTabsPrefixMatchingDeep = meta.story({ }, render: args => ( -
+ Current URL: /catalog/users/john/details diff --git a/packages/ui/src/components/Header/Header.tsx b/packages/ui/src/components/PluginHeader/PluginHeader.tsx similarity index 86% rename from packages/ui/src/components/Header/Header.tsx rename to packages/ui/src/components/PluginHeader/PluginHeader.tsx index 09f29b95c5..2ed663d61c 100644 --- a/packages/ui/src/components/Header/Header.tsx +++ b/packages/ui/src/components/PluginHeader/PluginHeader.tsx @@ -14,15 +14,15 @@ * limitations under the License. */ -import type { HeaderProps } from './types'; -import { HeaderToolbar } from './HeaderToolbar'; +import type { PluginHeaderProps } from './types'; +import { PluginHeaderToolbar } from './PluginHeaderToolbar'; import { Tabs, TabList, Tab } from '../Tabs'; import { useStyles } from '../../hooks/useStyles'; -import { HeaderDefinition } from './definition'; +import { PluginHeaderDefinition } from './definition'; import { type NavigateOptions } from 'react-router-dom'; import { useRef } from 'react'; import { useIsomorphicLayoutEffect } from '../../hooks/useIsomorphicLayoutEffect'; -import styles from './Header.module.css'; +import styles from './PluginHeader.module.css'; import clsx from 'clsx'; declare module 'react-aria-components' { @@ -32,12 +32,13 @@ declare module 'react-aria-components' { } /** - * A component that renders a toolbar. + * A component that renders a plugin header with icon, title, custom actions, + * and navigation tabs. * * @public */ -export const Header = (props: HeaderProps) => { - const { classNames, cleanedProps } = useStyles(HeaderDefinition, props); +export const PluginHeader = (props: PluginHeaderProps) => { + const { classNames, cleanedProps } = useStyles(PluginHeaderDefinition, props); const { className, tabs, @@ -88,7 +89,7 @@ export const Header = (props: HeaderProps) => { ref={headerRef} className={clsx(classNames.root, styles[classNames.root], className)} > - { - const { classNames, cleanedProps } = useStyles(HeaderDefinition, props); +export const PluginHeaderToolbar = (props: PluginHeaderToolbarProps) => { + const { classNames, cleanedProps } = useStyles(PluginHeaderDefinition, props); const { className, icon, title, titleLink, customActions, hasTabs } = cleanedProps; diff --git a/packages/ui/src/components/Header/definition.ts b/packages/ui/src/components/PluginHeader/definition.ts similarity index 61% rename from packages/ui/src/components/Header/definition.ts rename to packages/ui/src/components/PluginHeader/definition.ts index 6937bc5347..39de7a0443 100644 --- a/packages/ui/src/components/Header/definition.ts +++ b/packages/ui/src/components/PluginHeader/definition.ts @@ -17,18 +17,18 @@ import type { ComponentDefinition } from '../../types'; /** - * Component definition for Header + * Component definition for PluginHeader * @public */ -export const HeaderDefinition = { +export const PluginHeaderDefinition = { classNames: { - root: 'bui-Header', - toolbar: 'bui-HeaderToolbar', - toolbarWrapper: 'bui-HeaderToolbarWrapper', - toolbarContent: 'bui-HeaderToolbarContent', - toolbarControls: 'bui-HeaderToolbarControls', - toolbarIcon: 'bui-HeaderToolbarIcon', - toolbarName: 'bui-HeaderToolbarName', - tabsWrapper: 'bui-HeaderTabsWrapper', + root: 'bui-PluginHeader', + toolbar: 'bui-PluginHeaderToolbar', + toolbarWrapper: 'bui-PluginHeaderToolbarWrapper', + toolbarContent: 'bui-PluginHeaderToolbarContent', + toolbarControls: 'bui-PluginHeaderToolbarControls', + toolbarIcon: 'bui-PluginHeaderToolbarIcon', + toolbarName: 'bui-PluginHeaderToolbarName', + tabsWrapper: 'bui-PluginHeaderTabsWrapper', }, } as const satisfies ComponentDefinition; diff --git a/packages/ui/src/components/Header/index.tsx b/packages/ui/src/components/PluginHeader/index.tsx similarity index 78% rename from packages/ui/src/components/Header/index.tsx rename to packages/ui/src/components/PluginHeader/index.tsx index 60685cfaa6..306910f516 100644 --- a/packages/ui/src/components/Header/index.tsx +++ b/packages/ui/src/components/PluginHeader/index.tsx @@ -14,6 +14,6 @@ * limitations under the License. */ -export { Header } from './Header'; -export { HeaderDefinition } from './definition'; -export type { HeaderProps, HeaderTab } from './types'; +export { PluginHeader } from './PluginHeader'; +export { PluginHeaderDefinition } from './definition'; +export type { PluginHeaderProps, HeaderTab } from './types'; diff --git a/packages/ui/src/components/Header/types.ts b/packages/ui/src/components/PluginHeader/types.ts similarity index 80% rename from packages/ui/src/components/Header/types.ts rename to packages/ui/src/components/PluginHeader/types.ts index 57dd1088f5..21021cdbaf 100644 --- a/packages/ui/src/components/Header/types.ts +++ b/packages/ui/src/components/PluginHeader/types.ts @@ -18,11 +18,11 @@ import { TabsProps } from 'react-aria-components'; import { TabMatchStrategy } from '../Tabs'; /** - * Props for the main Header component. + * Props for the {@link PluginHeader} component. * * @public */ -export interface HeaderProps { +export interface PluginHeaderProps { icon?: React.ReactNode; title?: string; titleLink?: string; @@ -50,15 +50,15 @@ export interface HeaderTab { } /** - * Props for the HeaderToolbar component. + * Props for the PluginHeaderToolbar component. * * @internal */ -export interface HeaderToolbarProps { - icon?: HeaderProps['icon']; - title?: HeaderProps['title']; - titleLink?: HeaderProps['titleLink']; - customActions?: HeaderProps['customActions']; +export interface PluginHeaderToolbarProps { + icon?: PluginHeaderProps['icon']; + title?: PluginHeaderProps['title']; + titleLink?: PluginHeaderProps['titleLink']; + customActions?: PluginHeaderProps['customActions']; hasTabs?: boolean; className?: string; } diff --git a/packages/ui/src/components/SearchField/SearchField.stories.tsx b/packages/ui/src/components/SearchField/SearchField.stories.tsx index 2e07e742ad..7b75cfd24a 100644 --- a/packages/ui/src/components/SearchField/SearchField.stories.tsx +++ b/packages/ui/src/components/SearchField/SearchField.stories.tsx @@ -23,7 +23,7 @@ import { FieldLabel } from '../FieldLabel'; import { ButtonIcon } from '../ButtonIcon'; import { RiCactusLine, RiEBike2Line } from '@remixicon/react'; import { Button } from '../Button'; -import { Header } from '../Header'; +import { PluginHeader } from '../PluginHeader'; import { MemoryRouter } from 'react-router-dom'; const meta = preview.meta({ @@ -192,7 +192,7 @@ export const InHeader = meta.story({ ], render: args => ( <> -
@@ -229,7 +229,7 @@ export const StartCollapsedInHeader = meta.story({ ], render: args => ( <> -
diff --git a/packages/ui/src/definitions.ts b/packages/ui/src/definitions.ts index 030584fbb3..59003e8070 100644 --- a/packages/ui/src/definitions.ts +++ b/packages/ui/src/definitions.ts @@ -42,7 +42,7 @@ export { GridDefinition, GridItemDefinition, } from './components/Grid/definition'; -export { HeaderDefinition } from './components/Header/definition'; +export { PluginHeaderDefinition } from './components/PluginHeader/definition'; export { HeaderPageDefinition } from './components/HeaderPage/definition'; export { LinkDefinition } from './components/Link/definition'; export { MenuDefinition } from './components/Menu/definition'; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 40f37aba41..c70aa9d325 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -35,7 +35,7 @@ export * from './components/Button'; export * from './components/Card'; export * from './components/Dialog'; export * from './components/FieldLabel'; -export * from './components/Header'; +export * from './components/PluginHeader'; export * from './components/HeaderPage'; export * from './components/ButtonIcon'; export * from './components/ButtonLink';