From 6fbef22f5dd58e12b6f794929707599d93a37f55 Mon Sep 17 00:00:00 2001 From: Rebecca Alpert Date: Wed, 26 Aug 2026 11:05:36 -0400 Subject: [PATCH 1/2] feat(Page): Add PageHeader component PageHeader can be used to wrap masthead or hold a third-party custom header. Fixes https://github.com/patternfly/patternfly-react/issues/12624 Assisted-by: Cursor --- .../react-core/src/components/Page/Page.tsx | 5 +- .../src/components/Page/PageHeader.tsx | 28 ++++++++++ .../components/Page/__tests__/Page.test.tsx | 30 +++++++++++ .../Page/__tests__/PageHeader.test.tsx | 33 ++++++++++++ .../src/components/Page/examples/Page.md | 21 +++++++- .../Page/examples/PageHeaderContent.tsx | 52 +++++++++++++++++++ .../react-core/src/components/Page/index.ts | 1 + 7 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 packages/react-core/src/components/Page/PageHeader.tsx create mode 100644 packages/react-core/src/components/Page/__tests__/PageHeader.test.tsx create mode 100644 packages/react-core/src/components/Page/examples/PageHeaderContent.tsx diff --git a/packages/react-core/src/components/Page/Page.tsx b/packages/react-core/src/components/Page/Page.tsx index 034c72f9787..7a2deaba5a7 100644 --- a/packages/react-core/src/components/Page/Page.tsx +++ b/packages/react-core/src/components/Page/Page.tsx @@ -28,7 +28,10 @@ export interface PageProps extends React.HTMLProps { * will handle toggling the visibility of the text in individual isDocked components. */ isDockTextExpanded?: boolean; - /** The horizontal masthead content (e.g. ). When using the docked variant, this content will only render at mobile viewports. */ + /** The horizontal masthead content (e.g. or ). PageHeader is an alternative to Masthead + * and can wrap a Masthead or custom header content. When using the docked variant, this content will only render at + * mobile viewports. + */ masthead?: React.ReactNode; /** @beta Content to render in the vertical dock when variant of docked is used. At mobile viewports, this content will be replaced with the content passed to masthead. */ dockContent?: React.ReactNode; diff --git a/packages/react-core/src/components/Page/PageHeader.tsx b/packages/react-core/src/components/Page/PageHeader.tsx new file mode 100644 index 00000000000..f271ac5e05c --- /dev/null +++ b/packages/react-core/src/components/Page/PageHeader.tsx @@ -0,0 +1,28 @@ +import styles from '@patternfly/react-styles/css/components/Page/page'; +import { css } from '@patternfly/react-styles'; + +export interface PageHeaderProps extends React.HTMLProps { + /** Content rendered inside the page header. This can be a Masthead or custom header content. */ + children?: React.ReactNode; + /** Additional classes added to the page header */ + className?: string; + /** Sets the base component to render. Defaults to div */ + component?: keyof React.JSX.IntrinsicElements; +} + +export const PageHeader: React.FunctionComponent = ({ + className, + children, + component = 'div', + ...props +}: PageHeaderProps) => { + const Component = component as any; + + return ( + + {children} + + ); +}; + +PageHeader.displayName = 'PageHeader'; diff --git a/packages/react-core/src/components/Page/__tests__/Page.test.tsx b/packages/react-core/src/components/Page/__tests__/Page.test.tsx index c97e699685c..98fdd39762f 100644 --- a/packages/react-core/src/components/Page/__tests__/Page.test.tsx +++ b/packages/react-core/src/components/Page/__tests__/Page.test.tsx @@ -9,6 +9,7 @@ import { Nav, NavList, NavItem } from '../../Nav'; import { SkipToContent } from '../../SkipToContent'; import { PageBreadcrumb } from '../PageBreadcrumb'; import { PageGroup } from '../PageGroup'; +import { PageHeader } from '../PageHeader'; import { Masthead } from '../../Masthead'; import styles from '@patternfly/react-styles/css/components/Page/page'; @@ -487,4 +488,33 @@ describe('Page docked variant', () => { const pageDockMain = screen.getByText('Dock content').closest(`.${styles.pageDockMain}`); expect(pageDockMain).toBeInTheDocument(); }); + + test('Renders PageHeader when passed to the masthead prop', () => { + render( + Custom header}> + Custom content + + ); + + const header = screen.getByText('Custom header'); + expect(header).toHaveClass(styles.pageHeader); + expect(header.parentElement).toHaveClass(styles.page); + }); + + test('Renders Masthead inside PageHeader when passed to the masthead prop', () => { + render( + + Logo + + } + > + Custom content + + ); + + expect(screen.getByText('Logo').closest(`.${styles.pageHeader}`)).toBeInTheDocument(); + }); }); diff --git a/packages/react-core/src/components/Page/__tests__/PageHeader.test.tsx b/packages/react-core/src/components/Page/__tests__/PageHeader.test.tsx new file mode 100644 index 00000000000..f4bcfa1490d --- /dev/null +++ b/packages/react-core/src/components/Page/__tests__/PageHeader.test.tsx @@ -0,0 +1,33 @@ +import { render, screen } from '@testing-library/react'; +import styles from '@patternfly/react-styles/css/components/Page/page'; +import { PageHeader } from '../PageHeader'; + +test('Renders children', () => { + render(Header content); + expect(screen.getByText('Header content')).toBeVisible(); +}); + +test(`Renders with class ${styles.pageHeader} by default`, () => { + render(Header content); + expect(screen.getByText('Header content')).toHaveClass(styles.pageHeader); +}); + +test('Renders as a div by default', () => { + render(Header content); + expect(screen.getByText('Header content').tagName).toBe('DIV'); +}); + +test('Renders as a custom component when component is passed', () => { + render(Header content); + expect(screen.getByText('Header content').tagName).toBe('HEADER'); +}); + +test('Renders with custom classes when className is passed', () => { + render(Header content); + expect(screen.getByText('Header content')).toHaveClass('custom-class'); +}); + +test('Renders with spread props', () => { + render(Header content); + expect(screen.getByText('Header content')).toHaveAttribute('id', 'custom-id'); +}); diff --git a/packages/react-core/src/components/Page/examples/Page.md b/packages/react-core/src/components/Page/examples/Page.md index 5f4d491e7a2..ef0b6182428 100644 --- a/packages/react-core/src/components/Page/examples/Page.md +++ b/packages/react-core/src/components/Page/examples/Page.md @@ -3,7 +3,16 @@ id: Page section: components cssPrefix: pf-v6-c-page propComponents: - ['Page', 'PageSidebar', 'PageSidebarBody', 'PageSection', 'PageGroup', 'PageBreadcrumb', 'PageToggleButton'] + [ + 'Page', + 'PageHeader', + 'PageSidebar', + 'PageSidebarBody', + 'PageSection', + 'PageGroup', + 'PageBreadcrumb', + 'PageToggleButton' + ] --- import { useState, useLayoutEffect, useRef } from 'react'; @@ -16,7 +25,7 @@ import pageSectionWidthLimitMaxWidth from '@patternfly/react-tokens/dist/esm/c_p A page will typically contain the following components: -- A `` with a `masthead` prop that often contains a [masthead](/components/masthead) component +- A `` with a `masthead` prop that often contains a [masthead](/components/masthead) or a `` The `` component includes the smaller area that typically contains the `` and a ``. `` represents the main portion of the masthead, and will typically contain a `` or other menu-like components, like a ``. @@ -24,6 +33,14 @@ The `` component includes the smaller area that typically contains - 1 or more `` components inside `` for vertical navigation or other sidebar content - 1 or more `` components +### Page header + +To use a page header instead of passing a [masthead](/components/masthead) directly, pass a `` to the `masthead` property. `` can wrap a `` or custom header content. + +```ts file="./PageHeaderContent.tsx" + +``` + ### Vertical navigation To add a vertical sidebar to a ``, pass a `` component into the `sidebar` property. To render navigation in the sidebar, pass a `` component to ``. diff --git a/packages/react-core/src/components/Page/examples/PageHeaderContent.tsx b/packages/react-core/src/components/Page/examples/PageHeaderContent.tsx new file mode 100644 index 00000000000..6e1b88ceae3 --- /dev/null +++ b/packages/react-core/src/components/Page/examples/PageHeaderContent.tsx @@ -0,0 +1,52 @@ +import { + Page, + PageHeader, + Masthead, + MastheadMain, + MastheadBrand, + MastheadLogo, + MastheadContent, + PageSection, + Toolbar, + ToolbarContent, + ToolbarItem +} from '@patternfly/react-core'; + +export const PageHeaderContent: React.FunctionComponent = () => { + const headerToolbar = ( + + + header-tools + + + ); + + const pageHeader = ( + + + + + + Logo + + + + {headerToolbar} + + + ); + + return ( + + +

Page header example section 1

+
+ +

Page header example section 2 with secondary variant styling

+
+ +

Page header example section 3

+
+
+ ); +}; diff --git a/packages/react-core/src/components/Page/index.ts b/packages/react-core/src/components/Page/index.ts index 5afe6f03b36..55f94c2bffe 100644 --- a/packages/react-core/src/components/Page/index.ts +++ b/packages/react-core/src/components/Page/index.ts @@ -2,6 +2,7 @@ export * from './Page'; export * from './PageBody'; export * from './PageBreadcrumb'; export * from './PageGroup'; +export * from './PageHeader'; export * from './PageSidebar'; export * from './PageSidebarBody'; export * from './PageSection'; From 9d4b52270aba0e1ee8cd891deff6defc61e2c904 Mon Sep 17 00:00:00 2001 From: Rebecca Alpert Date: Thu, 27 Aug 2026 14:07:10 -0400 Subject: [PATCH 2/2] Remove masthead wrapping to reflect Core --- .../react-core/src/components/Page/Page.tsx | 2 +- .../src/components/Page/PageHeader.tsx | 2 +- .../src/components/Page/__tests__/Page.test.tsx | 17 ----------------- .../src/components/Page/examples/Page.md | 2 +- 4 files changed, 3 insertions(+), 20 deletions(-) diff --git a/packages/react-core/src/components/Page/Page.tsx b/packages/react-core/src/components/Page/Page.tsx index 7a2deaba5a7..595ac6a5c07 100644 --- a/packages/react-core/src/components/Page/Page.tsx +++ b/packages/react-core/src/components/Page/Page.tsx @@ -29,7 +29,7 @@ export interface PageProps extends React.HTMLProps { */ isDockTextExpanded?: boolean; /** The horizontal masthead content (e.g. or ). PageHeader is an alternative to Masthead - * and can wrap a Masthead or custom header content. When using the docked variant, this content will only render at + * and should only be used to wrap custom header content. When using the docked variant, this content will only render at * mobile viewports. */ masthead?: React.ReactNode; diff --git a/packages/react-core/src/components/Page/PageHeader.tsx b/packages/react-core/src/components/Page/PageHeader.tsx index f271ac5e05c..50eeefe08b0 100644 --- a/packages/react-core/src/components/Page/PageHeader.tsx +++ b/packages/react-core/src/components/Page/PageHeader.tsx @@ -2,7 +2,7 @@ import styles from '@patternfly/react-styles/css/components/Page/page'; import { css } from '@patternfly/react-styles'; export interface PageHeaderProps extends React.HTMLProps { - /** Content rendered inside the page header. This can be a Masthead or custom header content. */ + /** Content rendered inside the page header. This should be custom header content, rather than the PatternFly Masthead. */ children?: React.ReactNode; /** Additional classes added to the page header */ className?: string; diff --git a/packages/react-core/src/components/Page/__tests__/Page.test.tsx b/packages/react-core/src/components/Page/__tests__/Page.test.tsx index 98fdd39762f..bb76d3408dc 100644 --- a/packages/react-core/src/components/Page/__tests__/Page.test.tsx +++ b/packages/react-core/src/components/Page/__tests__/Page.test.tsx @@ -500,21 +500,4 @@ describe('Page docked variant', () => { expect(header).toHaveClass(styles.pageHeader); expect(header.parentElement).toHaveClass(styles.page); }); - - test('Renders Masthead inside PageHeader when passed to the masthead prop', () => { - render( - - Logo - - } - > - Custom content -
- ); - - expect(screen.getByText('Logo').closest(`.${styles.pageHeader}`)).toBeInTheDocument(); - }); }); diff --git a/packages/react-core/src/components/Page/examples/Page.md b/packages/react-core/src/components/Page/examples/Page.md index ef0b6182428..943ea3acd73 100644 --- a/packages/react-core/src/components/Page/examples/Page.md +++ b/packages/react-core/src/components/Page/examples/Page.md @@ -35,7 +35,7 @@ The `` component includes the smaller area that typically contains ### Page header -To use a page header instead of passing a [masthead](/components/masthead) directly, pass a `` to the `masthead` property. `` can wrap a `` or custom header content. +To use a page header instead of passing a [masthead](/components/masthead) directly, pass a `` to the `masthead` property. `` should only be used to wrap custom header content. ```ts file="./PageHeaderContent.tsx"