Skip to content
Merged
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
7 changes: 1 addition & 6 deletions .agents/skills/widget-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,7 @@ assume: namespace, text domain, and the dependency versions the package resolves
- Props are a named `type`/`interface` with each field documented on the type,
not echoed in `@param`. A component takes one typed tag
(`@param {Props} props - The component props.`) — never `@param props.<field>`
blocks.
- The same one-typed-tag rule applies to ANY function taking an object argument
(hooks, story helpers, test builders): `@param {UseXArgs} args - Hook
arguments.` with fields documented on the args type — never `@param
args.<field>` / `@param root0.<field>` blocks. Only scalar positional
parameters keep plain per-param tags (`@param interval - The interval.`).
blocks; plain functions keep positional `@param`s.
- Descriptions track the code: referenced symbols still exist, terminology is
consistent (`widget.json` ↔ `widget.ts` ↔ rendered strings), `@return` is accurate.
- Verify, don't guess: `jsdoc/require-param` is satisfied by that single typed
Expand Down
869 changes: 86 additions & 783 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

This file was deleted.

This file was deleted.

6 changes: 3 additions & 3 deletions projects/packages/premium-analytics/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@
"@wordpress/theme": "0.17.0",
"@wordpress/ui": "0.17.0",
"@wordpress/url": "4.50.0",
"@wordpress/widget-dashboard": "0.3.1-next.v.202607070741.0",
"@wordpress/widget-primitives": "0.3.1-next.v.202607070741.0",
"@wordpress/widget-dashboard": "0.2.0",
"@wordpress/widget-primitives": "0.2.0",
"clsx": "2.1.1",
"date-fns": "4.1.0",
"fast-deep-equal": "^3.1.3",
Expand All @@ -91,7 +91,7 @@
"@types/react": "18.3.28",
"@typescript/native-preview": "7.0.0-dev.20260225.1",
"@wordpress/base-styles": "10.2.0",
"@wordpress/build": "0.19.1-next.v.202607070741.0",
"@wordpress/build": "0.18.0",
"browserslist": "4.28.4",
"jest": "30.4.2",
"storybook": "10.4.6",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const WidgetCard = ( {
height,
border: '1px solid var(--wpds-color-stroke-surface-neutral-weak, #e0e0e0)',
borderRadius: 'var(--wpds-border-radius-md, 8px)',
background: 'var(--wpds-color-background-surface-neutral, #fff)',
background: 'var(--wpds-color-background-surface-neutral-strong, #fff)',
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function Dashboard(): JSX.Element {
DASHBOARD_NAME,
activeSection
);
const [ gridSettings ] = useDashboardGridSettings();
const [ gridSettings, setGridSettings ] = useDashboardGridSettings();

const widgetModules = useSelect(
select =>
Expand Down Expand Up @@ -74,6 +74,7 @@ function Dashboard(): JSX.Element {
onLayoutChange={ setLayout }
onLayoutReset={ resetLayout }
gridSettings={ gridSettings }
onGridSettingsChange={ setGridSettings }
editMode={ editMode }
onEditChange={ setEditMode }
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function PostDetail(): JSX.Element {
const tabs = useMemo( () => getPostDetailTabs(), [] );
const [ activeTab, setActiveTab ] = useActiveTab();
const [ layout, setLayout, resetLayout ] = usePostDetailTabLayout( activeTab );
const [ gridSettings ] = useDashboardGridSettings();
const [ gridSettings, setGridSettings ] = useDashboardGridSettings();

const summary = usePostSummary( postId );

Expand Down Expand Up @@ -92,6 +92,7 @@ function PostDetail(): JSX.Element {
onLayoutChange={ setLayout }
onLayoutReset={ resetLayout }
gridSettings={ gridSettings }
onGridSettingsChange={ setGridSettings }
editMode={ editMode }
onEditChange={ setEditMode }
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"@wordpress/i18n": "^6.9.0",
"@wordpress/icons": "^15.0.0",
"@wordpress/ui": "0.17.0",
"@wordpress/widget-primitives": "0.2.0",
"@wordpress/widget-primitives": "next",
"date-fns": "4.1.0",
"react": "18.3.1"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import type { WidgetRenderProps } from '@wordpress/widget-primitives';
// totals are all-time: the summary query takes no date params, so the picker's
// range and comparison state do not change what this widget shows.
type AllTimeStatsRenderAttributes = AllTimeStatsAttributes & Partial< ReportParamsFieldAttributes >;
type AllTimeStatsWidgetProps = WidgetRenderProps< AllTimeStatsRenderAttributes >;

/**
* The all-time summary carries dynamic WPCOM keys (`views`, `visitors`,
Expand Down Expand Up @@ -131,10 +130,13 @@ function AllTimeStatsReport() {
* range; report params still flow into WidgetRoot for parity with the other
* Stats widgets.
*
* @param {AllTimeStatsWidgetProps} props - The widget render props.
* @param props - Render props supplied by the widget host.
* @param props.attributes - Widget attributes.
* @return The rendered widget.
*/
export default function AllTimeStats( { attributes = {} }: AllTimeStatsWidgetProps ) {
export default function AllTimeStats( {
attributes = {},
}: WidgetRenderProps< AllTimeStatsRenderAttributes > ) {
return (
<WidgetRoot attributes={ attributes }>
<AllTimeStatsReport />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,15 @@ registerReportMocks();
const ALL_TIME_STATS_RENDER_MODULE = 'storybook/all-time-stats';

interface AllTimeStatsStoryControls {
/**
* Whether to include comparison report params.
*/
withComparison: boolean;
}

/**
* Renders the data-connected widget with report params derived from the
* date-range picker preset.
*
* @param {AllTimeStatsStoryControls} props - The story controls.
* @param props - The story controls.
* @param props.withComparison - Whether to include comparison report params.
* @return The rendered widget.
*/
function renderAllTimeStats( { withComparison }: AllTimeStatsStoryControls ) {
Expand Down Expand Up @@ -107,7 +105,8 @@ interface AllTimeStatsDashboardStoryProps
* Renders the data-connected widget through the shared dashboard harness, so it
* appears exactly as it does in product (framed card, sizing, edit mode).
*
* @param {AllTimeStatsDashboardStoryProps} props - The dashboard story controls.
* @param props - The dashboard story controls.
* @param props.withComparison - Whether to include comparison report params.
* @return The widget mounted inside the real `WidgetDashboard`.
*/
function AllTimeStatsDashboardStory( {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"@wordpress/i18n": "^6.9.0",
"@wordpress/icons": "^15.0.0",
"@wordpress/ui": "0.17.0",
"@wordpress/widget-primitives": "0.2.0",
"@wordpress/widget-primitives": "next",
"react": "18.3.1"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
type DataFormat,
type ReportParamsFieldAttributes,
} from '@jetpack-premium-analytics/widgets-toolkit';
import { __ } from '@wordpress/i18n';
import { __, sprintf } from '@wordpress/i18n';
import { arrowLeft, arrowRight, comment, paragraph, postList, starEmpty } from '@wordpress/icons';
import { Button, Icon, Text } from '@wordpress/ui';
import { useCallback, useMemo, useState } from 'react';
Expand All @@ -29,12 +29,6 @@ import type { WidgetRenderProps } from '@wordpress/widget-primitives';
// boundary (and Storybook may inject them) so the host contract holds.
type AnnualHighlightsRenderAttributes = AnnualHighlightsAttributes &
Partial< ReportParamsFieldAttributes >;
type AnnualHighlightsWidgetProps = WidgetRenderProps< AnnualHighlightsRenderAttributes >;

/**
* The enabled-metric flags from widget attributes, with defaults applied.
*/
type AnnualHighlightsReportProps = Required< AnnualHighlightsAttributes >;

const COUNT_FORMAT: DataFormat = {
type: 'number',
Expand All @@ -59,15 +53,19 @@ function sortYearsDescending( data?: StatsInsightsResponse ): StatsInsightsYear[
* comparison period, so each tile shows a bare formatted count. Which tiles
* appear is controlled by the per-metric visibility attributes.
*
* @param {AnnualHighlightsReportProps} props - The component props.
* @param props - The enabled-metric flags from widget attributes.
* @param props.showPosts - Whether the Posts tile is shown.
* @param props.showWords - Whether the Words tile is shown.
* @param props.showLikes - Whether the Likes tile is shown.
* @param props.showComments - Whether the Comments tile is shown.
* @return The widget content.
*/
function AnnualHighlightsReport( {
showPosts,
showWords,
showLikes,
showComments,
}: AnnualHighlightsReportProps ) {
}: Required< AnnualHighlightsAttributes > ) {
const { data, isLoading, isError } = useStatsInsights();

const years = useMemo( () => sortYearsDescending( data ), [ data ] );
Expand Down Expand Up @@ -153,34 +151,40 @@ function AnnualHighlightsReport( {

return (
<div className={ styles.root }>
<div className={ styles.yearNav }>
<Button
type="button"
variant="minimal"
tone="neutral"
size="small"
className={ styles.navButton }
onClick={ showOlderYear }
disabled={ ! canShowOlder }
aria-label={ __( 'Previous year', 'jetpack-premium-analytics' ) }
>
<Button.Icon icon={ arrowLeft } size={ 16 } />
</Button>

<Text className={ styles.yearLabel }>{ year.year }</Text>

<Button
type="button"
variant="minimal"
tone="neutral"
size="small"
className={ styles.navButton }
onClick={ showNewerYear }
disabled={ ! canShowNewer }
aria-label={ __( 'Next year', 'jetpack-premium-analytics' ) }
>
<Button.Icon icon={ arrowRight } size={ 16 } />
</Button>
<div className={ styles.header }>
<Text variant="heading-lg" render={ <h3 /> } className={ styles.title }>
{ sprintf(
/* translators: %s is a calendar year, e.g. "2026". */
__( '%s in review', 'jetpack-premium-analytics' ),
year.year
) }
</Text>
<div className={ styles.yearNav }>
<Button
type="button"
variant="minimal"
tone="neutral"
size="small"
className={ styles.navButton }
onClick={ showOlderYear }
disabled={ ! canShowOlder }
aria-label={ __( 'Previous year', 'jetpack-premium-analytics' ) }
>
<Button.Icon icon={ arrowLeft } size={ 16 } />
</Button>
<Button
type="button"
variant="minimal"
tone="neutral"
size="small"
className={ styles.navButton }
onClick={ showNewerYear }
disabled={ ! canShowNewer }
aria-label={ __( 'Next year', 'jetpack-premium-analytics' ) }
>
<Button.Icon icon={ arrowRight } size={ 16 } />
</Button>
</div>
</div>
{ tiles.length === 0 ? (
<Text className={ styles.placeholder }>
Expand Down Expand Up @@ -215,10 +219,13 @@ function AnnualHighlightsReport( {
* inner report. Host attributes are forwarded so any injected report params are
* preserved even though the insights endpoint is not period-scoped.
*
* @param {AnnualHighlightsWidgetProps} props - The widget render props.
* @param props - Render props supplied by the widget host.
* @param props.attributes - Widget attributes.
* @return The rendered widget.
*/
export default function AnnualHighlights( { attributes = {} }: AnnualHighlightsWidgetProps ) {
export default function AnnualHighlights( {
attributes = {},
}: WidgetRenderProps< AnnualHighlightsRenderAttributes > ) {
return (
<WidgetRoot attributes={ attributes }>
<AnnualHighlightsReport
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,24 @@ registerReportMocks();
const ANNUAL_HIGHLIGHTS_RENDER_MODULE = 'storybook/annual-highlights';

// Carry the widget's metadata, including the metric-visibility attribute schema
// so the dashboard story's settings drawer renders the real checkboxes.
// Presentation is left unset so the host frames the widget and renders its
// identity (title + icon), matching widget.json. The attribute schema is typed
// loosely on the widget definition, so it is cast to the WidgetType shape.
// so the dashboard story's settings drawer renders the real checkboxes, and
// surface the full-bleed presentation so it renders edge-to-edge with its own
// header instead of inside the host's framed chrome. The attribute schema is
// typed loosely on the widget definition, so it is cast to the WidgetType shape.
const storyWidgetType = {
name: widgetDefinition.name,
title: widgetDefinition.title,
icon: widgetDefinition.icon,
attributes: widgetDefinition.attributes as WidgetType[ 'attributes' ],
example: widgetDefinition.example,
presentation: 'full-bleed' as const,
};

interface AnnualHighlightsStoryControls {
/**
* Whether to inject comparison report params.
*/
withComparison: boolean;
/**
* Whether the Posts tile is shown.
*/
showPosts: boolean;
/**
* Whether the Words tile is shown.
*/
showWords: boolean;
/**
* Whether the Likes tile is shown.
*/
showLikes: boolean;
/**
* Whether the Comments tile is shown.
*/
showComments: boolean;
}

Expand All @@ -66,7 +52,12 @@ interface AnnualHighlightsStoryControls {
* host injects comparison params. The metric toggles mirror the widget's
* checkbox settings and hide/show the matching tile.
*
* @param {AnnualHighlightsStoryControls} props - Story controls.
* @param props - Story controls.
* @param props.withComparison - Whether to inject comparison report params.
* @param props.showPosts - Whether the Posts tile is shown.
* @param props.showWords - Whether the Words tile is shown.
* @param props.showLikes - Whether the Likes tile is shown.
* @param props.showComments - Whether the Comments tile is shown.
* @return The rendered widget.
*/
function renderAnnualHighlights( {
Expand Down Expand Up @@ -159,7 +150,12 @@ interface AnnualHighlightsDashboardStoryProps
/**
* Renders the real registered widget through the shared dashboard harness.
*
* @param {AnnualHighlightsDashboardStoryProps} props - Dashboard and widget controls.
* @param props - Dashboard and widget controls.
* @param props.withComparison - Whether to inject comparison report params.
* @param props.showPosts - Whether the Posts tile is shown.
* @param props.showWords - Whether the Words tile is shown.
* @param props.showLikes - Whether the Likes tile is shown.
* @param props.showComments - Whether the Comments tile is shown.
* @return The rendered dashboard with the widget.
*/
function AnnualHighlightsDashboardStory( {
Expand Down
Loading
Loading