Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
4a1a98a
Add AdminHeader component to jetpack-components for unified admin pag…
CGastrell Feb 25, 2026
e3b4a14
Add unified header props to AdminPage with backward-compatible legacy…
CGastrell Feb 25, 2026
422ea27
Migrate publicize admin page to use unified AdminHeader via title prop.
CGastrell Feb 25, 2026
afacc5f
Refactor AdminHeader to use VStack/HStack/Heading matching @wordpress…
CGastrell Feb 25, 2026
5a883b6
Rename tagline prop to subTitle across AdminPage and publicize for ad…
CGastrell Feb 25, 2026
cedd3ab
Add @wordpress/admin-ui dependency to jetpack-components.
CGastrell Feb 25, 2026
3664f98
Refactor AdminHeader to wrap @wordpress/admin-ui Page component.
CGastrell Feb 25, 2026
ef758cd
Remove unused AdminHeader style.module.scss, styles come from admin-ui.
CGastrell Feb 25, 2026
a7f1b2a
Delete unused publicize AdminPageHeader, replaced by unified AdminHea…
CGastrell Feb 25, 2026
f47248b
Add changelog entries for jetpack-components and publicize.
CGastrell Feb 25, 2026
fbf0627
Address review feedback: fix deprecated prop name, export types, remo…
CGastrell Feb 25, 2026
caa6268
add subtitle as in design MVP
CGastrell Feb 25, 2026
fcb207d
Bundle @wordpress/admin-ui CSS via proxy file to avoid DependencyExtr…
CGastrell Feb 25, 2026
a2759a1
Centralize admin-ui CSS bundling in jetpack-webpack-config
vianasw Feb 26, 2026
e73635f
Add changelog entries for webpack-config and components
vianasw Feb 26, 2026
8954b90
Remove AdminHeader, use admin-ui Page directly in AdminPage
vianasw Feb 26, 2026
58b4850
Fix Jest failing to parse @wordpress/admin-ui CSS import.
CGastrell Feb 27, 2026
271a54b
Fix Jest transformIgnorePatterns for @wordpress/admin-ui CSS in all c…
CGastrell Feb 27, 2026
275b400
Migrate search upsell page to unified AdminHeader (#47332)
CGastrell Feb 27, 2026
3e040f4
Migrate backup admin page header to unified AdminHeader component. (#…
CGastrell Feb 27, 2026
2beca82
Migrate VideoPress main admin page header to unified AdminHeader (#47…
CGastrell Feb 27, 2026
fd86027
Protect: Migrate admin page header to unified header pattern (#47361)
vianasw Feb 27, 2026
7c92caa
My Jetpack: Migrate admin page header to unified admin-ui Page layout…
vianasw Feb 27, 2026
beaa234
Forms: Remove admin-ui header border
vianasw Feb 27, 2026
768a77f
Newsletter: Migrate admin page header to use unified header pattern (…
vianasw Feb 28, 2026
b746b0f
Update/admin header normalization css override (#47397)
CGastrell Feb 28, 2026
77abffb
Boost: Migrate header to lightweight unified pattern (#47392)
vianasw Feb 28, 2026
9b6940a
Modules page: Migrate PHP-rendered header to unified pattern (#47398)
CGastrell Feb 28, 2026
25ad026
Add changelog entries.
invalid-email-address Mar 2, 2026
499846b
Apply suggestion from @simison
vianasw Mar 2, 2026
fdc21a8
Apply suggestion from @simison
vianasw Mar 2, 2026
ec7b5f6
Apply suggestion from @simison
vianasw Mar 2, 2026
748fbb2
Properly fix product names being translated
vianasw Mar 2, 2026
518a7ab
Apply suggestion from @Copilot
vianasw Mar 2, 2026
87755a0
Jetpack Settings: Migrate header to unified pattern and left-align ta…
vianasw Mar 2, 2026
61c745f
Boost: Fix header bottom padding and use untranslated product name
vianasw Mar 2, 2026
3965be6
Boost: Match header spacing with admin-ui Page layout
vianasw Mar 2, 2026
7ce445a
Boost: Add white background to mount point for Hello Dolly compatibility
vianasw Mar 2, 2026
f7e1852
Settings: Remove Hello Dolly border-bottom to match unified header
vianasw Mar 2, 2026
e86c044
Settings: Also fix Hello Dolly border in admin-static.scss
vianasw Mar 2, 2026
913f7d1
Stop translating product names across all admin pages
vianasw Mar 2, 2026
176cef3
Fix remaining translated product names flagged in review
vianasw Mar 2, 2026
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
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Replace admin-ui CSS proxy file with direct import, now that webpack-config handles bundling centrally.
4 changes: 4 additions & 0 deletions projects/js-packages/components/changelog/pr-47345
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

AdminPage: Remove admin-ui header border via scoped CSS to support unified admin-ui Page layout.
4 changes: 4 additions & 0 deletions projects/js-packages/components/changelog/pr-47361
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

AdminPage: Remove header border-bottom for a cleaner unified header appearance.
4 changes: 4 additions & 0 deletions projects/js-packages/components/changelog/pr-47388
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

Admin page: Fix Hello Dolly banner display and clear floats on Jetpack admin pages.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

AdminPage: override admin-ui header position so it's not sticky
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Add AdminHeader component wrapping @wordpress/admin-ui Page for unified admin page headers.
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The changelog mentions "Add AdminHeader component" but there is no separate AdminHeader component in the codebase. The functionality is integrated directly into the AdminPage component via the new title/subTitle/logo/actions/tabs props. The changelog should be updated to accurately reflect that AdminPage was extended rather than a new AdminHeader component being added.

Copilot uses AI. Check for mistakes.
75 changes: 62 additions & 13 deletions projects/js-packages/components/components/admin-page/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import restApi from '@automattic/jetpack-api';
import { Page } from '@wordpress/admin-ui';
import '@wordpress/admin-ui/build-style/style.css';
import {
__experimentalHeading as Heading, // eslint-disable-line @wordpress/no-unsafe-wp-apis
__experimentalHStack as HStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis
} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import clsx from 'clsx';
import { useEffect, useCallback } from 'react';
Expand All @@ -21,7 +27,7 @@ import type { FC, ReactNode } from 'react';
const AdminPage: FC< AdminPageProps > = ( {
children,
className,
moduleName = __( 'Jetpack', 'jetpack-components' ),
moduleName = 'Jetpack' /** "Jetpack" is a product name, do not translate. */,
moduleNameHref,
showHeader = true,
showFooter = true,
Expand All @@ -32,6 +38,11 @@ const AdminPage: FC< AdminPageProps > = ( {
apiNonce = '',
optionalMenuItems,
header,
title,
subTitle,
logo,
actions,
tabs,
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

AdminHeader supports breadcrumbs/badges, but AdminPage doesn’t currently accept/forward them in the unified-header path. If AdminPage is intended to be the single entrypoint for the new header API, consider adding breadcrumbs and badges to the props destructuring here and passing them through to .

Suggested change
tabs,
tabs,
breadcrumbs,
badges,

Copilot uses AI. Check for mistakes.
} ) => {
useEffect( () => {
restApi.setApiRoot( apiRoot );
Expand Down Expand Up @@ -60,6 +71,55 @@ const AdminPage: FC< AdminPageProps > = ( {
}
}, [] );

// Compose the title with logo for the admin-ui Page header.
// Note: The inner Heading causes a double h2 wrapping because Page's Header
// also wraps title in a Heading. This is a known issue — the inner Heading is
// needed until https://github.com/WordPress/gutenberg/pull/75899 fixes
// non-string title rendering in admin-ui. Once that lands, remove the Heading
// here and pass the plain HStack with a string child.
const composedTitle = title ? (
<HStack spacing={ 2 } justify="left">
{ logo || <JetpackLogo showText={ false } height={ 20 } /> }
<Heading as="h2" level={ 3 } weight={ 500 } truncate>
Comment on lines +75 to +83
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The comment mentions a known issue where the inner Heading causes double h2 wrapping because Page's Header also wraps the title in a Heading. While the comment references a Gutenberg PR (#75899) that should fix this, the workaround creates invalid HTML (nested h2 elements) which could cause accessibility and SEO issues. Consider adding a TODO comment with a tracking task to remove the inner Heading once the upstream fix lands, or explore alternative approaches that don't result in invalid HTML in the meantime.

Suggested change
// Note: The inner Heading causes a double h2 wrapping because Page's Header
// also wraps title in a Heading. This is a known issue — the inner Heading is
// needed until https://github.com/WordPress/gutenberg/pull/75899 fixes
// non-string title rendering in admin-ui. Once that lands, remove the Heading
// here and pass the plain HStack with a string child.
const composedTitle = title ? (
<HStack spacing={ 2 } justify="left">
{ logo || <JetpackLogo showText={ false } height={ 20 } /> }
<Heading as="h2" level={ 3 } weight={ 500 } truncate>
// Note: The inner Heading previously caused a double h2 wrapping because Page's Header
// also wraps title in a Heading. To avoid invalid nested headings while Gutenberg is
// still fixing non-string title rendering in admin-ui, we render the inner Heading
// as a non-heading element for styling only.
// TODO: Once https://github.com/WordPress/gutenberg/pull/75899 lands and non-string
// titles are supported, remove the inner Heading here and pass the plain HStack
// with a string child instead.
const composedTitle = title ? (
<HStack spacing={ 2 } justify="left">
{ logo || <JetpackLogo showText={ false } height={ 20 } /> }
<Heading as="span" weight={ 500 } truncate>

Copilot uses AI. Check for mistakes.
{ title }
</Heading>
</HStack>
) : undefined;
Comment on lines +74 to +87
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

The code passes a JSX element (an HStack containing an inner Heading as="h2") as the title prop to the Page component from @wordpress/admin-ui. The comment acknowledges this creates a double h2 heading (because Page's header internally also wraps the title in a Heading), which is an accessibility issue — it creates invalid nesting of h2 inside another h2. While the code comment references a Gutenberg PR for a future fix, it's worth noting this creates an accessibility violation in the current state and should be tracked as a known issue until the upstream fix lands.

Copilot uses AI. Check for mistakes.

const footer = showFooter && (
<Container horizontalSpacing={ 5 }>
<Col>
<JetpackFooter
moduleName={ moduleName }
moduleNameHref={ moduleNameHref }
menu={ optionalMenuItems }
useInternalLinks={ useInternalLinks }
/>
</Col>
</Container>
);

// When title is provided, use admin-ui Page for the full page layout.
if ( showHeader && composedTitle ) {
return (
<div className={ rootClassName }>
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The root div doesn't have a marker class to identify when the new admin-ui Page component is being used. The related PR #47332 mentions using :not(:has(.uses-new-admin-ui)) for scoping styles. Consider adding a class like 'uses-new-admin-ui' to the root div when the new header path is taken (when title is provided) to help with CSS scoping and debugging.

Suggested change
<div className={ rootClassName }>
<div className={ clsx( rootClassName, 'uses-new-admin-ui' ) }>

Copilot uses AI. Check for mistakes.
<Page
title={ composedTitle }
subTitle={ subTitle }
actions={ actions }
showSidebarToggle={ false }
>
{ tabs }
Comment on lines +111 to +112
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

The tabs prop is rendered as { tabs } directly inside the Page component as children (content area) rather than being passed as the tabs prop to @wordpress/admin-ui's Page component. The @wordpress/admin-ui Page component accepts a tabs prop to render tab navigation inside the sticky header (as seen in the forms package's Page component). By passing tabs as children, the tab navigation will end up in the scrollable content area rather than in the sticky header. For the Protect plugin which passes a Tabs component with padding-inline: 24px styling, this could cause visual layout issues.

Consider passing tabs as a prop to Page directly: <Page tabs={ tabs } ...>, which would position the tabs in the header as intended.

Suggested change
>
{ tabs }
tabs={ tabs }
>

Copilot uses AI. Check for mistakes.
<Container fluid horizontalSpacing={ 0 }>
<Col>{ children }</Col>
</Container>
{ footer }
</Page>
</div>
);
}
Comment on lines +102 to +120
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The PR title and description mention introducing a unified "AdminHeader component" but no separate AdminHeader component exists. The header functionality is integrated directly into the AdminPage component. While the implementation is functionally correct, consider updating the PR title and description to reflect that AdminPage was extended to support unified headers via the @wordpress/admin-ui Page component, rather than adding a new AdminHeader component.

Copilot uses AI. Check for mistakes.

// Legacy path: no title provided, render the classic header.
return (
<div className={ rootClassName }>
{ showHeader && (
Expand All @@ -85,18 +145,7 @@ const AdminPage: FC< AdminPageProps > = ( {
<Container fluid horizontalSpacing={ 0 }>
<Col>{ children }</Col>
</Container>
{ showFooter && (
<Container horizontalSpacing={ 5 }>
<Col>
<JetpackFooter
moduleName={ moduleName }
moduleNameHref={ moduleNameHref }
menu={ optionalMenuItems }
useInternalLinks={ useInternalLinks }
/>
</Col>
</Container>
) }
{ footer }
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@
background-color: var(--jp-white);
}

// Remove the admin-ui header border. Scoped to .admin-page so this only
// applies within our component. Ideally admin-ui would expose a prop or
// CSS custom property for this — revisit if the upstream API changes.
:global(.admin-ui-page__header) {
border-bottom: none;
position: relative;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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


// Normalize admin headers: implementation of admin-ui needs to
// comprehend old wp-admin floating containers, such as Hello Dolly.
:global(.admin-ui-page) {
clear: both;
}

.admin-page-header {
display: flex;
align-items: center;
Expand All @@ -26,3 +40,8 @@
color: #fff;
}
}

// Make Hello Dolly background white for Jetpack admin pages.
:global(.jetpack-admin-page #dolly) {
background-color: #fff;
}
29 changes: 28 additions & 1 deletion projects/js-packages/components/components/admin-page/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,37 @@ export type AdminPageProps = {
showHeader?: boolean;

/**
* Custom header. Optional
* Custom header. Optional.
* @deprecated Use `title` and `subTitle` props instead for the unified header.
*/
header?: ReactNode;

/**
* Product title displayed in the unified header (e.g. "Social", "Backup").
* When provided, renders the admin-ui Page header instead of the legacy header slot.
*/
title?: string;

/**
* Optional tagline displayed below the title in the unified header.
*/
subTitle?: string;

/**
* Custom logo element for the unified header. Defaults to JetpackLogo icon.
*/
logo?: ReactNode;

/**
* Action elements displayed on the right side of the unified header.
*/
actions?: ReactNode;

/**
* Tab navigation displayed below the title/tagline in the unified header.
*/
tabs?: ReactNode;
Comment on lines +26 to +50
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The PR description mentions adding breadcrumbs and badges props to AdminPage, but these props are not defined in the AdminPageProps type or used in the AdminPage component implementation. Consider either adding these props or updating the PR description to reflect what was actually implemented.

Copilot uses AI. Check for mistakes.

Comment on lines +26 to +51
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

The PR description states that AdminPage is extended with breadcrumbs and badges props, but neither prop is present in types.ts or implemented in index.tsx. The PR description appears to be inaccurate in this regard — either the description should be updated to remove the mention of these props, or they should be implemented.

Copilot uses AI. Check for mistakes.
/**
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

AdminPageProps exposes the new unified-header props, but it’s missing breadcrumbs and badges even though AdminHeader (and the PR description) supports them. This prevents consumers from passing these through via AdminPage and makes the public API incomplete. Consider adding breadcrumbs?: ReactNode and badges?: ReactNode to AdminPageProps (and wiring them through in AdminPage) or updating the PR description/props to match the actual supported surface.

Suggested change
/**
/**
* Breadcrumb navigation displayed in the unified header.
*/
breadcrumbs?: ReactNode;
/**
* Badge elements displayed alongside the title in the unified header.
*/
badges?: ReactNode;
/**

Copilot uses AI. Check for mistakes.
* Whether or not to display the Footer
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const ExternalIcon: FC = () => (
* @return {ReactNode} JetpackFooter component.
*/
const JetpackFooter: FC< JetpackFooterProps > = ( {
moduleName = __( 'Jetpack', 'jetpack-components' ),
moduleName = 'Jetpack' /** "Jetpack" is a product name, do not translate. */,
className,
moduleNameHref = 'https://jetpack.com',
menu,
Expand Down
1 change: 1 addition & 0 deletions projects/js-packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@automattic/jetpack-script-data": "workspace:*",
"@automattic/number-formatters": "workspace:*",
"@babel/runtime": "^7",
"@wordpress/admin-ui": "1.8.0",
"@wordpress/browserslist-config": "6.40.0",
"@wordpress/components": "32.2.0",
"@wordpress/compose": "7.40.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async function main() {
return;
}

for await ( const filePath of fs.glob( '**/*.scss', {
for await ( const filePath of fs.glob( '**/*.{scss,css}', {
cwd: packageRoot,
exclude: Array.from( IGNORED_DIRS, dir => `**/${ dir }/**` ),
} ) ) {
Comment on lines +53 to 56
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

This script now copies both .scss and .css files into build output, but the script name and npm script key are still copy-scss. Consider renaming to something like copy-styles (or adding a brief comment near the script definition) to avoid confusion for future maintainers.

Copilot uses AI. Check for mistakes.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Centralize admin-ui CSS bundling: add subpath to defaultRequestMap and mark CSS imports as sideEffects to prevent incorrect externalization and tree-shaking.
6 changes: 6 additions & 0 deletions projects/js-packages/webpack-config/src/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ const defaultRequestMap = {
external: 'JetpackConnection',
handle: 'jetpack-connection',
},
// Bundle admin-ui CSS with our assets. The JS side is already handled by the
// DependencyExtractionPlugin's BUNDLED_PACKAGES list, but the CSS subpath import
// doesn't match that exact-match check and would be incorrectly externalized.
'@wordpress/admin-ui/build-style/style.css': {
external: false,
},
Comment on lines +146 to +151
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This /build-style/* matching is probably something we could do upstream in DependencyExtractionPlugin. 🤔

};

const DependencyExtractionPlugin = ( { requestMap, ...options } = {} ) => {
Expand Down
3 changes: 3 additions & 0 deletions projects/js-packages/webpack-config/src/webpack/css-rule.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const CssRule = ( options = {} ) => {
'\\.(?:' + exts.map( ext => ext.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' ) ).join( '|' ) + ')$',
'i'
),
// CSS imports are always side effects (they inject styles into the DOM),
// so override any package.json "sideEffects: false" declarations.
sideEffects: true,
Comment on lines +30 to +32
Copy link
Copy Markdown
Member

@simison simison Mar 2, 2026

Choose a reason for hiding this comment

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

Can you elaborate a bit more why we needed this change int his PR?

use: [
MiniCssExtractLoader( options.MiniCssExtractLoader ),
CssLoader( {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Migrate admin page header to use unified AdminHeader component from jetpack-components.
81 changes: 0 additions & 81 deletions projects/packages/backup/src/js/components/Admin/header.js

This file was deleted.

Loading
Loading