- {breadcrumbs &&
- breadcrumbs.map(breadcrumb => (
-
-
- {breadcrumb.label}
-
-
-
+ <>
+
+ {tags && tags.length > 0 && (
+
+ {tags.map((tag, i) => (
+
+ {tag.href ? (
+
+ {tag.label}
+
+ ) : (
+
+ {tag.label}
+
+ )}
+
))}
-
- {title}
-
+
+ )}
+
+ {sticky && (
+
+ )}
+
0 ? '' : undefined}
+ {...dataAttributes}
+ >
+
+ {isStuck ? (
+
+ {breadcrumbs &&
+ breadcrumbs.map(breadcrumb => (
+
+
+ {breadcrumb.label}
+
+
+
+ ))}
+
{title}
+
+ ) : (
+
+ {breadcrumbs &&
+ breadcrumbs.map(breadcrumb => (
+
+
+ {breadcrumb.label}
+
+
+
+ ))}
+
{title}
+
+ )}
{customActions}
- {description && (
-
- {descriptionNodes}
-
- )}
- {metadata && metadata.length > 0 && (
-
- {metadata.map((item, i) => (
-
-
-
- {item.label}
-
-
-
- {typeof item.value === 'string' ? (
- {item.value}
- ) : (
- item.value
- )}
-
-
- ))}
-
- )}
- {tabs && (
-
-
-
- )}
-
+
+ {description && (
+
+ {descriptionNodes}
+
+ )}
+ {metadata && metadata.length > 0 && (
+
+ {metadata.map((item, i) => (
+
+
+
+ {item.label}
+
+
+
+ {typeof item.value === 'string' ? (
+ {item.value}
+ ) : (
+ item.value
+ )}
+
+
+ ))}
+
+ )}
+ {tabs && (
+
+
+
+ )}
+
+ >
);
};
diff --git a/packages/ui/src/components/Header/definition.ts b/packages/ui/src/components/Header/definition.ts
index c0420dade5..8fd12cc97b 100644
--- a/packages/ui/src/components/Header/definition.ts
+++ b/packages/ui/src/components/Header/definition.ts
@@ -24,10 +24,20 @@ import styles from './Header.module.css';
*/
export const HeaderDefinition = defineComponent
()({
styles,
+ bg: 'consumer',
classNames: {
- root: 'bui-Header',
+ headerTop: 'bui-HeaderTop',
+ stickySentinel: 'bui-HeaderStickySentinel',
content: 'bui-HeaderContent',
+ headerBottom: 'bui-HeaderBottom',
breadcrumbs: 'bui-HeaderBreadcrumbs',
+ breadcrumbsSmall: 'bui-HeaderBreadcrumbsSmall',
+ breadcrumbLink: 'bui-HeaderBreadcrumbLink',
+ breadcrumbLinkSmall: 'bui-HeaderBreadcrumbLinkSmall',
+ breadcrumbSeparator: 'bui-HeaderBreadcrumbSeparator',
+ titleStack: 'bui-HeaderTitleStack',
+ title: 'bui-HeaderTitle',
+ titleSmall: 'bui-HeaderTitleSmall',
tabsWrapper: 'bui-HeaderTabsWrapper',
controls: 'bui-HeaderControls',
tags: 'bui-HeaderTags',
@@ -46,6 +56,7 @@ export const HeaderDefinition = defineComponent()({
tags: {},
metadata: {},
className: {},
+ sticky: {},
},
});
diff --git a/packages/ui/src/components/Header/types.ts b/packages/ui/src/components/Header/types.ts
index bc9ce8985b..77fbe937c3 100644
--- a/packages/ui/src/components/Header/types.ts
+++ b/packages/ui/src/components/Header/types.ts
@@ -116,6 +116,11 @@ export interface HeaderOwnProps {
tags?: HeaderTag[];
metadata?: HeaderMetadataItem[];
className?: string;
+ /**
+ * Makes the title-and-actions row stick to the top of its nearest scroll
+ * container while the rest of the header content scrolls away.
+ */
+ sticky?: boolean;
}
/**
diff --git a/packages/ui/src/recipes/PluginHeaderAndHeader.stories.tsx b/packages/ui/src/recipes/PluginHeaderAndHeader.stories.tsx
index a43252a9b7..4f74572ef5 100644
--- a/packages/ui/src/recipes/PluginHeaderAndHeader.stories.tsx
+++ b/packages/ui/src/recipes/PluginHeaderAndHeader.stories.tsx
@@ -58,6 +58,19 @@ const PageContent = () => (
);
+const LongPageContent = () => (
+
+
+
+
+
+
+ {Array.from({ length: 40 }, (_, i) => (
+
+ ))}
+
+);
+
// ---------------------------------------------------------------------------
// Shared layout decorator
// ---------------------------------------------------------------------------
@@ -157,52 +170,6 @@ export const WithTabs = meta.story({
),
});
-export const WithBreadcrumb = meta.story({
- decorators: [withLayout],
- render: () => (
- <>
- }
- title="CI/CD"
- titleLink="/"
- tabs={[
- { id: 'builds', label: 'Builds', href: '/builds' },
- { id: 'pipelines', label: 'Pipelines', href: '/pipelines' },
- { id: 'deployments', label: 'Deployments', href: '/deployments' },
- { id: 'settings', label: 'Settings', href: '/settings' },
- ]}
- customActions={
- <>
- }
- aria-label="Refresh"
- />
- >
- }
- />
-
- }>
- Download logs
-
- }>
- Re-run pipeline
-
- >
- }
- />
-
- >
- ),
-});
-
const subTabs: HeaderNavTabItem[] = [
{ id: 'summary', label: 'Summary', href: '/summary' },
{ id: 'steps', label: 'Steps', href: '/steps' },
@@ -244,11 +211,65 @@ export const WithSubTabs = meta.story({
}
/>
+ }>
+ Download logs
+
+ }>
+ Re-run pipeline
+
+ >
+ }
+ />
+
+ >
+ );
+ },
+});
+
+export const WithSubTabsAndTags = meta.story({
+ decorators: [
+ (Story: StoryFn) => (
+
+
+
+
+
+ ),
+ ],
+ render: () => {
+ return (
+ <>
+ }
+ title="CI/CD"
+ titleLink="/"
+ tabs={[
+ { id: 'builds', label: 'Builds', href: '/builds' },
+ { id: 'pipelines', label: 'Pipelines', href: '/pipelines' },
+ { id: 'deployments', label: 'Deployments', href: '/deployments' },
+ { id: 'settings', label: 'Settings', href: '/settings' },
+ ]}
+ customActions={
+ <>
+ }
+ aria-label="Refresh"
+ />
+ >
+ }
+ />
+
@@ -266,3 +287,57 @@ export const WithSubTabs = meta.story({
);
},
});
+
+export const WithStickyHeader = meta.story({
+ decorators: [
+ (Story: StoryFn) => (
+
+
+
+
+
+ ),
+ ],
+ render: () => {
+ return (
+ <>
+ }
+ title="CI/CD"
+ titleLink="/"
+ tabs={[
+ { id: 'builds', label: 'Builds', href: '/builds' },
+ { id: 'pipelines', label: 'Pipelines', href: '/pipelines' },
+ { id: 'deployments', label: 'Deployments', href: '/deployments' },
+ { id: 'settings', label: 'Settings', href: '/settings' },
+ ]}
+ customActions={
+ <>
+ }
+ aria-label="Refresh"
+ />
+ >
+ }
+ />
+
+ }>
+ Download logs
+
+ }>
+ Re-run pipeline
+
+ >
+ }
+ />
+
+ >
+ );
+ },
+});