Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Enhancements

- `PanelBody`: Add a `headingLevel` prop to configure the title's heading level (`h1`–`h6`), defaulting to `2` ([#78752](https://github.com/WordPress/gutenberg/pull/78752)).

## 34.0.0 (2026-05-27)

### Breaking Changes
Expand Down
7 changes: 7 additions & 0 deletions packages/components/src/panel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ Title text. It shows even when the component is closed.

- Required: No

###### `headingLevel`: `1 | 2 | 3 | 4 | 5 | 6`

The heading level of the title. Use this to reflect the surrounding document structure so the title is rendered with the appropriate heading tag (`h1`–`h6`).

- Required: No
- Default: `2`

###### `opened`: `boolean`

When set to `true`, the component will remain open regardless of the `initialOpen` prop and the
Expand Down
10 changes: 8 additions & 2 deletions packages/components/src/panel/body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function UnforwardedPanelBody(
buttonProps = {},
children,
className,
headingLevel = 2,
icon,
initialOpen,
onToggle = noop,
Expand Down Expand Up @@ -90,6 +91,7 @@ export function UnforwardedPanelBody(
isOpened={ Boolean( isOpened ) }
onClick={ handleOnToggle }
title={ title }
headingLevel={ headingLevel }
{ ...buttonProps }
/>
{ typeof children === 'function'
Expand All @@ -102,6 +104,7 @@ export function UnforwardedPanelBody(
const PanelBodyTitle = forwardRef(
(
{
headingLevel = 2,
isOpened,
icon,
title,
Expand All @@ -113,8 +116,11 @@ const PanelBodyTitle = forwardRef(
return null;
}

const TitleTag =
`h${ headingLevel }` as keyof React.JSX.IntrinsicElements;

return (
<h2 className="components-panel__body-title">
<TitleTag className="components-panel__body-title">
<Button
__next40pxDefaultSize
className="components-panel__body-toggle"
Expand All @@ -141,7 +147,7 @@ const PanelBodyTitle = forwardRef(
/>
) }
</Button>
</h2>
</TitleTag>
);
}
);
Expand Down
21 changes: 21 additions & 0 deletions packages/components/src/panel/test/body.tsx

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also test for the string version, eg '3'

Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,25 @@ describe( 'PanelBody', () => {
expect( mock ).toHaveBeenCalled();
} );
} );

describe( 'title heading level', () => {
it( 'should render the title as a level 2 heading by default', () => {
render( <PanelBody title="Panel" /> );

expect(
screen.getByRole( 'heading', { level: 2, name: 'Panel' } )
).toBeInTheDocument();
} );

it( 'should render the title at the specified heading level', () => {
render( <PanelBody title="Panel" headingLevel={ 3 } /> );

expect(
screen.getByRole( 'heading', { level: 3, name: 'Panel' } )
).toBeInTheDocument();
expect(
screen.queryByRole( 'heading', { level: 2 } )
).not.toBeInTheDocument();
} );
} );
} );
15 changes: 15 additions & 0 deletions packages/components/src/panel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import type { ButtonAsButtonProps } from '../button/types';
import type { WordPressComponentProps } from '../context';
import type { HeadingSize } from '../heading/types';

export type PanelProps = {
/**
Expand Down Expand Up @@ -67,6 +68,14 @@ export type PanelBodyProps = {
* The CSS class to apply to the wrapper element.
*/
className?: string;
/**
* The heading level of the title. Use this to reflect the surrounding
* document structure so the title is rendered with the appropriate
* heading tag (`h1`–`h6`).
*
* @default 2
*/
headingLevel?: HeadingSize;
/**
* An icon to be shown next to the title.
*/
Expand Down Expand Up @@ -101,6 +110,12 @@ export type PanelBodyProps = {
};

export type PanelBodyTitleProps = Omit< ButtonAsButtonProps, 'icon' > & {
/**
* The heading level of the title.
*
* @default 2
*/
headingLevel?: HeadingSize;
/**
* An icon to be shown next to the title.
*/
Expand Down
Loading