Skip to content

Premium Analytics: use SectionHeader on the post and video detail pages - #51875

Merged
chihsuan merged 5 commits into
trunkfrom
change/wooa7s-1949-section-header-detail-pages
Sep 3, 2026
Merged

chihsuan merged 5 commits into
trunkfrom
change/wooa7s-1949-section-header-detail-pages

Conversation

@chihsuan

@chihsuan chihsuan commented Sep 2, 2026 •

Copy link
Copy Markdown
Member

Fixes WOOA7S-1949

Proposed changes

  • Use the shared SectionHeader on the Premium Analytics post and video detail pages, replacing the separate custom headers.
  • Extend SectionHeader with a visual, a subtitle, and a heading level so detail pages can use it as the page h1 while existing dashboard and report headers continue to work as before.
  • Move the post/video thumbnail or icon, title, subtitle, loading state, and shared date text into reusable header slots.
  • Let SectionHeader manage the responsive layout for the title and date controls. This removes the detail pages' separate width reservation and container wiring.
  • Give every summary state a page heading: a skeleton while it loads, a named state when a video fails or is missing, and an “Untitled post/page” fallback when a post resolves without a title.
  • Keep the date controls visible when a video cannot be loaded, and show the error or “not found” message below the header.

The API and naming for the new visual slot follow the @wordpress/admin-ui page-header pattern already used by these pages.

Related product discussion/links

Does this pull request change what data or activity we track or use?

No.

Testing instructions

  1. Open a post detail page at /wp-admin/admin.php?page=jetpack-premium-analytics-wp-admin#/post/<id>.
  2. Confirm the header shows the post thumbnail or type icon, an h1 title, its publish date/performance range, and the date controls.
  3. Repeat for /wp-admin/admin.php?page=jetpack-premium-analytics-wp-admin#/video/<id> and confirm the video poster or fallback icon appears with the same header layout.
  4. On each page, narrow and widen the browser window, then collapse and expand the wp-admin sidebar.
    • Above 812px of header width, the visual, title, and date controls should share one row.
    • The title should truncate with an ellipsis rather than cause horizontal scrolling.
    • Below that width, the date controls should move to a second row while the visual remains beside the title. There should be no horizontal page scrolling at any width.
  5. Hard-reload a post detail page. While its summary loads, confirm the header shows title and subtitle skeletons. On an email tab, confirm the envelope tile appears before the title resolves.
  6. Open a nonexistent video and a video that returns an error. Confirm the header keeps its date controls and names the state (Video not found or Video unavailable), with the message plus action (Back to Videos or Retry) below it.
  7. Check the existing dashboard and report pages. Confirm dashboard headers still pin and condense on scroll (including with reduced motion enabled), and report page headers are unchanged.

The detail-page breakpoint is 812px (720px + 72px visual + 20px gap). The dashboard and report-page breakpoint remains 720px.

Screenshots

Screenshot 2026-09-02 at 4 36 27 PM Screenshot 2026-09-02 at 4 37 34 PM
Screen.Recording.2026-09-02.at.4.37.40.PM.mov

Component-level screenshots from the two Storybook stories added in this PR (Packages/Premium Analytics/UI/SectionHeader).

Side by side, at 960px of header width:

Header at 960px

Stacked, at 700px:

Header at 700px

Notes for the reviewer

  • Detail pages now switch their date controls at the shared header's breakpoint instead of reserving 440px for the former summary card. This intentionally changes the width at which the controls abbreviate or stack.
  • SectionHeader's title stays required. Left optional, a page could spread empty slots into the header and silently lose its h1, which is what the video page did in its loading, error, and not-found states.
  • The loading state renders the page h1 around the skeleton, with a visually hidden “Loading…” label, and the header carries aria-busy while the summary resolves.
  • The shared visual slot now applies the same border-radius token to post thumbnails and video posters. This also makes the zero-radius theme consistent.
  • The slot factories and the header's date sentence take the package's own DateRange rather than their own inline spellings of it.
  • A follow-up PR will consolidate the remaining duplicated stage scaffolding (.scrollArea, .content, and .widgets).

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack or WordPress.com Site Helper), and enable the change/wooa7s-1949-section-header-detail-pages branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack change/wooa7s-1949-section-header-detail-pages
bin/jetpack-downloader test jetpack-mu-wpcom-plugin change/wooa7s-1949-section-header-detail-pages

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!


Jetpack plugin:

The Jetpack plugin has different release cadences depending on the platform:

  • WordPress.com Simple releases happen as soon as you deploy your changes after merging this PR (PCYsg-Jjm-p2).
  • WoA releases happen weekly.
  • Releases to self-hosted sites happen monthly:
    • Scheduled release: September 7, 2026

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Wpcomsh plugin:

  • Next scheduled release: Atomic deploys happen twice daily on weekdays (p9o2xV-2EN-p2)

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Premium Analytics plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.

@jp-launch-control

jp-launch-control Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

Code Coverage Summary

Coverage changed in 4 files.

File Coverage Δ% Δ Uncovered
projects/packages/premium-analytics/packages/ui/src/section-header/section-header.tsx 2/2 (100.00%) 0.00% 0 💚
projects/packages/premium-analytics/routes/post-detail/stage.tsx 25/27 (92.59%) 0.28% 0 💚
projects/packages/premium-analytics/routes/video-detail/stage.tsx 21/22 (95.45%) -0.70% 0 💚
projects/packages/premium-analytics/packages/ui/src/section-tabs/section-tabs.tsx 4/5 (80.00%) 20.00% -1 💚

7 files are newly checked for coverage. Only the first 5 are listed here.

File Coverage
projects/packages/premium-analytics/routes/post-detail/components/post-header-slots/post-header-slots.tsx 14/15 (93.33%) 💚
projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/detail-page-layout.tsx 2/2 (100.00%) 💚
projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/detail-page-shell.tsx 1/1 (100.00%) 💚
projects/packages/premium-analytics/packages/widgets-toolkit/src/components/detail-page/detail-page-tabs.tsx 1/1 (100.00%) 💚
projects/packages/premium-analytics/routes/detail-header.ts 7/7 (100.00%) 💚

Full summary · PHP report · JS report

The detail pages hand-rolled a third header beside the dashboard's and the
report pages': a flex row with its own wrap rules, plus a 440px constant
reserved for the date panel and a ref feeding it `containerElement`.

SectionHeader gains the two parameters those pages needed and nothing else
had — a `visual` slot and a `subTitle` — plus a `headingLevel`, since a
detail page's title is its `h1`. Naming and the decorative contract on the
visual follow `@wordpress/admin-ui`'s own page header, which the same files
already render.

The slot owns the visual's box, so both summary cards stop restating the
72px square and its radius. That also settles a latent difference between
them: the post thumbnail hard-coded `8px` where the video poster read
`--wpds-border-radius-lg`, which is 8px in the default theme and 0 in the
zero-radius one.

The pages keep the panel's responsive step-down through the header's
container query, so the external measurement retires with the migration.

On video detail, a failed or missing video now leaves the header with its
date controls alone and states the reason below it, where the widgets would
have been: an error is not something the shared header should know about.

WOOA7S-1949
@chihsuan
chihsuan force-pushed the change/wooa7s-1949-section-header-detail-pages branch from c0e74e7 to e276e4d Compare September 2, 2026 08:00
From review of the SectionHeader migration.

* `title` goes back to a required prop. Optional, it let a page render a
  header with no heading at all, which is what the video stage did in its
  loading, error and not-found states: it spread `{}` into the header and
  the page lost its `h1`. Required, the spread is a type error.
* `videoHeaderSlots` now owns every summary state, so the video page keeps
  its `h1`, its visual box and its date controls throughout: a skeleton
  while the summary resolves (matching the post page, WOOA7S-2059), and a
  named state when it fails. The notice below the header is unchanged.
* `postHeaderSlots` names an untitled or failed post rather than dropping
  the heading, and restores the `aria-busy` the summary card carried.
* Both slot factories and `performanceSentence` take the package's own
  `DateRange` instead of three inline spellings of it, two of which the
  real type could not be assigned to. `HeaderSlots` is now one exported
  `Pick` of the header's props rather than a hand-copy in each file.
* `.withVisual` no longer widens the gap between the title and the
  controls, so the stacking breakpoint constant matches the width the row
  actually needs.
* Cover the untitled and failed paths, the video slots (which had no test
  of their own), the date helpers' guards, and the stage wiring the mocks
  were hiding; run the header's date helpers under test-tz.
…ection-header-detail-pages

# Conflicts:
#	projects/packages/premium-analytics/routes/post-detail/stage.tsx
#	projects/packages/premium-analytics/routes/video-detail/stage.tsx
@chihsuan
chihsuan marked this pull request as ready for review September 2, 2026 08:47
@chihsuan
chihsuan requested a review from a team as a code owner September 2, 2026 08:47
@chihsuan chihsuan added [Status] Needs Review This PR is ready for review. and removed [Status] In Progress labels Sep 2, 2026
The fixture publishes at 08:00, so neither timezone test-tz runs shifts
its day. The entry would suggest coverage it does not give.
@chihsuan
chihsuan requested a review from dognose24 September 2, 2026 08:54
Nikschavan
Nikschavan previously approved these changes Sep 2, 2026

@Nikschavan Nikschavan left a comment

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.

Thank you, the changes look good. I left three non-blocking notes inline.

.title {
overflow: visible;
text-overflow: clip;
white-space: normal;

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.

The stacked layout relaxes white-space on .title only, so at a phone width the subtitle still runs on one line and the performance window is the part that gets cut: at 390px the line measures 435px in a 250px cell and ends at "Performan…". Letting .subTitle wrap inside section-header-stacked alongside .title would keep the window readable where the title already takes several lines.

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.

At a 390px viewport, this branch at 11317a3:

Post detail header at 390px: the subtitle ends in an ellipsis after "Performan…"

// bottom. A floor, not a height — the stacked layout lets the title wrap,
// and a fixed box would push the subtitle out from under it.
min-block-size: $section-header-visual-size;
justify-content: space-between;

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.

.withVisual .text keeps justify-content: space-between when the header has no subtitle, and with one flex child that puts the h1 at the top of the 72px cell: on the video not-found state the title centre sits at 157px against the icon centre at 173px, with the bottom half of the cell empty. justify-content: center plus margin-block-start: auto on .subTitle gives the same top/bottom split when both lines are present and centres a lone title.

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.

The video not-found state at 1280px, this branch at 11317a3:

Video not found header: the heading aligns to the top of the icon box with empty space below it


describe( 'formatPublishedDate', () => {
it( 'reads an offset-less value as site wall time', () => {
expect( formatPublishedDate( '2026-01-10T08:00:00' ) ).toBe( 'Jan 10, 2026' );

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 case cannot fail without toLocalTZ: the test script pins TZ=UTC, siteTimeZone() resolves to +00:00 under the default @wordpress/date settings, and a fixture at 08:00 lands on the same day in America/Los_Angeles and Asia/Tokyo as well, which is what the commit dropping routes/detail-header from test-tz observes. Pinning the site timezone to Asia/Tokyo with setSettings, as stats-queries.test.ts does for UTC, makes the same 08:00 fixture cross midnight in UTC, so the assertion turns on the conversion. Fine as a follow-up.

…etail stages (#51877)

* refactor: share one page layout between the post and video detail stages

Both stages carried their own copy of the same scaffold: a `.page`, a
`.scrollArea`, a `.content` band and a `.widgets` block that was byte-identical
down to its TODO. `DetailPageShell` / `DetailPageLayout` / `DetailPageSection`
now own that structure, following `ReportPageShell` / `ReportPageLayout` next
door, so the stages hand over their header slots, their date controls and their
widget content and nothing else.

The layout lives in `widgets-toolkit` beside `report-page` rather than under
`routes/`: wp-build treats every directory in `routes/` as a route to build, so
a shared folder there would be discovered as one.

`.widgets` folds into the section: `--wp-grid-gap` is inherited and the Card
rules are descendant selectors, so moving them one level out matches the same
elements without an extra wrapper around the grid.

Two differences the copies had drifted into, both invisible in the default
theme, are settled. The header's `padding-block` converges on 40px, written as
`--wpds-dimension-gap-3xl`, which the video page's comment records as the
design's breathing room. Post detail reaches the same 40px only once its tab
bar stops adding a bottom margin on top of it: #50540 zeroed that margin for
exactly this reason and #50562 restored it for a two-row header that no longer
exists, so 16px + 32px becomes 40px and the two pages finally agree. And the
shared `.content` end padding was a bare 24px restating
`--wpds-dimension-padding-2xl`.

`postHeaderSlots` and `videoHeaderSlots` each declared their own copy of the
slot shape; both now return `DetailPageHeaderSlots`, which the layout derives
from `SectionHeaderProps` so a required `title` and `busy` stay required.

`DetailPageTabs` re-exports the tab root beside the panel, so a detail route
reaches both through one package specifier — the constraint `report-page-tabs`
already documents. `DetailPageTabPanelProps` is declared and exported with its
`TabId` generic rather than leaving consumers to import the underlying UI type.

Drops the `display: flex` widget-chrome workaround: `@wordpress/widget-dashboard`
0.5.0, the version pinned here, ships the upstream fix
(WordPress/gutenberg#80570) in `.widget-chrome` itself.

The suites assert the classes the gutters, the grid gap and the Card padding
hang off, with a local style mock — which keeps them out of the shared no-mocks
group, per `tests/groups/README.md`.

WOOA7S-1949

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W4VNbKvETw12J6L1nCzixU

* change: settle the three review notes on the shared detail page layout

The post tab bar's `margin-block-end: 0` lands on the same element as
`SectionTabs`' own rule, both single-class, so it only won because the
toolkit's stylesheet is emitted first. Doubling the selector makes it win
outright, and an import reorder can no longer move the header spacing.

`DetailPageTabs` and `DetailPageTabPanel` move into `detail-page-tabs.tsx`,
matching `report-page-tabs` next door. The layout no longer imports
`SectionTabPanel`, so `video-detail/stage.test.tsx` drops the mock it carried
for a page with no tabs — a mock the suite already passed without.

`DetailPageShell`'s test mock swallowed `...pageProps`, leaving the one thing
the component does beyond `clsx` unasserted. The mock now captures the rest
props and the test checks `visual` and `breadcrumbs` reach `Page`; removing the
spread fails it.

WOOA7S-1949

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LouhrnbjGjdAK2BZqC2M7s

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chihsuan
chihsuan merged commit df431e9 into trunk Sep 3, 2026
80 checks passed
@chihsuan
chihsuan deleted the change/wooa7s-1949-section-header-detail-pages branch September 3, 2026 04:36
@github-actions github-actions Bot added [Status] UI Changes Add this to PRs that change the UI so documentation can be updated. and removed [Status] Needs Review This PR is ready for review. labels Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Premium Analytics [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Plugin] Premium Analytics [Plugin] Wpcomsh [Status] UI Changes Add this to PRs that change the UI so documentation can be updated. [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants