Podcast: add locked-preview component for Episodes + Stats#48885
Podcast: add locked-preview component for Episodes + Stats#48885robertbpugh wants to merge 2 commits into
Conversation
The forthcoming Premium feature-gate PRs render a plain upsell card for free users on the Episodes tab and on the Stats panel. This adds a richer locked-preview component that supersedes that card: - A sample-data preview (5 episode rows for Episodes; Downloads bar chart + Top episodes list + By app list for Stats) renders behind a 50%/4px blur and aria-hidden so it's purely visual. - A centered overlay card carries the upgrade title, description, and primary CTA to Premium checkout. - Keyboard: the CTA receives autoFocus on mount so a keyboard / screen reader user lands on the upgrade affordance, not on placeholder data; the wrapper handles Escape by blurring the active element so the user can tab back out to the dashboard tab list. - Static data only — no network calls for free users; no @automattic/charts dependency in the Stats variant so free users don't pull the heavy stats chunk. Component is added but call sites aren't flipped here: the Episodes call site lives in #48704 and the Stats premiumRequired flag in #48703, neither of which has merged yet. PR description spells out the follow-up wiring.
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
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:
If you have questions about anything, reach out in #jetpack-developers for guidance! |
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
Code Coverage SummaryThis PR did not change code coverage! That could be good or bad, depending on the situation. Everything covered before, and still is? Great! Nothing was covered before? Not so great. 🤷 |
Review verdict: READY (after fix)Scope reviewed: branch diff against trunk — 5 files in
Fix applied this passPass 1 CI failed on three ESLint findings in
Patched in Verification
SeverityNone CRITICAL, none MAJOR, none MINOR. |
There was a problem hiding this comment.
Pull request overview
Adds a new Podcast locked-preview UI intended for free users, showing blurred static Episodes/Stats previews with an upgrade CTA before the future gate call sites wire it in.
Changes:
- Adds the
LockedPreviewshell with variant-specific overlay copy and checkout CTA. - Adds static Episodes and Stats preview components plus scoped SCSS.
- Adds a package changelog entry.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
projects/packages/podcast/src/dashboard/locked-preview/index.tsx |
Adds the shared locked-preview shell and CTA behavior. |
projects/packages/podcast/src/dashboard/locked-preview/episodes-preview.tsx |
Adds a static blurred episode table preview. |
projects/packages/podcast/src/dashboard/locked-preview/stats-preview.tsx |
Adds static CSS-only stats preview modules. |
projects/packages/podcast/src/dashboard/locked-preview/style.scss |
Adds styles for the shell, overlay card, and preview variants. |
projects/packages/podcast/changelog/pods-locked-preview |
Adds the changelog entry for the new component. |
Comments suppressed due to low confidence (1)
projects/packages/podcast/src/dashboard/locked-preview/stats-preview.tsx:29
- These visible sample counts are hard-coded with US comma separators. Please format numeric placeholder values through the same localization path used by the live stats UI (or use skeleton placeholders) so the preview does not show locale-specific number formatting.
const SAMPLE_BY_APP: SampleRow[] = [
{ id: 1, label: 'Apple Podcasts', value: '2,140', pct: 100 },
{ id: 2, label: 'Spotify', value: '1,420', pct: 66 },
{ id: 3, label: 'Pocket Casts', value: '510', pct: 24 },
{ id: 4, label: 'Overcast', value: '320', pct: 15 },
| Significance: minor | ||
| Type: added | ||
|
|
||
| Add a locked-preview component for free users on the Episodes and Stats panels: a blurred sample of the gated content behind a centered upgrade overlay. Replaces the plain upsell card once the upstream feature-gate PRs land. |
| const active = wrapperRef.current?.ownerDocument.activeElement; | ||
| if ( active instanceof HTMLElement ) { | ||
| active.blur(); | ||
| } |
| const SAMPLE_TOP_EPISODES: SampleRow[] = [ | ||
| { id: 1, label: 'Episode 1 — the first conversation', value: '1,284', pct: 100 }, | ||
| { id: 2, label: 'Episode 2 — guest interview with a friend', value: '973', pct: 76 }, | ||
| { id: 3, label: 'Episode 3 — back-to-basics, why we started', value: '812', pct: 63 }, | ||
| { id: 4, label: 'Episode 4 — Q&A from our listeners', value: '604', pct: 47 }, |
| const SAMPLE_EPISODES: SampleEpisode[] = [ | ||
| { | ||
| id: 1, | ||
| title: 'Episode 1 — the first conversation', | ||
| duration: '42:18', | ||
| plays: '1,284', | ||
| date: 'May 1, 2026', | ||
| status: 'Published', | ||
| }, | ||
| { | ||
| id: 2, | ||
| title: 'Episode 2 — guest interview with a friend', | ||
| duration: '38:02', | ||
| plays: '973', | ||
| date: 'May 8, 2026', | ||
| status: 'Published', | ||
| }, | ||
| { | ||
| id: 3, | ||
| title: 'Episode 3 — back-to-basics, why we started', | ||
| duration: '51:47', | ||
| plays: '812', | ||
| date: 'May 15, 2026', | ||
| status: 'Published', | ||
| }, | ||
| { | ||
| id: 4, | ||
| title: 'Episode 4 — Q&A from our listeners', | ||
| duration: '29:33', | ||
| plays: '604', | ||
| date: 'May 22, 2026', | ||
| status: 'Published', | ||
| }, | ||
| { | ||
| id: 5, | ||
| title: 'Episode 5 — short bonus thoughts', | ||
| duration: '14:20', | ||
| plays: '450', | ||
| date: 'May 28, 2026', | ||
| status: 'Scheduled', |
Fixes #
Proposed changes
Adds a richer locked-preview UX for free users on the Episodes tab and Stats panel, superseding the plain upsell card the upstream gate PRs ship today.
src/dashboard/locked-preview/index.tsx— the shell. Renders the variant's sample-data preview behind a 50% / 4px blur (aria-hidden), with a centered overlay card carrying the upgrade title, description, and primary CTA to Premium checkout.episodes-preview.tsx— static 5-row episode table with thumbnail / title / duration / plays / date / status columns, mirroring the live DataViews shape.stats-preview.tsx— static Downloads bar chart (CSS-only, no@automattic/chartsdependency so free users don't pull the heavy stats chunk), Top episodes list, By app list.style.scss— shell, blur layer, overlay card, and the two preview variants. Uses CSS logical properties throughout.Accessibility
aria-hidden="true"and contains no focusable elements, so screen readers and keyboard users skip it entirely.autoFocuson mount so keyboard / screen reader users land on the upgrade affordance instead of placeholder data. (Per the spec's "focus trap on the CTA" — since the CTA is the only focusable element on the panel, focus naturally stays there; no synthetic trap needed.)role="region"+aria-labelledbypointing at the overlay title so screen readers announce the locked-preview as a labeled region.What this PR does NOT change
The Episodes / Stats call sites aren't flipped here — both live in not-yet-merged upstream PRs:
dashboard/upsell/index.tsx(the plain upsell card) and renders it indashboard/index.tsxwhenhasProductAccess()is false. Once Podcast: gate the Episodes dashboard tab on Premium product access (PODS-145) #48704 merges, swap that for<LockedPreview variant="episodes" />and removedashboard/upsell/. Sample patch:premiumRequiredtouseEpisodeStatsQuery. The Stats panel's gate-render (forthcoming PR) should render<LockedPreview variant="stats" />when the gate flips true.Distribution and Settings are explicitly out of scope per the spec — not gated.
Dependencies
hasProductAccesscheck).premiumRequiredflag → consumer wires up the LockedPreview render).Per Rob's override (out-of-office): "If jetpack#48702 hasn't merged, open PR 8 as draft anyway. Add 'Depends on: #48702' to the description."
Related product discussion/links
Does this pull request change what data or activity we track or use?
No. The component renders only hardcoded sample data and a checkout link.
Testing instructions
Because the call sites aren't wired here, you can't visit
/podcastand see the locked preview yet. Two options for visual review:<LockedPreview variant="episodes" />/<LockedPreview variant="stats" />somewhere in the dashboard temporarily to eyeball./podcaston a free / non-grandfathered site.When you do view it:
Notes for reviewer:
@automattic/chartsfor free users is the kind of free-plan footgun this UX is supposed to avoid in the first place.Spec excerpt (from the work order):