From 96e6e087cdf18be0f2f91d88f00c915d88467901 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Tue, 22 Jul 2025 21:38:23 +0100 Subject: [PATCH] Improve Header and HeaderPage content Signed-off-by: Charles de Dreuille --- .../StickyHeader/StickyHeader.module.css | 22 ++-- .../src/components/Toolbar/Toolbar.module.css | 3 +- .../src/content/components/header-page.mdx | 40 +++++++ .../content/components/header-page.props.ts | 91 ++++++++++++++++ docs-ui/src/content/components/header.mdx | 62 +++++++---- .../src/content/components/header.props.ts | 101 +++++++++++++++--- docs-ui/src/css/globals.css | 2 + docs-ui/src/utils/data.ts | 5 + .../src/components/Header/Header.stories.tsx | 79 +++++++++----- 9 files changed, 334 insertions(+), 71 deletions(-) create mode 100644 docs-ui/src/content/components/header-page.mdx create mode 100644 docs-ui/src/content/components/header-page.props.ts diff --git a/docs-ui/src/components/StickyHeader/StickyHeader.module.css b/docs-ui/src/components/StickyHeader/StickyHeader.module.css index 783b4a55be..7cfc197f83 100644 --- a/docs-ui/src/components/StickyHeader/StickyHeader.module.css +++ b/docs-ui/src/components/StickyHeader/StickyHeader.module.css @@ -57,16 +57,22 @@ .versionLinks { display: flex; align-items: center; - gap: 12px; -} -.versionLinks a { - color: var(--text-secondary); - transition: color 0.2s ease; -} + a { + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + color: var(--secondary); + transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; + border-radius: 48px; -.versionLinks a:hover { - color: var(--text-primary); + &:hover { + color: var(--primary); + background-color: var(--action); + } + } } @media (max-width: 768px) { diff --git a/docs-ui/src/components/Toolbar/Toolbar.module.css b/docs-ui/src/components/Toolbar/Toolbar.module.css index 5e3fe70808..f0e58f2edb 100644 --- a/docs-ui/src/components/Toolbar/Toolbar.module.css +++ b/docs-ui/src/components/Toolbar/Toolbar.module.css @@ -78,7 +78,8 @@ display: flex; align-items: center; justify-content: center; - background-color: var(--action); + /* background-color: var(--action); */ + border: 1px solid var(--border2); border-radius: 48px; padding-inline: 20px; color: var(--primary); diff --git a/docs-ui/src/content/components/header-page.mdx b/docs-ui/src/content/components/header-page.mdx new file mode 100644 index 0000000000..4589e037a4 --- /dev/null +++ b/docs-ui/src/content/components/header-page.mdx @@ -0,0 +1,40 @@ +import { PropsTable } from '@/components/PropsTable'; +import { CodeBlock } from '@/components/CodeBlock'; +import { Snippet } from '@/components/Snippet'; +import { HeaderPageSnippet } from '@/snippets/stories-snippets'; +import { + propDefs, + usage, + simple, + defaultSnippet, + withTabs, + withBreadcrumbs, + withHeaderPage, +} from './header-page.props'; +import { PageTitle } from '@/components/PageTitle'; +import { Theming } from '@/components/Theming'; +import { ChangelogComponent } from '@/components/ChangelogComponent'; + + + +} + code={defaultSnippet} +/> + +## Usage + + + +## API reference + + + + + + diff --git a/docs-ui/src/content/components/header-page.props.ts b/docs-ui/src/content/components/header-page.props.ts new file mode 100644 index 0000000000..d287c8baf8 --- /dev/null +++ b/docs-ui/src/content/components/header-page.props.ts @@ -0,0 +1,91 @@ +import { + childrenPropDefs, + classNamePropDefs, + stylePropDefs, + type PropDef, +} from '@/utils/propDefs'; + +export const propDefs: Record = { + title: { + type: 'string', + default: 'Your plugin', + }, + customActions: { + type: 'enum', + values: ['ReactNode'], + }, + menuItems: { + type: 'complex', + complexType: { + name: 'MenuItem[]', + properties: { + label: { + type: 'string', + required: true, + description: 'Display text for the menu item', + }, + value: { + type: 'string', + required: true, + description: 'Unique value for the menu item', + }, + onClick: { + type: '() => void', + required: false, + description: 'Callback function when menu item is clicked', + }, + }, + }, + }, + tabs: { + type: 'complex', + complexType: { + name: 'HeaderTab[]', + properties: { + id: { + type: 'string', + required: true, + description: 'Unique identifier for the tab', + }, + label: { + type: 'string', + required: true, + description: 'Display text for the tab', + }, + href: { + type: 'string', + required: false, + description: 'URL to navigate to when tab is clicked', + }, + matchStrategy: { + type: "'exact' | 'prefix'", + required: false, + description: 'How to match the current route to highlight the tab', + }, + }, + }, + }, + ...childrenPropDefs, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const usage = `import { HeaderPage } from '@backstage/ui'; + +`; + +export const defaultSnippet = `Custom action} +/>`; diff --git a/docs-ui/src/content/components/header.mdx b/docs-ui/src/content/components/header.mdx index d59dd79613..af4e8010c5 100644 --- a/docs-ui/src/content/components/header.mdx +++ b/docs-ui/src/content/components/header.mdx @@ -4,16 +4,13 @@ import { Snippet } from '@/components/Snippet'; import { HeaderSnippet } from '@/snippets/stories-snippets'; import { propDefs, - flexUsageSnippet, - flexDefaultSnippet, - flexFAQ1Snippet, - flexSimpleSnippet, - flexResponsiveSnippet, - flexAlignSnippet, usage, + simple, defaultSnippet, + withTabs, + withBreadcrumbs, + withHeaderPage, } from './header.props'; -import { spacingPropDefs } from '@/utils/propDefs'; import { PageTitle } from '@/components/PageTitle'; import { Theming } from '@/components/Theming'; import { ChangelogComponent } from '@/components/ChangelogComponent'; @@ -26,7 +23,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent'; } + preview={} code={defaultSnippet} /> @@ -38,31 +35,50 @@ import { ChangelogComponent } from '@/components/ChangelogComponent'; -The grid component also accepts all the spacing props from the Box component. - - - ## Examples -### Simple +### Simple header -A simple example of how to use the Flex component. +A simple example of how to use the Header component. - +} + code={simple} + open +/> -### Responsive +### Header with tabs -The Flex component also supports responsive values, making it easy to create -responsive designs. +A simple example of how to use the Header component with tabs. All links are using React Router +under the hood and will be active when you are on the corresponding page. - +} +code={withTabs} +open +/> -The Flex component also supports responsive alignment, making it easy to -create responsive designs. +### Header with breadcrumbs - +Breacrumbs should appear when you scroll down (and not directly visible as it is in the demo below). + +} + code={withBreadcrumbs} + open +/> + +### Header with HeaderPage + +You can use the `Header` component inside the [HeaderPage](/components/header-page) component to compose your multi-level navigation. + +} + code={withHeaderPage} + open +/> diff --git a/docs-ui/src/content/components/header.props.ts b/docs-ui/src/content/components/header.props.ts index ee22fb7d67..50c5540153 100644 --- a/docs-ui/src/content/components/header.props.ts +++ b/docs-ui/src/content/components/header.props.ts @@ -91,6 +91,10 @@ export const propDefs: Record = { }, }, }, + onTabSelectionChange: { + type: 'enum', + values: ['(key: string) => void'], + }, ...childrenPropDefs, ...classNamePropDefs, ...stylePropDefs, @@ -128,20 +132,87 @@ export const defaultSnippet = `
`; -export const flexSimpleSnippet = ` - Hello World - Hello World - Hello World -`; +export const simple = `
+ } /> + } /> + } /> + + } +/>`; -export const flexResponsiveSnippet = ` - Hello World - Hello World - Hello World -`; +export const withTabs = ` +
+ } /> + } /> + } /> + + } + tabs={[ + { id: 'overview', label: 'Overview' }, + { id: 'checks', label: 'Checks' }, + { id: 'tracks', label: 'Tracks' }, + { id: 'campaigns', label: 'Campaigns' }, + { id: 'integrations', label: 'Integrations' }, + ]} +/> +`; -export const flexAlignSnippet = ` - Hello World - Hello World - Hello World -`; +export const withBreadcrumbs = `
`; + +export const withHeaderPage = `
+Custom action} +/>`; diff --git a/docs-ui/src/css/globals.css b/docs-ui/src/css/globals.css index 9193f49b5a..69a2c8ddfe 100644 --- a/docs-ui/src/css/globals.css +++ b/docs-ui/src/css/globals.css @@ -13,6 +13,7 @@ --font-weight-regular: 400; --font-weight-bold: 600; --border: #e5e5e5; + --border2: #cdcdcd; --surface-1: #f4f4f4; } @@ -24,6 +25,7 @@ --action: #202020; --link: #4856e8; --border: #2a2a2a; + --border2: #2a2a2a; --surface-1: #282828; } diff --git a/docs-ui/src/utils/data.ts b/docs-ui/src/utils/data.ts index 326ecd5aa5..348f4df24e 100644 --- a/docs-ui/src/utils/data.ts +++ b/docs-ui/src/utils/data.ts @@ -106,6 +106,11 @@ export const components: Page[] = [ slug: 'header', status: 'alpha', }, + { + title: 'HeaderPage', + slug: 'header-page', + status: 'alpha', + }, { title: 'Icon', slug: 'icon', diff --git a/packages/ui/src/components/Header/Header.stories.tsx b/packages/ui/src/components/Header/Header.stories.tsx index 7d58aad1de..302aef9765 100644 --- a/packages/ui/src/components/Header/Header.stories.tsx +++ b/packages/ui/src/components/Header/Header.stories.tsx @@ -169,30 +169,7 @@ export const WithOptions: Story = { }; export const WithCustomActions: Story = { - args: { - menuItems, - }, - decorators: [withRouter], - render: args => ( -
Custom action} /> - ), -}; - -export const WithBreadcrumbs: Story = { - args: { - breadcrumbs, - }, - decorators: [withRouter], -}; - -export const WithAllComponents: Story = { - args: { - title: 'My plugin', - titleLink: '/', - menuItems, - tabs, - breadcrumbs, - }, + args: {}, decorators: [withRouter], render: args => (
( + <> +
+ } /> + } /> + } /> + + } + /> + Custom action} + /> + + ), +}; + export const WithLayout: Story = { args: { menuItems,