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..70197c7750fb --- /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, 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/UpsellCallout.tsx b/projects/packages/activity-log/src/js/components/ActivityLog/UpsellCallout.tsx index 44bce8790691..0bf291fb9a29 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'; @@ -75,40 +76,37 @@ export function UpsellCallout() { return (
-

- { __( 'Track every action with Jetpack Activity Log', 'jetpack-activity-log' ) } + { __( 'Track every action with Activity logs', 'jetpack-activity-log' ) }

{ __( - 'Debug issues faster with insights from a comprehensive audit log of all your admin activities.', + 'Debug issues faster with insights from a comprehensive audit log of all your site events.', 'jetpack-activity-log' ) } { __( - 'With your free plan, you can see your 20 most recent events. Upgrade for 30 days of history, plus filtering and date range controls.', + 'Upgrade to get complete activity history for the last 30 days, advanced filtering and date range selection. Available on the Jetpack Security and Complete plans.', 'jetpack-activity-log' ) } - - { __( 'Available on the Jetpack Security and Complete plans.', 'jetpack-activity-log' ) } -
+
); } 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..6ea493cbfce4 100644 --- a/projects/packages/activity-log/src/js/components/ActivityLog/index.tsx +++ b/projects/packages/activity-log/src/js/components/ActivityLog/index.tsx @@ -131,6 +131,55 @@ export default function ActivityLog() { return () => observer.disconnect(); }, [] ); + // On free tier, neutralize DataViews' real search + filter cluster + // (the `.dataviews__search` Stack rendered by `DataViews`'s default + // UI). We let DataViews ship its own toolbar so the page tracks + // upstream changes for free, then attach: `aria-disabled` for + // assistive tech, a `title` attribute that surfaces the upgrade + // nudge as a native browser tooltip on hover, and `tabindex="-1"` + // on every focusable descendant so the cluster is unreachable via + // keyboard. Pointer-event blocking on the children is handled in + // CSS via the `[aria-disabled="true"]` rule. + // `MutationObserver` re-applies after DataViews remounts the + // toolbar / re-renders the input (e.g., on initial fetch resolution + // or layout switch) so React's render doesn't strip the attributes. + useEffect( () => { + if ( hasActivityLogsAccess ) { + return; + } + + const wrapper = wrapperRef.current; + if ( ! wrapper ) { + return; + } + + const tooltipText = __( 'Upgrade your plan to use this feature.', 'jetpack-activity-log' ); + + const apply = ( root: ParentNode ) => { + const cluster = root.querySelector< HTMLElement >( '.dataviews__search' ); + if ( ! cluster ) { + return; + } + + if ( cluster.getAttribute( 'aria-disabled' ) !== 'true' ) { + cluster.setAttribute( 'aria-disabled', 'true' ); + cluster.setAttribute( 'title', tooltipText ); + } + + cluster.querySelectorAll< HTMLElement >( 'input, button, [tabindex]' ).forEach( el => { + if ( el.getAttribute( 'tabindex' ) !== '-1' ) { + el.setAttribute( 'tabindex', '-1' ); + } + } ); + }; + + apply( wrapper ); + const observer = new MutationObserver( () => apply( wrapper ) ); + observer.observe( wrapper, { subtree: true, childList: true } ); + + return () => observer.disconnect(); + }, [ hasActivityLogsAccess ] ); + // Date-range defaults to "Last 7 days" anchored at the site's calendar // today (not the browser's) — matches Calypso's `getDefaultDateRange`. // The range is client-only state: refreshes reset to the default @@ -328,8 +377,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 ( -
- - data={ logData } - isLoading={ isFetching || isLoadingList } - paginationInfo={ paginationInfo } - fields={ fields as Field< Activity >[] } - view={ view } - actions={ actions } - getItemId={ getItemId } - search - // Advertise both DataViews' built-in Activity timeline - // (the default) and a Table layout. Toggle lives in - // the cog popover's layout switcher. Each layout maps - // the event parts to the right slots: - // - Activity: `event_icon` → mediaField (left - // bullet slot), `event_title` → titleField, - // `event_description` → descriptionField, plus - // `groupBy: published_date` for day headers. - // - Table: one composite `event` column alongside - // Date / User. - // See DEFAULT_LAYOUTS in ./views for the full shape — - // it explicitly nulls slot/groupBy refs on Table so a - // round-trip Activity → Table doesn't carry those - // over and double-render as a primary column. - defaultLayouts={ DEFAULT_LAYOUTS } - onChangeView={ onChangeView } - onReset={ isViewModified ? onResetView : false } - // On the free tier, lock the perPage selector to the - // capped size and hide search/filters/sort/view-config - // by replacing the default UI with just the table (same - // switches Calypso uses at logs-activity/dataviews/ - // index.tsx:201-208). - config={ - hasActivityLogsAccess - ? undefined - : { perPageSizes: [ ACTIVITY_LOGS_DEFAULT_PAGE_SIZE ] } - } - empty={ -

- { view.search - ? __( 'No activity found', 'jetpack-activity-log' ) - : __( 'No activities', 'jetpack-activity-log' ) } -

- } - > - { hasActivityLogsAccess ? undefined : } - - { ! hasActivityLogsAccess && ! isFetching && logData.length > 0 && } +
+ { /* + * Single inner div soaks up `jetpack-admin-page-layout`'s + * `.admin-ui-page > :not(...):not(...) > *` rule (which + * force-applies `flex: 1 1 auto; flex-direction: column` + * to every direct child of the page's scroll column). + * With the chain landing here, DataViews and the + * free-tier UpsellCallout are grandchildren and stack + * without competing for flex space — no `!important` + * overrides needed. + */ } +
+ + data={ logData } + isLoading={ isFetching || isLoadingList } + paginationInfo={ paginationInfo } + fields={ fields as Field< Activity >[] } + view={ view } + actions={ actions } + getItemId={ getItemId } + search + // Advertise both DataViews' built-in Activity timeline + // (the default) and a Table layout. Toggle lives in + // the cog popover's layout switcher. Each layout maps + // the event parts to the right slots: + // - Activity: `event_icon` → mediaField (left + // bullet slot), `event_title` → titleField, + // `event_description` → descriptionField, plus + // `groupBy: published_date` for day headers. + // - Table: one composite `event` column alongside + // Date / User. + // See DEFAULT_LAYOUTS in ./views for the full shape — + // it explicitly nulls slot/groupBy refs on Table so a + // round-trip Activity → Table doesn't carry those + // over and double-render as a primary column. + defaultLayouts={ DEFAULT_LAYOUTS } + onChangeView={ onChangeView } + onReset={ isViewModified ? onResetView : false } + // On the free tier, lock the perPage selector to the + // capped size. DataViews keeps rendering its real + // toolbar (search + filter toggle + cog); the + // search/filter cluster is neutralized by the + // `aria-disabled` + `tabindex="-1"` overlay + // applied in the effect above — 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 + : { perPageSizes: [ ACTIVITY_LOGS_DEFAULT_PAGE_SIZE ] } + } + empty={ +

+ { view.search + ? __( 'No activity found', 'jetpack-activity-log' ) + : __( 'No activities', 'jetpack-activity-log' ) } +

+ } + /> + { ! hasActivityLogsAccess && ! isFetching && logData.length > 0 && } +
); 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 +
+ + ); + } + return ( .dataviews-wrapper { + height: auto; + flex: 0 0 auto; + } + } + + // Visual + click-blocking half of the free-tier "overlay" approach: + // the effect in ActivityLog/index.tsx toggles `aria-disabled` and + // `tabindex="-1"` on DataViews' real `.dataviews__search` cluster + // (search input + filter toggle), and adds a `title` for the + // upgrade tooltip. This rule dims the cluster, shows a not-allowed + // cursor, and shuts off pointer events on the controls inside so + // clicks fall through to the cluster (keeping it hoverable for the + // browser-native title tooltip). + .dataviews__search[aria-disabled="true"] { + opacity: 0.5; + cursor: not-allowed; + + > * { + pointer-events: none; + } + } + // DataViews renders its own surface; don't double-pad it. - > .dataviews-wrapper { + .dataviews-wrapper { margin: 0; } diff --git a/projects/plugins/jetpack/changelog/fix-activity-log-table-default-and-upsell b/projects/plugins/jetpack/changelog/fix-activity-log-table-default-and-upsell new file mode 100644 index 000000000000..44d699a3f9cc --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-activity-log-table-default-and-upsell @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +Activity Log: default the page to the Table layout, fix the free-tier upsell callout layout, and surface the disabled toolbar + disabled date-range picker on the free tier with upgrade tooltips.