Merge branch 'master' into range-slider-component

This commit is contained in:
root
2026-03-10 20:29:25 +05:30
401 changed files with 12549 additions and 5799 deletions
@@ -62,6 +62,11 @@ export const buttonLinkPropDefs: Record<string, PropDef> = {
</>
),
},
noTrack: {
type: 'boolean',
description:
'Suppresses the automatic analytics click event, e.g. if you already have custom tracking.',
},
onSurface: {
type: 'enum',
values: ['0', '1', '2', '3', 'danger', 'warning', 'success', 'auto'],
@@ -1,6 +1,6 @@
'use client';
import { HeaderPage } from '../../../../../packages/ui/src/components/HeaderPage/HeaderPage';
import { Header } from '../../../../../packages/ui/src/components/Header/Header';
import { Button } from '../../../../../packages/ui/src/components/Button/Button';
import { ButtonIcon } from '../../../../../packages/ui/src/components/ButtonIcon/ButtonIcon';
import {
@@ -31,7 +31,7 @@ const breadcrumbs = [
export const WithEverything = () => (
<MemoryRouter>
<HeaderPage
<Header
title="Page Title"
tabs={tabs.slice(0, 2)}
breadcrumbs={breadcrumbs.slice(0, 2)}
@@ -47,28 +47,25 @@ export const WithEverything = () => (
export const WithLongBreadcrumbs = () => (
<MemoryRouter>
<HeaderPage title="Page Title" breadcrumbs={breadcrumbs.slice(0, 2)} />
<Header title="Page Title" breadcrumbs={breadcrumbs.slice(0, 2)} />
</MemoryRouter>
);
export const WithTabs = () => (
<MemoryRouter>
<HeaderPage title="Page Title" tabs={tabs.slice(0, 3)} />
<Header title="Page Title" tabs={tabs.slice(0, 3)} />
</MemoryRouter>
);
export const WithCustomActions = () => (
<MemoryRouter>
<HeaderPage
title="Page Title"
customActions={<Button>Custom action</Button>}
/>
<Header title="Page Title" customActions={<Button>Custom action</Button>} />
</MemoryRouter>
);
export const WithMenu = () => (
<MemoryRouter>
<HeaderPage
<Header
title="Page Title"
customActions={
<MenuTrigger>
@@ -19,11 +19,11 @@ import {
} from './snippets';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { HeaderPageDefinition } from '../../../utils/definitions';
import { HeaderDefinition } from '../../../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
title="HeaderPage"
title="Header"
description="A secondary header with title, breadcrumbs, tabs, and actions."
/>
@@ -61,6 +61,6 @@ Use `customActions` to add a dropdown menu.
<Snippet open preview={<WithMenu />} code={withMenu} />
<Theming definition={HeaderPageDefinition} />
<Theming definition={HeaderDefinition} />
<ChangelogComponent component="header-page" />
<ChangelogComponent component="header" />
@@ -50,7 +50,7 @@ export const headerPagePropDefs: Record<string, PropDef> = {
type: 'complex',
description: 'Breadcrumb trail displayed above the title.',
complexType: {
name: 'HeaderPageBreadcrumb[]',
name: 'HeaderBreadcrumb[]',
properties: {
label: {
type: 'string',
@@ -1,8 +1,8 @@
export const usage = `import { HeaderPage } from '@backstage/ui';
export const usage = `import { Header } from '@backstage/ui';
<HeaderPage title="Page Title" />`;
<Header title="Page Title" />`;
export const defaultSnippet = `<HeaderPage
export const defaultSnippet = `<Header
title="Page Title"
breadcrumbs={[
{ label: 'Home', href: '/' },
@@ -20,7 +20,7 @@ export const defaultSnippet = `<HeaderPage
}
/>`;
export const withBreadcrumbs = `<HeaderPage
export const withBreadcrumbs = `<Header
title="Page Title"
breadcrumbs={[
{ label: 'Home', href: '/' },
@@ -28,7 +28,7 @@ export const withBreadcrumbs = `<HeaderPage
]}
/>`;
export const withTabs = `<HeaderPage
export const withTabs = `<Header
title="Page Title"
tabs={[
{ id: 'overview', label: 'Overview', href: '/overview' },
@@ -37,12 +37,12 @@ export const withTabs = `<HeaderPage
]}
/>`;
export const withCustomActions = `<HeaderPage
export const withCustomActions = `<Header
title="Page Title"
customActions={<Button>Custom action</Button>}
/>`;
export const withMenu = `<HeaderPage
export const withMenu = `<Header
title="Page Title"
customActions={
<MenuTrigger>
@@ -68,6 +68,11 @@ export const linkPropDefs: Record<string, PropDef> = {
'Truncates text with ellipsis when it overflows its container.',
default: 'false',
},
noTrack: {
type: 'boolean',
description:
'Suppresses the automatic analytics click event, e.g. if you already have custom tracking.',
},
standalone: {
type: 'boolean',
description: 'Removes underline by default. Underline appears on hover.',
@@ -309,6 +309,11 @@ export const menuItemPropDefs: Record<string, PropDef> = {
type: 'boolean',
description: 'Whether the item is disabled.',
},
noTrack: {
type: 'boolean',
description:
'Suppresses the automatic analytics click event, e.g. if you already have custom tracking.',
},
textValue: {
type: 'string',
description: 'Text used for typeahead and accessibility.',
@@ -1,7 +1,7 @@
'use client';
import { PluginHeader } from '../../../../../packages/ui/src/components/PluginHeader/PluginHeader';
import { HeaderPage } from '../../../../../packages/ui/src/components/HeaderPage/HeaderPage';
import { Header } from '../../../../../packages/ui/src/components/Header/Header';
import { ButtonIcon } from '../../../../../packages/ui/src/components/ButtonIcon/ButtonIcon';
import { Button } from '../../../../../packages/ui/src/components/Button/Button';
import { MemoryRouter } from 'react-router-dom';
@@ -58,11 +58,11 @@ export const WithAllOptions = () => (
</MemoryRouter>
);
export const WithHeaderPage = () => (
export const WithHeader = () => (
<MemoryRouter>
<>
<PluginHeader title="My plugin" titleLink="/" tabs={tabs.slice(0, 2)} />
<HeaderPage
<Header
title="Page title"
tabs={tabs2}
customActions={<Button>Custom action</Button>}
@@ -4,7 +4,7 @@ import { Snippet } from '@/components/Snippet';
import {
WithAllOptionsAndTabs,
WithAllOptions,
WithHeaderPage,
WithHeader,
} from './components';
import { headerPropDefs } from './props-definition';
import {
@@ -12,7 +12,7 @@ import {
simple,
defaultSnippet,
withTabs,
withHeaderPage,
withHeader,
} from './snippets';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -46,11 +46,11 @@ Tabs use React Router and highlight automatically based on the current route.
<Snippet preview={<WithAllOptionsAndTabs />} code={withTabs} open />
### Plugin header with HeaderPage
### Plugin header with Header
Combine with [HeaderPage](/components/header-page) for multi-level navigation.
Combine with [Header](/components/header) for multi-level navigation.
<Snippet preview={<WithHeaderPage />} code={withHeaderPage} open />
<Snippet preview={<WithHeader />} code={withHeader} open />
<Theming definition={PluginHeaderDefinition} />
@@ -43,7 +43,7 @@ export const withTabs = `<PluginHeader
]}
/>`;
export const withHeaderPage = `<PluginHeader
export const withHeader = `<PluginHeader
title="My plugin"
titleLink="/"
tabs={[
@@ -51,7 +51,7 @@ export const withHeaderPage = `<PluginHeader
{ id: 'checks', label: 'Checks', href: '/checks' },
]}
/>
<HeaderPage
<Header
title="Page title"
tabs={[
{ id: 'banana', label: 'Banana', href: '/banana' },
@@ -456,6 +456,11 @@ export const rowPropDefs: Record<string, PropDef> = {
description:
'Row content. Can be a render function receiving column config.',
},
noTrack: {
type: 'boolean',
description:
'Suppresses the automatic analytics click event, e.g. if you already have custom tracking.',
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -68,6 +68,11 @@ export const tabPropDefs: Record<string, PropDef> = {
default: 'false',
description: 'Disables this tab. Use for temporarily unavailable options.',
},
noTrack: {
type: 'boolean',
description:
'Suppresses the automatic analytics click event, e.g. if you already have custom tracking.',
},
...childrenPropDefs,
...classNamePropDefs,
};
@@ -84,6 +84,11 @@ export const tagPropDefs: Record<string, PropDef> = {
type: 'boolean',
description: 'Whether the tag is disabled.',
},
noTrack: {
type: 'boolean',
description:
'Suppresses the automatic analytics click event, e.g. if you already have custom tracking.',
},
...childrenPropDefs,
...classNamePropDefs,
};
@@ -1,6 +1,10 @@
import { CodeBlock } from '@/components/CodeBlock';
import { Banner } from '@/components/Banner';
import { snippet } from './snippets';
import {
snippet,
analyticsSetupSnippet,
analyticsNoTrackSnippet,
} from './snippets';
# Installation
@@ -44,3 +48,25 @@ your plugin and import the components you need.
/>
<CodeBlock lang="tsx" title="Let's get started 🚀" code={snippet} />
## Analytics
BUI components with navigation behavior — Link, ButtonLink, Tab, MenuItem, Tag, and Row — can fire analytics events when clicked. To enable this, you need to connect BUI's analytics layer to Backstage's analytics system.
### Setup
If you're using the **new frontend system**, analytics is wired automatically via `@backstage/plugin-app` — no setup required.
For the **old frontend system**, the `BUIProvider` is included in the app shell from `@backstage/core-app-api` and works out of the box.
If you need to set up the provider manually (e.g. in a custom app shell), wrap your app content with the `BUIProvider` and pass in Backstage's `useAnalytics` hook:
<CodeBlock lang="tsx" code={analyticsSetupSnippet} />
### How it works
Once configured, BUI components fire a `click` event through Backstage's analytics system when a user navigates. Events include the link text as the subject and the destination URL in the attributes, along with any `AnalyticsContext` metadata (such as `pluginId`) from the component's position in the tree.
To suppress tracking on an individual component, use the `noTrack` prop:
<CodeBlock lang="tsx" code={analyticsNoTrackSnippet} />
@@ -4,3 +4,16 @@ export const snippet = `import { Flex, Button, Text } from '@backstage/ui';
<Text>Hello World</Text>
<Button>Click me</Button>
</Flex>;`;
export const analyticsSetupSnippet = `import { BUIProvider } from '@backstage/ui';
import { useAnalytics } from '@backstage/core-plugin-api';
// Wrap your app content with the provider
<BUIProvider useAnalytics={useAnalytics}>
<AppContent />
</BUIProvider>`;
export const analyticsNoTrackSnippet = `// Suppress analytics for a specific link
<Link href="/internal" noTrack>
Skip tracking
</Link>`;
@@ -3,6 +3,7 @@
import { useState, useEffect } from 'react';
import {
RiArrowDownSLine,
RiDiscordLine,
RiGithubLine,
RiMoonLine,
RiSearchLine,
@@ -95,6 +96,7 @@ export const Toolbar = ({ version }: ToolbarProps) => {
<a
href="https://www.npmjs.com/package/@backstage/ui"
target="_blank"
rel="noopener noreferrer"
className={styles.bubble}
data-hide-tablet
>
@@ -103,11 +105,23 @@ export const Toolbar = ({ version }: ToolbarProps) => {
<a
href="https://github.com/backstage/backstage/tree/master/packages/ui"
target="_blank"
rel="noopener noreferrer"
className={styles.bubble}
data-hide-tablet
aria-label="View source on GitHub"
>
<RiGithubLine size={16} />
</a>
<a
href="https://discord.gg/backstage-687207715902193673"
target="_blank"
rel="noopener noreferrer"
className={styles.bubble}
data-hide-tablet
aria-label="Join Backstage on Discord"
>
<RiDiscordLine size={16} />
</a>
<ToggleButtonGroup
defaultSelectedKeys={['light']}
selectedKeys={selectedTheme}
+1 -1
View File
@@ -114,7 +114,7 @@ After:
'switch',
'skeleton',
'plugin-header',
'header-page',
'header',
'tabs',
],
version: '0.9.0',
+2 -2
View File
@@ -62,8 +62,8 @@ export const components: Page[] = [
slug: 'plugin-header',
},
{
title: 'HeaderPage',
slug: 'header-page',
title: 'Header',
slug: 'header',
},
{
title: 'Link',
+1 -1
View File
@@ -15,7 +15,7 @@ export type Component =
| 'flex'
| 'grid'
| 'plugin-header'
| 'header-page'
| 'header'
| 'heading'
| 'icon'
| 'link'