From 48e023114c9092fe1e904ebc926f0a76bf2b7bcb Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Wed, 25 Feb 2026 01:26:45 -0300 Subject: [PATCH 01/13] Add AdminHeader component to jetpack-components for unified admin page headers. Co-Authored-By: Claude Opus 4.6 --- .../components/admin-header/index.tsx | 41 ++++++++++++++ .../components/admin-header/style.module.scss | 53 +++++++++++++++++++ .../components/admin-header/types.ts | 33 ++++++++++++ projects/js-packages/components/index.ts | 1 + 4 files changed, 128 insertions(+) create mode 100644 projects/js-packages/components/components/admin-header/index.tsx create mode 100644 projects/js-packages/components/components/admin-header/style.module.scss create mode 100644 projects/js-packages/components/components/admin-header/types.ts diff --git a/projects/js-packages/components/components/admin-header/index.tsx b/projects/js-packages/components/components/admin-header/index.tsx new file mode 100644 index 000000000000..b578c433da13 --- /dev/null +++ b/projects/js-packages/components/components/admin-header/index.tsx @@ -0,0 +1,41 @@ +import clsx from 'clsx'; +import JetpackLogo from '../jetpack-logo/index.tsx'; +import styles from './style.module.scss'; +import type { AdminHeaderProps } from './types.ts'; +import type { FC } from 'react'; + +/** + * Unified admin page header component. + * + * Renders a sticky header with logo, product title, optional tagline, + * actions, and tabs. Follows the `@wordpress/admin-ui` Page header pattern. + * + * @param {AdminHeaderProps} props - Component properties. + * @return {ReactNode} AdminHeader component. + */ +const AdminHeader: FC< AdminHeaderProps > = ( { + logo, + title, + tagline, + actions, + tabs, + className, +} ) => { + return ( +
+
+
+ + { logo || } + +

{ title }

+
+ { actions &&
{ actions }
} +
+ { tagline &&

{ tagline }

} + { tabs } +
+ ); +}; + +export default AdminHeader; diff --git a/projects/js-packages/components/components/admin-header/style.module.scss b/projects/js-packages/components/components/admin-header/style.module.scss new file mode 100644 index 000000000000..6cff7b2b9bf7 --- /dev/null +++ b/projects/js-packages/components/components/admin-header/style.module.scss @@ -0,0 +1,53 @@ +.admin-header { + padding: 16px 24px; + border-bottom: 1px solid #e0e0e0; + background: #fff; + position: sticky; + top: 0; + z-index: 1; +} + +.admin-header__title-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.admin-header__title { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; // allow truncation +} + +.admin-header__logo { + display: flex; + align-items: center; + flex-shrink: 0; +} + +.admin-header__product-name { + font-size: 15px; + font-weight: 500; + line-height: 32px; + margin: 0; + padding: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.admin-header__tagline { + margin: 4px 0 0; + font-size: 13px; + line-height: 1.4; + color: #757575; +} + +.admin-header__actions { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; +} diff --git a/projects/js-packages/components/components/admin-header/types.ts b/projects/js-packages/components/components/admin-header/types.ts new file mode 100644 index 000000000000..8fe76df341f2 --- /dev/null +++ b/projects/js-packages/components/components/admin-header/types.ts @@ -0,0 +1,33 @@ +import type { ReactNode } from 'react'; + +export type AdminHeaderProps = { + /** + * Custom logo element. Defaults to JetpackLogo icon (bolt only). + */ + logo?: ReactNode; + + /** + * Product title displayed next to the logo. + */ + title: string; + + /** + * Optional tagline displayed below the title row. + */ + tagline?: string; + + /** + * Optional action elements (buttons, links) displayed on the right side of the header. + */ + actions?: ReactNode; + + /** + * Optional tab navigation displayed below the title/tagline. + */ + tabs?: ReactNode; + + /** + * Additional CSS class name. + */ + className?: string; +}; diff --git a/projects/js-packages/components/index.ts b/projects/js-packages/components/index.ts index 5b2df114305e..9c9946a12b4e 100644 --- a/projects/js-packages/components/index.ts +++ b/projects/js-packages/components/index.ts @@ -34,6 +34,7 @@ export { default as NumberSlider } from './components/number-slider/index.tsx'; export { default as AdminSection } from './components/admin-section/basic/index.tsx'; export { default as AdminSectionHero } from './components/admin-section/hero/index.tsx'; export { default as AdminPage } from './components/admin-page/index.tsx'; +export { default as AdminHeader } from './components/admin-header/index.tsx'; export { default as DecorativeCard } from './components/decorative-card/index.tsx'; export { default as Col } from './components/layout/col/index.tsx'; export { default as Testimonials } from './components/testimonials/index.tsx'; From a9c29d9259e998d106bc0ef162f651ba83425d1a Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Wed, 25 Feb 2026 01:26:54 -0300 Subject: [PATCH 02/13] Add unified header props to AdminPage with backward-compatible legacy fallback. Co-Authored-By: Claude Opus 4.6 --- .../components/admin-page/index.tsx | 54 ++++++++++++------- .../components/components/admin-page/types.ts | 29 +++++++++- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/projects/js-packages/components/components/admin-page/index.tsx b/projects/js-packages/components/components/admin-page/index.tsx index c543af519dc6..5745de92ec18 100644 --- a/projects/js-packages/components/components/admin-page/index.tsx +++ b/projects/js-packages/components/components/admin-page/index.tsx @@ -2,6 +2,7 @@ import restApi from '@automattic/jetpack-api'; import { __, sprintf } from '@wordpress/i18n'; import clsx from 'clsx'; import { useEffect, useCallback } from 'react'; +import AdminHeader from '../admin-header/index.tsx'; import JetpackFooter from '../jetpack-footer/index.tsx'; import JetpackLogo from '../jetpack-logo/index.tsx'; import Col from '../layout/col/index.tsx'; @@ -32,6 +33,11 @@ const AdminPage: FC< AdminPageProps > = ( { apiNonce = '', optionalMenuItems, header, + title, + tagline, + logo, + actions, + tabs, } ) => { useEffect( () => { restApi.setApiRoot( apiRoot ); @@ -62,25 +68,35 @@ const AdminPage: FC< AdminPageProps > = ( { return (
- { showHeader && ( - - - { header ? header : } - { sandboxedDomain && ( - - API Sandboxed - - ) } - - + { showHeader && title ? ( + + ) : ( + showHeader && ( + + + { header ? header : } + { sandboxedDomain && ( + + API Sandboxed + + ) } + + + ) ) } { children } diff --git a/projects/js-packages/components/components/admin-page/types.ts b/projects/js-packages/components/components/admin-page/types.ts index 7e9e6ad59bf3..9d7219dec823 100644 --- a/projects/js-packages/components/components/admin-page/types.ts +++ b/projects/js-packages/components/components/admin-page/types.ts @@ -18,10 +18,37 @@ export type AdminPageProps = { showHeader?: boolean; /** - * Custom header. Optional + * Custom header. Optional. + * @deprecated Use `title` and `tagline` props instead for the unified header. */ header?: ReactNode; + /** + * Product title displayed in the unified header (e.g. "Social", "Backup"). + * When provided, renders the new AdminHeader instead of the legacy header slot. + */ + title?: string; + + /** + * Optional tagline displayed below the title in the unified header. + */ + tagline?: 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; + /** * Whether or not to display the Footer */ From 3a0292618a4f8d74718304606c4a2f9754d0a3aa Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Wed, 25 Feb 2026 01:27:02 -0300 Subject: [PATCH 03/13] Migrate publicize admin page to use unified AdminHeader via title prop. Co-Authored-By: Claude Opus 4.6 --- .../_inc/components/admin-page/index.tsx | 26 +++++++-- .../admin-page/test/page-header.test.jsx | 58 ------------------- 2 files changed, 22 insertions(+), 62 deletions(-) delete mode 100644 projects/packages/publicize/_inc/components/admin-page/test/page-header.test.jsx diff --git a/projects/packages/publicize/_inc/components/admin-page/index.tsx b/projects/packages/publicize/_inc/components/admin-page/index.tsx index c10b78feb15b..ca15dae8a312 100644 --- a/projects/packages/publicize/_inc/components/admin-page/index.tsx +++ b/projects/packages/publicize/_inc/components/admin-page/index.tsx @@ -8,6 +8,7 @@ import { } from '@automattic/jetpack-components'; import { useConnection } from '@automattic/jetpack-connection'; import { + getMyJetpackUrl, isJetpackSelfHostedSite, isSimpleSite, siteHasFeature, @@ -15,13 +16,13 @@ import { } from '@automattic/jetpack-script-data'; import { shouldUseInternalLinks } from '@automattic/jetpack-shared-extension-utils'; import { useSelect } from '@wordpress/data'; -import { useState, useCallback } from '@wordpress/element'; +import { createInterpolateElement, useState, useCallback } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; import { store as socialStore } from '../../social-store'; import { features, getSocialScriptData, hasSocialPaidFeatures } from '../../utils'; import ConnectionScreen from './connection-screen'; import Header from './header'; import InfoSection from './info-section'; -import AdminPageHeader from './page-header'; import './styles.module.scss'; import PricingPage from './pricing-page'; import SupportSection from './support-section'; @@ -65,7 +66,8 @@ export const SocialAdminPage = () => { return ( @@ -78,10 +80,26 @@ export const SocialAdminPage = () => { ); } + const licenseAction = + ! hasSocialPaidFeatures() && isJetpackSite + ? createInterpolateElement( + __( + 'Already have an existing plan or license key? Click here to get started', + 'jetpack-publicize-pkg' + ), + { + a: , + } + ) + : null; + return ( } + title={ __( 'Social', 'jetpack-publicize-pkg' ) } + // should we add a tagline? Page seems already crowded + // tagline={ __( 'Share your posts with your social media network.', 'jetpack-publicize-pkg' ) } + actions={ licenseAction } showFooter={ isJetpackSite } useInternalLinks={ shouldUseInternalLinks() } > diff --git a/projects/packages/publicize/_inc/components/admin-page/test/page-header.test.jsx b/projects/packages/publicize/_inc/components/admin-page/test/page-header.test.jsx deleted file mode 100644 index f74f6e861369..000000000000 --- a/projects/packages/publicize/_inc/components/admin-page/test/page-header.test.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import { clearMockedScriptData, mockScriptData } from '../../../utils/test-utils'; -import AdminPageHeader from '../page-header'; - -describe( 'AdminPageHeader', () => { - beforeEach( () => { - mockScriptData(); - } ); - - afterEach( () => { - clearMockedScriptData(); - } ); - it( 'should show license text when no paid features and is Jetpack site', () => { - render( ); - expect( - screen.getByText( /Already have an existing plan or license key\?/i ) - ).toBeInTheDocument(); - expect( screen.getByRole( 'link' ) ).toHaveAttribute( - 'href', - expect.stringContaining( 'admin.php?page=my-jetpack#/add-license' ) - ); - } ); - - it( 'should not show license text when has paid features', () => { - mockScriptData( { - site: { - plan: { - features: { - active: [ 'social-enhanced-publishing' ], - }, - }, - }, - } ); - render( ); - expect( - screen.queryByText( /Already have an existing plan or license key\?/i ) - ).not.toBeInTheDocument(); - clearMockedScriptData(); - } ); - - it( 'should not show license text when not a Jetpack site', () => { - mockScriptData( { - site: { - host: 'wpcom', - plan: { - features: { - active: [ 'social-enhanced-publishing' ], - }, - }, - }, - } ); - render( ); - expect( - screen.queryByText( /Already have an existing plan or license key\?/i ) - ).not.toBeInTheDocument(); - clearMockedScriptData(); - } ); -} ); From d4664258bb70b5af63dc43e8dd8279ed186a0821 Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Wed, 25 Feb 2026 01:32:32 -0300 Subject: [PATCH 04/13] Refactor AdminHeader to use VStack/HStack/Heading matching @wordpress/admin-ui pattern. Co-Authored-By: Claude Opus 4.6 --- .../components/admin-header/index.tsx | 50 +++++++++++++------ .../components/admin-header/style.module.scss | 45 ++--------------- .../components/admin-header/types.ts | 14 +++++- 3 files changed, 51 insertions(+), 58 deletions(-) diff --git a/projects/js-packages/components/components/admin-header/index.tsx b/projects/js-packages/components/components/admin-header/index.tsx index b578c433da13..346904ff5bf2 100644 --- a/projects/js-packages/components/components/admin-header/index.tsx +++ b/projects/js-packages/components/components/admin-header/index.tsx @@ -1,3 +1,8 @@ +import { + __experimentalVStack as VStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis + __experimentalHStack as HStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis + __experimentalHeading as Heading, // eslint-disable-line @wordpress/no-unsafe-wp-apis +} from '@wordpress/components'; import clsx from 'clsx'; import JetpackLogo from '../jetpack-logo/index.tsx'; import styles from './style.module.scss'; @@ -7,7 +12,7 @@ import type { FC } from 'react'; /** * Unified admin page header component. * - * Renders a sticky header with logo, product title, optional tagline, + * Renders a sticky header with logo, product title, optional subtitle, * actions, and tabs. Follows the `@wordpress/admin-ui` Page header pattern. * * @param {AdminHeaderProps} props - Component properties. @@ -16,25 +21,40 @@ import type { FC } from 'react'; const AdminHeader: FC< AdminHeaderProps > = ( { logo, title, - tagline, + subTitle, actions, - tabs, + tabs = null, className, + breadcrumbs = null, + badges = null, } ) => { + const classes = clsx( styles[ 'admin-header' ], className ); return ( -
-
-
- - { logo || } - -

{ title }

-
- { actions &&
{ actions }
} -
- { tagline &&

{ tagline }

} + + + + { title && ( + + { logo || } + + { title } + + + ) } + { breadcrumbs } + { badges } + + + { actions } + + + { subTitle &&

{ subTitle }

} { tabs } -
+ ); }; diff --git a/projects/js-packages/components/components/admin-header/style.module.scss b/projects/js-packages/components/components/admin-header/style.module.scss index 6cff7b2b9bf7..57710993684f 100644 --- a/projects/js-packages/components/components/admin-header/style.module.scss +++ b/projects/js-packages/components/components/admin-header/style.module.scss @@ -7,47 +7,10 @@ z-index: 1; } -.admin-header__title-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; -} - -.admin-header__title { - display: flex; - align-items: center; - gap: 8px; - min-width: 0; // allow truncation -} - -.admin-header__logo { - display: flex; - align-items: center; - flex-shrink: 0; -} - -.admin-header__product-name { - font-size: 15px; - font-weight: 500; - line-height: 32px; - margin: 0; - padding: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.admin-header__tagline { - margin: 4px 0 0; +.admin-header .admin-header-subtitle { + padding-block-end: 8px; + color: #757575; font-size: 13px; line-height: 1.4; - color: #757575; -} - -.admin-header__actions { - display: flex; - align-items: center; - gap: 8px; - flex-shrink: 0; + margin: 0; } diff --git a/projects/js-packages/components/components/admin-header/types.ts b/projects/js-packages/components/components/admin-header/types.ts index 8fe76df341f2..cd2940a4b5b8 100644 --- a/projects/js-packages/components/components/admin-header/types.ts +++ b/projects/js-packages/components/components/admin-header/types.ts @@ -12,9 +12,19 @@ export type AdminHeaderProps = { title: string; /** - * Optional tagline displayed below the title row. + * Optional subtitle displayed below the title row. */ - tagline?: string; + subTitle?: string; + + /** + * Optional breadcrumb elements displayed next to the title. + */ + breadcrumbs?: ReactNode; + + /** + * Optional badge elements displayed next to the title. + */ + badges?: ReactNode; /** * Optional action elements (buttons, links) displayed on the right side of the header. From 26e75cd45a9ce7daff508de84793b24121519efe Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Wed, 25 Feb 2026 01:32:40 -0300 Subject: [PATCH 05/13] Rename tagline prop to subTitle across AdminPage and publicize for admin-ui alignment. Co-Authored-By: Claude Opus 4.6 --- .../js-packages/components/components/admin-page/index.tsx | 4 ++-- .../js-packages/components/components/admin-page/types.ts | 2 +- .../packages/publicize/_inc/components/admin-page/index.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/projects/js-packages/components/components/admin-page/index.tsx b/projects/js-packages/components/components/admin-page/index.tsx index 5745de92ec18..d0bad43689b2 100644 --- a/projects/js-packages/components/components/admin-page/index.tsx +++ b/projects/js-packages/components/components/admin-page/index.tsx @@ -34,7 +34,7 @@ const AdminPage: FC< AdminPageProps > = ( { optionalMenuItems, header, title, - tagline, + subTitle, logo, actions, tabs, @@ -72,7 +72,7 @@ const AdminPage: FC< AdminPageProps > = ( { diff --git a/projects/js-packages/components/components/admin-page/types.ts b/projects/js-packages/components/components/admin-page/types.ts index 9d7219dec823..a93767c9e63c 100644 --- a/projects/js-packages/components/components/admin-page/types.ts +++ b/projects/js-packages/components/components/admin-page/types.ts @@ -32,7 +32,7 @@ export type AdminPageProps = { /** * Optional tagline displayed below the title in the unified header. */ - tagline?: string; + subTitle?: string; /** * Custom logo element for the unified header. Defaults to JetpackLogo icon. diff --git a/projects/packages/publicize/_inc/components/admin-page/index.tsx b/projects/packages/publicize/_inc/components/admin-page/index.tsx index ca15dae8a312..c692bc892416 100644 --- a/projects/packages/publicize/_inc/components/admin-page/index.tsx +++ b/projects/packages/publicize/_inc/components/admin-page/index.tsx @@ -97,8 +97,8 @@ export const SocialAdminPage = () => { Date: Wed, 25 Feb 2026 02:08:28 -0300 Subject: [PATCH 06/13] Add @wordpress/admin-ui dependency to jetpack-components. Co-Authored-By: Claude Opus 4.6 --- pnpm-lock.yaml | 3 +++ projects/js-packages/components/package.json | 1 + 2 files changed, 4 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9db70934e007..6320b407a48e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -536,6 +536,9 @@ importers: '@babel/runtime': specifier: ^7 version: 7.28.6 + '@wordpress/admin-ui': + specifier: 1.8.0 + version: 1.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@wordpress/browserslist-config': specifier: 6.40.0 version: 6.40.0 diff --git a/projects/js-packages/components/package.json b/projects/js-packages/components/package.json index a349d5d31e40..1713f5b0eeb3 100644 --- a/projects/js-packages/components/package.json +++ b/projects/js-packages/components/package.json @@ -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", From 9c6bbaccbc5a89256521b5ac30551515cc29e374 Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Wed, 25 Feb 2026 02:08:51 -0300 Subject: [PATCH 07/13] Refactor AdminHeader to wrap @wordpress/admin-ui Page component. Co-Authored-By: Claude Opus 4.6 --- .../components/admin-header/index.tsx | 53 +++++++++---------- .../components/admin-header/style.module.scss | 3 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/projects/js-packages/components/components/admin-header/index.tsx b/projects/js-packages/components/components/admin-header/index.tsx index 346904ff5bf2..e896d011b636 100644 --- a/projects/js-packages/components/components/admin-header/index.tsx +++ b/projects/js-packages/components/components/admin-header/index.tsx @@ -1,7 +1,7 @@ +import { Page } from '@wordpress/admin-ui'; import { - __experimentalVStack as VStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis - __experimentalHStack as HStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis __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 clsx from 'clsx'; import JetpackLogo from '../jetpack-logo/index.tsx'; @@ -13,7 +13,7 @@ import type { FC } from 'react'; * Unified admin page header component. * * Renders a sticky header with logo, product title, optional subtitle, - * actions, and tabs. Follows the `@wordpress/admin-ui` Page header pattern. + * actions, and tabs. Wraps the `@wordpress/admin-ui` Page component. * * @param {AdminHeaderProps} props - Component properties. * @return {ReactNode} AdminHeader component. @@ -29,32 +29,31 @@ const AdminHeader: FC< AdminHeaderProps > = ( { badges = null, } ) => { const classes = clsx( styles[ 'admin-header' ], className ); + + // While admin-ui Page has a title prop, it fails to render both the logo and + // text. Internally it tries to accommodate both inside Heading. + // Composing here with Heading as it is on admin-ui Page. + const composedTitle = title ? ( + + { logo || } + + { title } + + + ) : undefined; + return ( - - - - { title && ( - - { logo || } - - { title } - - - ) } - { breadcrumbs } - { badges } - - - { actions } - - - { subTitle &&

{ subTitle }

} + { tabs } -
+ ); }; diff --git a/projects/js-packages/components/components/admin-header/style.module.scss b/projects/js-packages/components/components/admin-header/style.module.scss index 57710993684f..d57162ee328d 100644 --- a/projects/js-packages/components/components/admin-header/style.module.scss +++ b/projects/js-packages/components/components/admin-header/style.module.scss @@ -7,7 +7,8 @@ z-index: 1; } -.admin-header .admin-header-subtitle { +/* stylelint-disable-next-line selector-class-pattern */ +.admin-header :global(.admin-ui-page__header-subtitle) { padding-block-end: 8px; color: #757575; font-size: 13px; From 3ed2b64cbefcf3650024e87696c0921dbd142a33 Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Wed, 25 Feb 2026 02:10:42 -0300 Subject: [PATCH 08/13] Remove unused AdminHeader style.module.scss, styles come from admin-ui. Co-Authored-By: Claude Opus 4.6 --- .../components/admin-header/index.tsx | 4 +--- .../components/admin-header/style.module.scss | 17 ----------------- 2 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 projects/js-packages/components/components/admin-header/style.module.scss diff --git a/projects/js-packages/components/components/admin-header/index.tsx b/projects/js-packages/components/components/admin-header/index.tsx index e896d011b636..a6a763e5eac7 100644 --- a/projects/js-packages/components/components/admin-header/index.tsx +++ b/projects/js-packages/components/components/admin-header/index.tsx @@ -3,9 +3,7 @@ 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 clsx from 'clsx'; import JetpackLogo from '../jetpack-logo/index.tsx'; -import styles from './style.module.scss'; import type { AdminHeaderProps } from './types.ts'; import type { FC } from 'react'; @@ -28,7 +26,7 @@ const AdminHeader: FC< AdminHeaderProps > = ( { breadcrumbs = null, badges = null, } ) => { - const classes = clsx( styles[ 'admin-header' ], className ); + const classes = className; // While admin-ui Page has a title prop, it fails to render both the logo and // text. Internally it tries to accommodate both inside Heading. diff --git a/projects/js-packages/components/components/admin-header/style.module.scss b/projects/js-packages/components/components/admin-header/style.module.scss deleted file mode 100644 index d57162ee328d..000000000000 --- a/projects/js-packages/components/components/admin-header/style.module.scss +++ /dev/null @@ -1,17 +0,0 @@ -.admin-header { - padding: 16px 24px; - border-bottom: 1px solid #e0e0e0; - background: #fff; - position: sticky; - top: 0; - z-index: 1; -} - -/* stylelint-disable-next-line selector-class-pattern */ -.admin-header :global(.admin-ui-page__header-subtitle) { - padding-block-end: 8px; - color: #757575; - font-size: 13px; - line-height: 1.4; - margin: 0; -} From 4c1729485b582eb7c3c726d0fdb78d4e731dcdf9 Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Wed, 25 Feb 2026 02:10:52 -0300 Subject: [PATCH 09/13] Delete unused publicize AdminPageHeader, replaced by unified AdminHeader. Co-Authored-By: Claude Opus 4.6 --- .../admin-page/page-header/index.jsx | 34 ------------------- .../components/admin-page/page-header/logo.js | 22 ------------ .../admin-page/page-header/styles.module.scss | 10 ------ 3 files changed, 66 deletions(-) delete mode 100644 projects/packages/publicize/_inc/components/admin-page/page-header/index.jsx delete mode 100644 projects/packages/publicize/_inc/components/admin-page/page-header/logo.js delete mode 100644 projects/packages/publicize/_inc/components/admin-page/page-header/styles.module.scss diff --git a/projects/packages/publicize/_inc/components/admin-page/page-header/index.jsx b/projects/packages/publicize/_inc/components/admin-page/page-header/index.jsx deleted file mode 100644 index bbeb0c2ae4f2..000000000000 --- a/projects/packages/publicize/_inc/components/admin-page/page-header/index.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import { getMyJetpackUrl, isJetpackSelfHostedSite } from '@automattic/jetpack-script-data'; -import { createInterpolateElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { hasSocialPaidFeatures } from '../../../utils'; -import Logo from './logo'; -import styles from './styles.module.scss'; - -const AdminPageHeader = () => { - const isJetpackSite = isJetpackSelfHostedSite(); - - return ( -
- ); -}; - -export default AdminPageHeader; diff --git a/projects/packages/publicize/_inc/components/admin-page/page-header/logo.js b/projects/packages/publicize/_inc/components/admin-page/page-header/logo.js deleted file mode 100644 index fa319c65f09a..000000000000 --- a/projects/packages/publicize/_inc/components/admin-page/page-header/logo.js +++ /dev/null @@ -1,22 +0,0 @@ -const Logo = ( { height = 40 } ) => ( - - - - - - -); - -export default Logo; diff --git a/projects/packages/publicize/_inc/components/admin-page/page-header/styles.module.scss b/projects/packages/publicize/_inc/components/admin-page/page-header/styles.module.scss deleted file mode 100644 index 05a64bb759a4..000000000000 --- a/projects/packages/publicize/_inc/components/admin-page/page-header/styles.module.scss +++ /dev/null @@ -1,10 +0,0 @@ -.header { - display: flex; - justify-content: space-between; - flex-wrap: wrap; - gap: calc(var(--horizontal-spacing) * 3); - - .logo { - flex-shrink: 0; - } -} From 8f91ee2cc6bf2d25a4479898b0dee5d2563a8a06 Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Wed, 25 Feb 2026 02:17:49 -0300 Subject: [PATCH 10/13] Add changelog entries for jetpack-components and publicize. Co-Authored-By: Claude Opus 4.6 --- .../components/changelog/update-normalize-admin-page-headers | 4 ++++ .../publicize/changelog/update-normalize-admin-page-headers | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 projects/js-packages/components/changelog/update-normalize-admin-page-headers create mode 100644 projects/packages/publicize/changelog/update-normalize-admin-page-headers diff --git a/projects/js-packages/components/changelog/update-normalize-admin-page-headers b/projects/js-packages/components/changelog/update-normalize-admin-page-headers new file mode 100644 index 000000000000..ad02bc7be2d0 --- /dev/null +++ b/projects/js-packages/components/changelog/update-normalize-admin-page-headers @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Add AdminHeader component wrapping @wordpress/admin-ui Page for unified admin page headers. diff --git a/projects/packages/publicize/changelog/update-normalize-admin-page-headers b/projects/packages/publicize/changelog/update-normalize-admin-page-headers new file mode 100644 index 000000000000..179dd8abc914 --- /dev/null +++ b/projects/packages/publicize/changelog/update-normalize-admin-page-headers @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Migrate admin page header to use unified AdminHeader component. From b6aa07230a6ff65bfe2dbc225fe2d8a89a262b7f Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Wed, 25 Feb 2026 11:22:20 -0300 Subject: [PATCH 11/13] Address review feedback: fix deprecated prop name, export types, remove redundant props and comments. Co-Authored-By: Claude Opus 4.6 --- projects/js-packages/components/components/admin-page/types.ts | 2 +- projects/js-packages/components/index.ts | 1 + .../packages/publicize/_inc/components/admin-page/index.tsx | 3 --- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/projects/js-packages/components/components/admin-page/types.ts b/projects/js-packages/components/components/admin-page/types.ts index a93767c9e63c..bec390ae496d 100644 --- a/projects/js-packages/components/components/admin-page/types.ts +++ b/projects/js-packages/components/components/admin-page/types.ts @@ -19,7 +19,7 @@ export type AdminPageProps = { /** * Custom header. Optional. - * @deprecated Use `title` and `tagline` props instead for the unified header. + * @deprecated Use `title` and `subTitle` props instead for the unified header. */ header?: ReactNode; diff --git a/projects/js-packages/components/index.ts b/projects/js-packages/components/index.ts index 9c9946a12b4e..7dc51e617bbc 100644 --- a/projects/js-packages/components/index.ts +++ b/projects/js-packages/components/index.ts @@ -35,6 +35,7 @@ export { default as AdminSection } from './components/admin-section/basic/index. export { default as AdminSectionHero } from './components/admin-section/hero/index.tsx'; export { default as AdminPage } from './components/admin-page/index.tsx'; export { default as AdminHeader } from './components/admin-header/index.tsx'; +export type { AdminHeaderProps } from './components/admin-header/types.ts'; export { default as DecorativeCard } from './components/decorative-card/index.tsx'; export { default as Col } from './components/layout/col/index.tsx'; export { default as Testimonials } from './components/testimonials/index.tsx'; diff --git a/projects/packages/publicize/_inc/components/admin-page/index.tsx b/projects/packages/publicize/_inc/components/admin-page/index.tsx index c692bc892416..cbe11e9f27c3 100644 --- a/projects/packages/publicize/_inc/components/admin-page/index.tsx +++ b/projects/packages/publicize/_inc/components/admin-page/index.tsx @@ -67,7 +67,6 @@ export const SocialAdminPage = () => { @@ -97,8 +96,6 @@ export const SocialAdminPage = () => { Date: Wed, 25 Feb 2026 12:37:18 -0300 Subject: [PATCH 12/13] add subtitle as in design MVP --- .../packages/publicize/_inc/components/admin-page/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects/packages/publicize/_inc/components/admin-page/index.tsx b/projects/packages/publicize/_inc/components/admin-page/index.tsx index cbe11e9f27c3..b0ef6e2d3ca1 100644 --- a/projects/packages/publicize/_inc/components/admin-page/index.tsx +++ b/projects/packages/publicize/_inc/components/admin-page/index.tsx @@ -67,6 +67,7 @@ export const SocialAdminPage = () => { @@ -96,6 +97,7 @@ export const SocialAdminPage = () => { Date: Wed, 25 Feb 2026 15:44:57 -0300 Subject: [PATCH 13/13] Bundle @wordpress/admin-ui CSS via proxy file to avoid DependencyExtractionPlugin externalization. Co-Authored-By: Claude Opus 4.6 --- .../components/admin-header/admin-ui-styles.css | 8 ++++++++ .../components/components/admin-header/index.tsx | 1 + .../js-packages/components/tools/copy-scss-to-build.mjs | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 projects/js-packages/components/components/admin-header/admin-ui-styles.css diff --git a/projects/js-packages/components/components/admin-header/admin-ui-styles.css b/projects/js-packages/components/components/admin-header/admin-ui-styles.css new file mode 100644 index 000000000000..b9b1bf26cef7 --- /dev/null +++ b/projects/js-packages/components/components/admin-header/admin-ui-styles.css @@ -0,0 +1,8 @@ +/* Proxy file to import @wordpress/admin-ui styles. + * + * Importing the CSS via @import inside a .css file avoids the + * @wordpress/dependency-extraction-webpack-plugin externalization that + * breaks direct JS `import '@wordpress/admin-ui/build-style/style.css'`. + * css-loader resolves @import independently of the externals plugin. + */ +@import "@wordpress/admin-ui/build-style/style.css"; diff --git a/projects/js-packages/components/components/admin-header/index.tsx b/projects/js-packages/components/components/admin-header/index.tsx index a6a763e5eac7..516729064504 100644 --- a/projects/js-packages/components/components/admin-header/index.tsx +++ b/projects/js-packages/components/components/admin-header/index.tsx @@ -1,4 +1,5 @@ import { Page } from '@wordpress/admin-ui'; +import './admin-ui-styles.css'; import { __experimentalHeading as Heading, // eslint-disable-line @wordpress/no-unsafe-wp-apis __experimentalHStack as HStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis diff --git a/projects/js-packages/components/tools/copy-scss-to-build.mjs b/projects/js-packages/components/tools/copy-scss-to-build.mjs index f2622ba37f5b..505e73b64612 100644 --- a/projects/js-packages/components/tools/copy-scss-to-build.mjs +++ b/projects/js-packages/components/tools/copy-scss-to-build.mjs @@ -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 }/**` ), } ) ) {