From dfc56599c0a2f11109328966e3241eb326ebb7f1 Mon Sep 17 00:00:00 2001 From: ilonagl Date: Thu, 30 Apr 2026 21:12:23 +0300 Subject: [PATCH 1/9] Activity Log: default to Table layout and fix free-tier upsell layout - Flip DEFAULT_VIEW.type from 'activity' to 'table' so new visitors land on the Date / Event / User columns instead of the timeline. Existing localStorage-persisted views are preserved by usePersistentView. - Forward upsell-callout.scss from the main entry style.scss via @use instead of relying on the side-effect import in UpsellCallout.tsx, so the rules land in the same chunk as the wrapper styles and the flex layout (image left, copy right on >= 782px) actually applies. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../default-table-view-and-fix-upsell-styles | 4 ++++ .../src/js/components/ActivityLog/UpsellCallout.tsx | 3 ++- .../src/js/components/ActivityLog/views.ts | 13 +++---------- projects/packages/activity-log/src/js/style.scss | 5 +++++ .../changelog/fix-activity-log-default-and-upsell | 4 ++++ 5 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 projects/packages/activity-log/changelog/default-table-view-and-fix-upsell-styles create mode 100644 projects/plugins/jetpack/changelog/fix-activity-log-default-and-upsell diff --git a/projects/packages/activity-log/changelog/default-table-view-and-fix-upsell-styles b/projects/packages/activity-log/changelog/default-table-view-and-fix-upsell-styles new file mode 100644 index 000000000000..a0959031aeb0 --- /dev/null +++ b/projects/packages/activity-log/changelog/default-table-view-and-fix-upsell-styles @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Activity Log: default the page to the Table layout and load the upsell-callout stylesheet from the main entry so its flex rules reach the bundle. diff --git a/projects/packages/activity-log/src/js/components/ActivityLog/UpsellCallout.tsx b/projects/packages/activity-log/src/js/components/ActivityLog/UpsellCallout.tsx index 44bce8790691..155386e80599 100644 --- a/projects/packages/activity-log/src/js/components/ActivityLog/UpsellCallout.tsx +++ b/projects/packages/activity-log/src/js/components/ActivityLog/UpsellCallout.tsx @@ -17,7 +17,8 @@ import { addQueryArgs } from '@wordpress/url'; import { useCallback } from 'react'; import { useAnalytics } from '../../hooks/use-analytics'; import illustrationUrl from './activity-logs-callout-illustration.svg'; -import './upsell-callout.scss'; +// Stylesheet is `@use`d from `src/js/style.scss` so the rules ride the +// main entry chunk instead of relying on a side-effect JS import. const PRODUCT_SLUG = 'jetpack_security_t1_yearly'; const UPSELL_SOURCE = 'activity-log-page-purchase'; diff --git a/projects/packages/activity-log/src/js/components/ActivityLog/views.ts b/projects/packages/activity-log/src/js/components/ActivityLog/views.ts index 29c076490753..d7b824b08d21 100644 --- a/projects/packages/activity-log/src/js/components/ActivityLog/views.ts +++ b/projects/packages/activity-log/src/js/components/ActivityLog/views.ts @@ -14,21 +14,14 @@ const TABLE_LAYOUT = { }; export const DEFAULT_VIEW: View = { - type: 'activity', + type: 'table', perPage: 20, sort: { field: 'published', direction: 'desc', }, - fields: [ 'published', 'actor' ], - layout: { density: 'balanced' }, - titleField: 'event_title', - mediaField: 'event_icon', - descriptionField: 'event_description', - // Group consecutive events that fall on the same calendar day - // (site timezone) under a "Apr 24, 2026" header. See the matching - // entry in DEFAULT_LAYOUTS.activity below for the full rationale. - groupBy: { field: 'published_date', direction: 'desc', showLabel: false }, + fields: [ 'published', 'event', 'actor' ], + layout: TABLE_LAYOUT, showLevels: false, }; diff --git a/projects/packages/activity-log/src/js/style.scss b/projects/packages/activity-log/src/js/style.scss index e3bfde46e02b..4a2be031b76c 100644 --- a/projects/packages/activity-log/src/js/style.scss +++ b/projects/packages/activity-log/src/js/style.scss @@ -1,5 +1,10 @@ @use "@automattic/jetpack-base-styles/admin-page-layout" as *; @use "sass:meta"; +// Component styles are forwarded from this main entry rather than +// imported via per-component side-effect imports in each .tsx file — +// that way they live in the same emitted chunk as the wrapper rules +// below and can't get dropped by chunk splitting. +@use "./components/ActivityLog/upsell-callout"; // DataViews is a bundled (not externalized) @wordpress package in // jetpack-webpack-config, so its stylesheet must be brought in alongside diff --git a/projects/plugins/jetpack/changelog/fix-activity-log-default-and-upsell b/projects/plugins/jetpack/changelog/fix-activity-log-default-and-upsell new file mode 100644 index 000000000000..e841ba927961 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-activity-log-default-and-upsell @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Activity Log: default the page to the Table layout and fix the free-tier upsell callout's flex layout. From 222bff97e57cb62abaaac8cc717cce5c9289ce75 Mon Sep 17 00:00:00 2001 From: ilonagl Date: Thu, 30 Apr 2026 21:33:25 +0300 Subject: [PATCH 2/9] Activity Log: show disabled toolbar + date picker on free tier with upgrade tooltips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add FreeTierToolbar that mirrors the DataViews default toolbar shape: disabled SearchControl + disabled funnel button on the left, the real DataViews.ViewConfig (cog) on the right. Each disabled affordance is wrapped in a Tooltip explaining the upgrade requirement. - Extend DateRangePicker with `disabled` + `disabledTooltipText` props. When disabled, the trigger renders as a non-interactive Button (no Dropdown / popover) wrapped in the upgrade tooltip — same hover target and label as the paid trigger so the surface stays visually stable across tiers. - Mount the picker in AdminPage `actions` on both tiers; pass `disabled={!hasActivityLogsAccess}` so the free tier sees the disabled variant alongside the title/subtitle, matching the paid layout. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../default-table-view-and-fix-upsell-styles | 2 +- .../ActivityLog/FreeTierToolbar.tsx | 68 +++++++++++++++++++ .../ActivityLog/free-tier-toolbar.scss | 37 ++++++++++ .../src/js/components/ActivityLog/index.tsx | 27 +++++--- .../js/components/DateRangePicker/index.tsx | 40 +++++++++++ .../packages/activity-log/src/js/style.scss | 1 + .../fix-activity-log-default-and-upsell | 2 +- 7 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 projects/packages/activity-log/src/js/components/ActivityLog/FreeTierToolbar.tsx create mode 100644 projects/packages/activity-log/src/js/components/ActivityLog/free-tier-toolbar.scss diff --git a/projects/packages/activity-log/changelog/default-table-view-and-fix-upsell-styles b/projects/packages/activity-log/changelog/default-table-view-and-fix-upsell-styles index a0959031aeb0..70197c7750fb 100644 --- a/projects/packages/activity-log/changelog/default-table-view-and-fix-upsell-styles +++ b/projects/packages/activity-log/changelog/default-table-view-and-fix-upsell-styles @@ -1,4 +1,4 @@ Significance: patch Type: fixed -Activity Log: default the page to the Table layout and load the upsell-callout stylesheet from the main entry so its flex rules reach the bundle. +Activity Log: default the page to the Table layout, load the upsell-callout stylesheet from the main entry, and surface the disabled toolbar + disabled date-range picker on the free tier with upgrade tooltips. diff --git a/projects/packages/activity-log/src/js/components/ActivityLog/FreeTierToolbar.tsx b/projects/packages/activity-log/src/js/components/ActivityLog/FreeTierToolbar.tsx new file mode 100644 index 000000000000..b45eb64d141c --- /dev/null +++ b/projects/packages/activity-log/src/js/components/ActivityLog/FreeTierToolbar.tsx @@ -0,0 +1,68 @@ +/** + * Free-tier replica of the DataViews toolbar. + * + * On the free tier we replace ``'s default UI with a custom + * children tree (see ActivityLog/index.tsx). The default UI's left + * cluster (search + filters) and right cluster (cog + header slot) is + * mirrored here as visually-disabled lookalikes wrapped in upgrade + * tooltips, so the page surface still reads as a real DataViews table + * even though the affordances themselves only unlock on a Backup plan. + * + * The cog is the one functional control — it falls through to the real + * `DataViews.ViewConfig` and stays gated to the locked `perPage` via + * the `config.perPageSizes` we pass into ``. + */ +import { Button, SearchControl, Tooltip } from '@wordpress/components'; +import { DataViews } from '@wordpress/dataviews'; +import { __, _x } from '@wordpress/i18n'; +import { funnel } from '@wordpress/icons'; + +const NOOP = () => undefined; + +/** + * Render the disabled toolbar shown above the locked Activity Log table. + * + * @return The toolbar element. + */ +export function FreeTierToolbar() { + const upgradeTooltip = __( 'Upgrade your plan to use this feature.', 'jetpack-activity-log' ); + const searchLabel = __( 'Search', 'jetpack-activity-log' ); + + return ( +
+
+ + { /* + * SearchControl swallows pointer events on its inner + * input when `disabled`, so wrap it to give Tooltip a + * stable hover target across the whole control. + */ } +
+ +
+
+ +
+
+ +
+
+ ); +} diff --git a/projects/packages/activity-log/src/js/components/ActivityLog/free-tier-toolbar.scss b/projects/packages/activity-log/src/js/components/ActivityLog/free-tier-toolbar.scss new file mode 100644 index 000000000000..43d2815f3c30 --- /dev/null +++ b/projects/packages/activity-log/src/js/components/ActivityLog/free-tier-toolbar.scss @@ -0,0 +1,37 @@ +// Visual mirror of DataViews' default `dataviews__view-actions` Stack +// (see @wordpress/dataviews build-module/dataviews/index.mjs). Keeping +// the gap / flex shape in lock-step here means the disabled free-tier +// toolbar lines up pixel-for-pixel with the real toolbar on paid tier +// — so the page doesn't visually shift the day a user upgrades. +.jp-activity-log__free-toolbar { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: flex-start; + gap: 4px; +} + +.jp-activity-log__free-toolbar-primary { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + gap: 8px; +} + +.jp-activity-log__free-toolbar-actions { + display: flex; + flex-direction: row; + align-items: center; + gap: 4px; + flex-shrink: 0; +} + +// `` clones its child's ref to attach pointer listeners. When +// the child is a `` whose root
is `disabled`-styled +// the listeners still fire because hover bubbles up; this wrapper just +// keeps the ref target small so the tooltip anchors against the input, +// not the surrounding flex track. +.jp-activity-log__free-toolbar-search { + display: inline-block; +} diff --git a/projects/packages/activity-log/src/js/components/ActivityLog/index.tsx b/projects/packages/activity-log/src/js/components/ActivityLog/index.tsx index bac3a9381b1a..1147d056124d 100644 --- a/projects/packages/activity-log/src/js/components/ActivityLog/index.tsx +++ b/projects/packages/activity-log/src/js/components/ActivityLog/index.tsx @@ -17,6 +17,7 @@ import { useAnalytics } from '../../hooks/use-analytics'; import { usePersistentView } from '../../hooks/use-persistent-view'; import { DateRangePicker } from '../DateRangePicker'; import { formatYmd, parseYmdLocal } from '../DateRangePicker/datetime'; +import { FreeTierToolbar } from './FreeTierToolbar'; import { UpsellCallout } from './UpsellCallout'; import { useActivityActions } from './actions'; import { transformActivityLogEntry } from './activity-transformer'; @@ -328,8 +329,10 @@ export default function ActivityLog() { // Mounting the picker as an admin-ui `actions` slot places it in the // AdminPage header alongside the title/subtitle — matches MSD's - // layout for the logs pages. - const headerActions = hasActivityLogsAccess ? ( + // layout for the logs pages. On free tier the picker renders as a + // disabled upgrade affordance (no popover, hover tooltip), keeping + // the page surface visually consistent with the paid version. + const headerActions = ( - ) : undefined; + ); return ( ` below — Calypso's + // equivalent switch at logs-activity/dataviews/ + // index.tsx:201-208 hides them, but we want the + // upgrade affordance to be discoverable on hover. config={ hasActivityLogsAccess ? undefined @@ -396,7 +402,12 @@ export default function ActivityLog() {

} > - { hasActivityLogsAccess ? undefined : } + { hasActivityLogsAccess ? undefined : ( + <> + + + + ) } { ! hasActivityLogsAccess && ! isFetching && logData.length > 0 && }
diff --git a/projects/packages/activity-log/src/js/components/DateRangePicker/index.tsx b/projects/packages/activity-log/src/js/components/DateRangePicker/index.tsx index 2cca7fca9924..9bbdb79ab8e5 100644 --- a/projects/packages/activity-log/src/js/components/DateRangePicker/index.tsx +++ b/projects/packages/activity-log/src/js/components/DateRangePicker/index.tsx @@ -41,6 +41,12 @@ type DateRangePickerProps = { onStartBlur?: ( e: React.FocusEvent< HTMLInputElement > ) => void; onEndBlur?: ( e: React.FocusEvent< HTMLInputElement > ) => void; }; + // When `disabled`, the toggle renders as a non-interactive button + // wrapped in `disabledTooltipText` (no Dropdown / popover). Lets the + // free-tier surface keep the picker visible as an upgrade cue + // without rebuilding the trigger markup elsewhere. + disabled?: boolean; + disabledTooltipText?: string; }; /** @@ -55,6 +61,8 @@ type DateRangePickerProps = { * @param root0.disableFuture * @param root0.defaultFallbackPreset * @param root0.inputsProps + * @param root0.disabled + * @param root0.disabledTooltipText */ export function DateRangePicker( { start, @@ -66,6 +74,8 @@ export function DateRangePicker( { disableFuture = true, defaultFallbackPreset = 'last-7-days', inputsProps, + disabled = false, + disabledTooltipText, }: DateRangePickerProps ) { const isSmall = useMediaQuery( '(max-width: 600px)' ); const showTwoMonths = useMediaQuery( '(min-width: 900px)' ); @@ -82,6 +92,36 @@ export function DateRangePicker( { gmtOffset ?? '', ].join( '|' ); + if ( disabled ) { + return ( + +
+ +
+
+ ); + } + return ( Date: Thu, 30 Apr 2026 22:48:41 +0300 Subject: [PATCH 3/9] Activity Log: actually disable the free-tier search via
MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `` from @wordpress/components destructures `disabled` out of restProps before forwarding to its StyledInputControl, so the prop was being silently dropped — leaving a fully functional input underneath the upgrade tooltip. Wrap it in a `
` instead, which natively disables every form control in its subtree regardless of what the wrapping component filters out, and reset the fieldset's default chrome so the layout doesn't shift. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../ActivityLog/FreeTierToolbar.tsx | 19 +++++++++++++------ .../ActivityLog/free-tier-toolbar.scss | 17 ++++++++++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/projects/packages/activity-log/src/js/components/ActivityLog/FreeTierToolbar.tsx b/projects/packages/activity-log/src/js/components/ActivityLog/FreeTierToolbar.tsx index b45eb64d141c..d67b50ab43cb 100644 --- a/projects/packages/activity-log/src/js/components/ActivityLog/FreeTierToolbar.tsx +++ b/projects/packages/activity-log/src/js/components/ActivityLog/FreeTierToolbar.tsx @@ -33,11 +33,19 @@ export function FreeTierToolbar() {
{ /* - * SearchControl swallows pointer events on its inner - * input when `disabled`, so wrap it to give Tooltip a - * stable hover target across the whole control. + * `` actively destructures `disabled` + * out of restProps before forwarding to its + * StyledInputControl (see @wordpress/components + * search-control/index.mjs), so passing `disabled` + * directly is a no-op. Wrap in `
` + * instead — disabled fieldsets natively disable every + * form control in their subtree regardless of what + * the wrapping component filters out, and the + * fieldset itself still receives pointer events so + * the hover tooltip fires. Default fieldset + * margin/border/padding are reset in the SCSS. */ } -
+
-
+
+ ); } diff --git a/projects/packages/activity-log/src/js/components/ActivityLog/upsell-callout.scss b/projects/packages/activity-log/src/js/components/ActivityLog/upsell-callout.scss index 3d025da7b103..25782bad775a 100644 --- a/projects/packages/activity-log/src/js/components/ActivityLog/upsell-callout.scss +++ b/projects/packages/activity-log/src/js/components/ActivityLog/upsell-callout.scss @@ -1,49 +1,56 @@ // Upsell callout rendered below the Activity Log table on free plans. -// Visual intent mirrors Calypso's `ActivityLogsCallout` layout: a centered -// card with the illustration on one side, copy + CTA on the other. +// Visual: a bordered card with the copy + CTA on the left and the +// illustration on the right (column-reverse on mobile, image moves +// below the copy). .jp-activity-log__upsell-callout { display: flex; - flex-direction: column-reverse; - align-items: center; + flex-direction: column; gap: 24px; - padding: 32px 24px; - margin: 32px auto 0; + margin: 24px auto 0; max-width: 960px; + padding: 24px; + border: 1px solid var(--wpds-color-stroke-surface-neutral-weak, #dcdcde); + border-radius: 4px; + background-color: var(--wpds-color-bg-surface-neutral, #fff); @media (min-width: 782px) { flex-direction: row; align-items: center; - gap: 48px; - padding: 40px; + gap: 32px; + padding: 32px; } } .jp-activity-log__upsell-callout-image { display: block; width: 100%; - max-width: 320px; + max-width: 360px; height: auto; flex-shrink: 0; + align-self: center; } .jp-activity-log__upsell-callout-content { display: flex; flex-direction: column; - gap: 16px; + gap: 12px; flex: 1 1 auto; min-width: 0; } .jp-activity-log__upsell-callout-title { margin: 0; - font-size: 24px; + font-size: 20px; line-height: 1.3; font-weight: 500; color: var(--wpds-color-fg-content-neutral, #1e1e1e); } // The