From a99dbec1a15e5ce03af7b9df2606329c9a60e6a4 Mon Sep 17 00:00:00 2001 From: Bryan Elliott Date: Thu, 19 Dec 2024 14:19:25 -0500 Subject: [PATCH 1/8] Fix Protect product card Tooltips placement & content. --- .../_inc/components/info-tooltip/index.tsx | 8 +- .../_inc/components/info-tooltip/types.ts | 210 ++++++++++++++++++ .../protect-card/auto-firewall-status.tsx | 3 + .../protect-card/scan-threats-status.tsx | 20 +- .../protect-card/use-protect-tooltip-copy.ts | 50 +++-- projects/packages/my-jetpack/global.d.ts | 1 + 6 files changed, 268 insertions(+), 24 deletions(-) create mode 100644 projects/packages/my-jetpack/_inc/components/info-tooltip/types.ts diff --git a/projects/packages/my-jetpack/_inc/components/info-tooltip/index.tsx b/projects/packages/my-jetpack/_inc/components/info-tooltip/index.tsx index b4e6a62eae873..c0773c3ba6d2b 100644 --- a/projects/packages/my-jetpack/_inc/components/info-tooltip/index.tsx +++ b/projects/packages/my-jetpack/_inc/components/info-tooltip/index.tsx @@ -3,18 +3,18 @@ import { Popover } from '@wordpress/components'; import { useViewportMatch } from '@wordpress/compose'; import { useState, useCallback, useRef } from 'react'; import useAnalytics from '../../hooks/use-analytics'; -import type { FC, ReactNode } from 'react'; +import type { PopoverProps } from './types'; +import type { FC } from 'react'; import './style.scss'; -type Props = { - children: ReactNode; +interface Props extends PopoverProps { className?: string; icon?: string; iconSize?: number; tracksEventName?: string; tracksEventProps?: Record< Lowercase< string >, unknown >; -}; +} export const InfoTooltip: FC< Props > = ( { children, diff --git a/projects/packages/my-jetpack/_inc/components/info-tooltip/types.ts b/projects/packages/my-jetpack/_inc/components/info-tooltip/types.ts new file mode 100644 index 0000000000000..8fa06ae06e05f --- /dev/null +++ b/projects/packages/my-jetpack/_inc/components/info-tooltip/types.ts @@ -0,0 +1,210 @@ +/* eslint-disable jsdoc/check-indentation */ +import type { ReactNode, MutableRefObject, SyntheticEvent } from 'react'; + +type PositionYAxis = 'top' | 'middle' | 'bottom'; +type PositionXAxis = 'left' | 'center' | 'right'; +type PositionCorner = 'top' | 'right' | 'bottom' | 'left'; + +type DomRectWithOwnerDocument = DOMRect & { + ownerDocument?: Document; +}; + +type PopoverPlacement = + | 'top' + | 'top-start' + | 'top-end' + | 'right' + | 'right-start' + | 'right-end' + | 'bottom' + | 'bottom-start' + | 'bottom-end' + | 'left' + | 'left-start' + | 'left-end' + | 'overlay'; + +export type PopoverAnchorRefReference = MutableRefObject< Element | null | undefined >; +export type PopoverAnchorRefTopBottom = { top: Element; bottom: Element }; + +export type VirtualElement = Pick< Element, 'getBoundingClientRect' > & { + ownerDocument?: Document; +}; + +export type PopoverProps = { + /** + * The name of the Slot in which the popover should be rendered. It should + * be also passed to the corresponding `PopoverSlot` component. + * + * @default 'Popover' + */ + __unstableSlotName?: string; + /** + * The element that should be used by the popover as its anchor. It can either + * be an `Element` or, alternatively, a `VirtualElement` — ie. an object with + * the `getBoundingClientRect()` and the `ownerDocument` properties defined. + * + * **The anchor element should be stored in local state** rather than a + * plain React ref to ensure reactive updating when it changes. + */ + anchor?: Element | VirtualElement | null; + /** + * Whether the popover should animate when opening. + * + * @default true + */ + animate?: boolean; + /** + * The `children` elements rendered as the popover's content. + */ + children: ReactNode; + /** + * Show the popover fullscreen on mobile viewports. + */ + expandOnMobile?: boolean; + /** + * Specifies whether the popover should flip across its axis if there isn't + * space for it in the normal placement. + * When the using a 'top' placement, the popover will switch to a 'bottom' + * placement. When using a 'left' placement, the popover will switch to a + * `right' placement. + * The popover will retain its alignment of 'start' or 'end' when flipping. + * + * @default true + */ + flip?: boolean; + /** + * Determines whether tabbing is constrained to within the popover, + * preventing keyboard focus from leaving the popover content without + * explicit focus elswhere, or whether the popover remains part of the wider + * tab order. If no value is passed, it will be derived from `focusOnMount`. + * + * @default `focusOnMount` !== false + */ + constrainTabbing?: boolean; + /** + * By default, the _first tabbable element_ in the popover will receive focus + * when it mounts. This is the same as setting this prop to `"firstElement"`. + * Specifying a `false` value disables the focus handling entirely (this + * should only be done when an appropriately accessible substitute behavior + * exists). + * + * @default 'firstElement' + */ + focusOnMount?: 'firstElement' | boolean; + /** + * A callback invoked when the focus leaves the opened popover. This should + * only be provided in advanced use-cases when a popover should close under + * specific circumstances (for example, if the new `document.activeElement` + * is content of or otherwise controlling popover visibility). + * + * When not provided, the `onClose` callback will be called instead. + */ + onFocusOutside?: ( event: SyntheticEvent ) => void; + /** + * Used to customize the header text shown when the popover is toggled to + * fullscreen on mobile viewports (see the `expandOnMobile` prop). + */ + headerTitle?: string; + /** + * Used to show/hide the arrow that points at the popover's anchor. + * + * @default true + */ + noArrow?: boolean; + /** + * The distance (in px) between the anchor and the popover. + */ + offset?: number; + /** + * A callback invoked when the popover should be closed. + */ + onClose?: () => void; + /** + * Used to specify the popover's position with respect to its anchor. + * + * @default 'bottom-start' + */ + placement?: PopoverPlacement; + /** + * Legacy way to specify the popover's position with respect to its anchor. + * _Note: this prop is deprecated. Use the `placement` prop instead._ + */ + position?: + | `${ PositionYAxis }` + | `${ PositionYAxis } ${ PositionXAxis }` + | `${ PositionYAxis } ${ PositionXAxis } ${ PositionCorner }`; + /** + * Adjusts the size of the popover to prevent its contents from going out of + * view when meeting the viewport edges. + * + * @default true + */ + resize?: boolean; + /** + * Enables the `Popover` to shift in order to stay in view when meeting the + * viewport edges. + * + * @default false + */ + shift?: boolean; + /** + * Specifies the popover's style. + * + * Leave undefined for the default style. Other values are: + * - 'unstyled': The popover is essentially without any visible style, it + * has no background, border, outline or drop shadow, but + * the popover contents are still displayed. + * - 'toolbar': A style that has no elevation, but a high contrast with + * other elements. This is matches the style of the + * `Toolbar` component. + * + * @default undefined + */ + variant?: 'unstyled' | 'toolbar'; + /** + * Whether to render the popover inline or within the slot. + * + * @default false + */ + inline?: boolean; + // Deprecated props + /** + * Prevent the popover from flipping and resizing when meeting the viewport + * edges. _Note: this prop is deprecated. Instead, provide use the individual + * `flip` and `resize` props._ + * + * @deprecated + */ + __unstableForcePosition?: boolean; + /** + * An object extending a `DOMRect` with an additional optional `ownerDocument` + * property, used to specify a fixed popover position. + * + * @deprecated + */ + anchorRect?: DomRectWithOwnerDocument; + /** + * Used to specify a fixed popover position. It can be an `Element`, a React + * reference to an `element`, an object with a `top` and a `bottom` properties + * (both pointing to elements), or a `range`. + * + * @deprecated + */ + anchorRef?: Element | PopoverAnchorRefReference | PopoverAnchorRefTopBottom | Range; + /** + * A function returning the same value as the one expected by the `anchorRect` + * prop, used to specify a dynamic popover position. + * + * @deprecated + */ + getAnchorRect?: ( fallbackReferenceElement: Element | null ) => DomRectWithOwnerDocument; + /** + * Used to enable a different visual style for the popover. + * _Note: this prop is deprecated. Use the `variant` prop with the + * 'toolbar' value instead._ + * + * @deprecated + */ + isAlternate?: boolean; +}; diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx index 79a5c4238a446..e403c034a2b94 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx @@ -1,3 +1,4 @@ +import { useViewportMatch } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; import clsx from 'clsx'; import useProduct from '../../../data/products/use-product'; @@ -41,6 +42,7 @@ export const AutoFirewallStatus = () => { */ function WafStatus( { status }: { status: 'active' | 'inactive' | 'off' } ) { const slug = 'protect'; + const isMobileViewport: boolean = useViewportMatch( 'medium', '<' ); const { detail } = useProduct( slug ); const { hasPaidPlanForProduct = false } = detail || {}; const tooltipContent = useProtectTooltipCopy(); @@ -78,6 +80,7 @@ function WafStatus( { status }: { status: 'active' | 'inactive' | 'off' } ) { feature: 'jetpack-protect', has_paid_plan: hasPaidPlanForProduct, } } + placement={ isMobileViewport ? 'top' : 'right' } > <>

{ autoFirewallTooltip.title }

diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx index af25fe770b8e0..414ed3db040b9 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx @@ -138,10 +138,10 @@ function ThreatStatus( { focusOnMount={ 'container' } onClose={ hideTooltip } > - <> +

{ scanThreatsTooltip.title }

{ scanThreatsTooltip.text }

- +
) } @@ -157,8 +157,22 @@ function ThreatStatus( { return ( <> -
+
{ __( 'Threats', 'jetpack-my-jetpack' ) } + + <> +

{ scanThreatsTooltip.title }

+

{ scanThreatsTooltip.text }

+ +
{ numThreats }
diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts index 6f95e251ea099..4561bf81eacf7 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts @@ -39,6 +39,7 @@ export function useProtectTooltipCopy(): TooltipContent { plugins: fromScanPlugins, themes: fromScanThemes, num_threats: numThreats = 0, + threats = [], } = scanData || {}; const { jetpack_waf_automatic_rules: isAutoFirewallEnabled, @@ -49,6 +50,12 @@ export function useProtectTooltipCopy(): TooltipContent { const pluginsCount = fromScanPlugins.length || Object.keys( plugins ).length; const themesCount = fromScanThemes.length || Object.keys( themes ).length; + const criticalThreatCount: number = useMemo( () => { + return threats.length + ? threats.reduce( ( accum, threat ) => ( threat.severity >= 5 ? ( accum += 1 ) : accum ), 0 ) + : 0; + }, [ threats ] ); + const settingsLink = useMemo( () => { if ( isProtectPluginActive ) { return 'admin.php?page=jetpack-protect#/firewall'; @@ -173,23 +180,32 @@ export function useProtectTooltipCopy(): TooltipContent { hasProtectPaidPlan && numThreats ? { title: __( 'Auto-fix threats', 'jetpack-my-jetpack' ), - text: sprintf( - /* translators: %s is the singular or plural of number of detected critical threats on the site. */ - __( - 'The last scan identified %s. But don’t worry, use the “Auto-fix” button in the product to automatically fix most threats.', - 'jetpack-my-jetpack' - ), - sprintf( - /* translators: %d is the number of detected scan threats on the site. */ - _n( - '%d critical threat.', - '%d critical threats.', - numThreats, - 'jetpack-my-jetpack' - ), - numThreats - ) - ), + text: criticalThreatCount + ? sprintf( + /* translators: %1$s is the number of threats and %2$s is the numner of critical threats on the site. */ + __( + 'The last scan identified %1$s (%2$d\u00A0critical). But don’t worry, use the “Auto-fix” button in the product to automatically fix most threats.', + 'jetpack-my-jetpack' + ), + sprintf( + /* translators: %d is the number of detected scan threats on the site. */ + _n( '%d threat', '%d threats', numThreats, 'jetpack-my-jetpack' ), + numThreats + ), + criticalThreatCount + ) + : sprintf( + /* translators: %s is the singular or plural of number of detected critical threats on the site. */ + __( + 'The last scan identified %s. But don’t worry, use the “Auto-fix” button in the product to automatically fix most threats.', + 'jetpack-my-jetpack' + ), + sprintf( + /* translators: %d is the number of detected scan threats on the site. */ + _n( '%d threat', '%d threats', numThreats, 'jetpack-my-jetpack' ), + numThreats + ) + ), } : { title: __( 'Elevate your malware protection', 'jetpack-my-jetpack' ), diff --git a/projects/packages/my-jetpack/global.d.ts b/projects/packages/my-jetpack/global.d.ts index 5ae0a3a54fcf6..134f55d8e4ef0 100644 --- a/projects/packages/my-jetpack/global.d.ts +++ b/projects/packages/my-jetpack/global.d.ts @@ -268,6 +268,7 @@ interface Window { plugins: ScanItem[]; status: string; themes: ScanItem[]; + threats?: ThreatItem[]; }; wafConfig: { automatic_rules_available: boolean; From 1b16f47bc981855760748bdee768760f97a5a4f5 Mon Sep 17 00:00:00 2001 From: Bryan Elliott Date: Thu, 19 Dec 2024 14:21:20 -0500 Subject: [PATCH 2/8] changelog --- .../changelog/update-my-jetpack-fix-protect-card-tooltips | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 projects/packages/my-jetpack/changelog/update-my-jetpack-fix-protect-card-tooltips diff --git a/projects/packages/my-jetpack/changelog/update-my-jetpack-fix-protect-card-tooltips b/projects/packages/my-jetpack/changelog/update-my-jetpack-fix-protect-card-tooltips new file mode 100644 index 0000000000000..481fa13dec6a7 --- /dev/null +++ b/projects/packages/my-jetpack/changelog/update-my-jetpack-fix-protect-card-tooltips @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +My Jetpack: Protect card- Fixed Tooltip placement & content issues. From b6968714c3d881c718daa1f1c062064241608357 Mon Sep 17 00:00:00 2001 From: Bryan Elliott Date: Fri, 20 Dec 2024 02:10:17 -0500 Subject: [PATCH 3/8] Adjust wording & add link in the "Threats" tooltip. --- .../protect-card/use-protect-tooltip-copy.ts | 82 ++++++++++++++----- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts index 4561bf81eacf7..6d67a4b839ea8 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts @@ -27,13 +27,17 @@ export type TooltipContent = { export function useProtectTooltipCopy(): TooltipContent { const slug = PRODUCT_SLUGS.PROTECT; const { detail } = useProduct( slug ); - const { isPluginActive: isProtectPluginActive, hasPaidPlanForProduct: hasProtectPaidPlan } = - detail || {}; + const { + isPluginActive: isProtectPluginActive, + hasPaidPlanForProduct: hasProtectPaidPlan, + manageUrl: protectPluginDashboardUrl, + } = detail || {}; const { recordEvent } = useAnalytics(); const { plugins, themes, protect: { scanData, wafConfig: wafData }, + siteSuffix, } = getMyJetpackWindowInitialState(); const { plugins: fromScanPlugins, @@ -63,6 +67,13 @@ export function useProtectTooltipCopy(): TooltipContent { return isJetpackPluginActive() ? 'admin.php?page=jetpack#/settings' : null; }, [ isProtectPluginActive ] ); + const protectDashboardUrl = useMemo( () => { + if ( isProtectPluginActive ) { + return protectPluginDashboardUrl; + } + return `https://cloud.jetpack.com/scan/${ siteSuffix }`; + }, [ isProtectPluginActive, protectPluginDashboardUrl, siteSuffix ] ); + const trackFirewallSettingsLinkClick = useCallback( () => { recordEvent( 'jetpack_protect_card_tooltip_content_link_click', { page: 'my-jetpack', @@ -72,6 +83,15 @@ export function useProtectTooltipCopy(): TooltipContent { } ); }, [ recordEvent, settingsLink ] ); + const trackProtectDashboardLinkClick = useCallback( () => { + recordEvent( 'jetpack_protect_card_tooltip_content_link_click', { + page: 'my-jetpack', + feature: 'jetpack-protect', + location: 'scan-threats-tooltip', + path: protectDashboardUrl, + } ); + }, [ recordEvent, protectDashboardUrl ] ); + const isBruteForcePluginsActive = isProtectPluginActive || isJetpackPluginActive(); const blockedLoginsTooltip = useMemo( () => { @@ -181,30 +201,48 @@ export function useProtectTooltipCopy(): TooltipContent { ? { title: __( 'Auto-fix threats', 'jetpack-my-jetpack' ), text: criticalThreatCount - ? sprintf( - /* translators: %1$s is the number of threats and %2$s is the numner of critical threats on the site. */ - __( - 'The last scan identified %1$s (%2$d\u00A0critical). But don’t worry, use the “Auto-fix” button in the product to automatically fix most threats.', - 'jetpack-my-jetpack' - ), + ? createInterpolateElement( sprintf( - /* translators: %d is the number of detected scan threats on the site. */ - _n( '%d threat', '%d threats', numThreats, 'jetpack-my-jetpack' ), - numThreats + /* translators: %1$s is the number of threats and %2$s is the numner of critical threats on the site. */ + __( + 'The last scan identified %1$s (%2$d\u00A0critical). But don’t worry, Protect is usually able to “Auto-fix” threats, in most cases. Visit the %3$s dashboard to view more details.', + 'jetpack-my-jetpack' + ), + sprintf( + /* translators: %d is the number of detected scan threats on the site. */ + _n( '%d threat', '%d threats', numThreats, 'jetpack-my-jetpack' ), + numThreats + ), + criticalThreatCount, + isProtectPluginActive ? 'Protect' : 'Scan' ), - criticalThreatCount + { + a: createElement( 'a', { + href: protectDashboardUrl, + onClick: trackProtectDashboardLinkClick, + } ), + } ) - : sprintf( - /* translators: %s is the singular or plural of number of detected critical threats on the site. */ - __( - 'The last scan identified %s. But don’t worry, use the “Auto-fix” button in the product to automatically fix most threats.', - 'jetpack-my-jetpack' - ), + : createInterpolateElement( sprintf( - /* translators: %d is the number of detected scan threats on the site. */ - _n( '%d threat', '%d threats', numThreats, 'jetpack-my-jetpack' ), - numThreats - ) + /* translators: %s is the singular or plural of number of detected threats on the site. */ + __( + 'The last scan identified %1$s. But don’t worry, Protect is usually able to “Auto-fix” threats, in most cases. Visit the %2$s dashboard to view more details.', + 'jetpack-my-jetpack' + ), + sprintf( + /* translators: %d is the number of detected scan threats on the site. */ + _n( '%d threat', '%d threats', numThreats, 'jetpack-my-jetpack' ), + numThreats + ), + isProtectPluginActive ? 'Protect' : 'Scan' + ), + { + a: createElement( 'a', { + href: protectDashboardUrl, + onClick: trackProtectDashboardLinkClick, + } ), + } ), } : { From 2663af5a82afcf6b562157c031fda69c32cc8d2d Mon Sep 17 00:00:00 2001 From: Bryan Elliott Date: Fri, 20 Dec 2024 14:06:44 -0500 Subject: [PATCH 4/8] Remove unnecessary fragments in InfoTooltip child elements, per feedback. --- .../protect-card/auto-firewall-status.tsx | 6 ++---- .../protect-card/logins-blocked-status.tsx | 12 ++++-------- .../protect-card/protect-value-section.tsx | 6 ++---- .../protect-card/scan-threats-status.tsx | 12 ++++-------- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx index e403c034a2b94..9b1d94839270e 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx @@ -82,10 +82,8 @@ function WafStatus( { status }: { status: 'active' | 'inactive' | 'off' } ) { } } placement={ isMobileViewport ? 'top' : 'right' } > - <> -

{ autoFirewallTooltip.title }

-

{ autoFirewallTooltip.text }

- +

{ autoFirewallTooltip.title }

+

{ autoFirewallTooltip.text }

diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/logins-blocked-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/logins-blocked-status.tsx index 384fe1329ce4b..71aa5cada5027 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/logins-blocked-status.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/logins-blocked-status.tsx @@ -79,10 +79,8 @@ function BlockedStatus( { status }: { status: 'active' | 'inactive' | 'off' } ) message: 'no data yet', } } > - <> -

{ blockedLoginsTooltip.title }

-

{ blockedLoginsTooltip.text }

- +

{ blockedLoginsTooltip.title }

+

{ blockedLoginsTooltip.text }

@@ -113,10 +111,8 @@ function BlockedStatus( { status }: { status: 'active' | 'inactive' | 'off' } ) status: status, } } > - <> -

{ blockedLoginsTooltip.title }

-

{ blockedLoginsTooltip.text }

- +

{ blockedLoginsTooltip.title }

+

{ blockedLoginsTooltip.text }

diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/protect-value-section.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/protect-value-section.tsx index 015cce91ea44f..bb744c6f503b4 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/protect-value-section.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/protect-value-section.tsx @@ -29,10 +29,8 @@ const ProtectValueSection = () => { status: 'inactive', } } > - <> -

{ pluginsThemesTooltip.title }

-

{ pluginsThemesTooltip.text }

- +

{ pluginsThemesTooltip.title }

+

{ pluginsThemesTooltip.text }

) }
diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx index 414ed3db040b9..6cda8a7331bdf 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx @@ -168,10 +168,8 @@ function ThreatStatus( { threats: numThreats, } } > - <> -

{ scanThreatsTooltip.title }

-

{ scanThreatsTooltip.text }

- +

{ scanThreatsTooltip.title }

+

{ scanThreatsTooltip.text }

@@ -227,10 +225,8 @@ function ScanStatus( { status }: { status: 'success' | 'partial' | 'off' } ) { threats: 0, } } > - <> -

{ scanThreatsTooltip.title }

-

{ scanThreatsTooltip.text }

- +

{ scanThreatsTooltip.title }

+

{ scanThreatsTooltip.text }

From ca6226fbb2946d329b9102069b6abe56fed201d8 Mon Sep 17 00:00:00 2001 From: Bryan Elliott Date: Fri, 20 Dec 2024 14:48:51 -0500 Subject: [PATCH 5/8] Refactor Protect::get_manage_url() to return either jp-cloud or plugin url. --- .../protect-card/use-protect-tooltip-copy.ts | 12 ++---------- .../my-jetpack/src/products/class-protect.php | 8 +++++++- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts index 6d67a4b839ea8..fa19409cbf001 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts @@ -30,14 +30,13 @@ export function useProtectTooltipCopy(): TooltipContent { const { isPluginActive: isProtectPluginActive, hasPaidPlanForProduct: hasProtectPaidPlan, - manageUrl: protectPluginDashboardUrl, + manageUrl: protectDashboardUrl, } = detail || {}; const { recordEvent } = useAnalytics(); const { plugins, themes, protect: { scanData, wafConfig: wafData }, - siteSuffix, } = getMyJetpackWindowInitialState(); const { plugins: fromScanPlugins, @@ -67,13 +66,6 @@ export function useProtectTooltipCopy(): TooltipContent { return isJetpackPluginActive() ? 'admin.php?page=jetpack#/settings' : null; }, [ isProtectPluginActive ] ); - const protectDashboardUrl = useMemo( () => { - if ( isProtectPluginActive ) { - return protectPluginDashboardUrl; - } - return `https://cloud.jetpack.com/scan/${ siteSuffix }`; - }, [ isProtectPluginActive, protectPluginDashboardUrl, siteSuffix ] ); - const trackFirewallSettingsLinkClick = useCallback( () => { recordEvent( 'jetpack_protect_card_tooltip_content_link_click', { page: 'my-jetpack', @@ -203,7 +195,7 @@ export function useProtectTooltipCopy(): TooltipContent { text: criticalThreatCount ? createInterpolateElement( sprintf( - /* translators: %1$s is the number of threats and %2$s is the numner of critical threats on the site. */ + /* translators: %1$s is the number of threats and %2$s is the numner of critical threats on the site, and %3$s */ __( 'The last scan identified %1$s (%2$d\u00A0critical). But don’t worry, Protect is usually able to “Auto-fix” threats, in most cases. Visit the %3$s dashboard to view more details.', 'jetpack-my-jetpack' diff --git a/projects/packages/my-jetpack/src/products/class-protect.php b/projects/packages/my-jetpack/src/products/class-protect.php index 7d325c4302cb6..ce90c19cc0401 100644 --- a/projects/packages/my-jetpack/src/products/class-protect.php +++ b/projects/packages/my-jetpack/src/products/class-protect.php @@ -321,7 +321,13 @@ public static function get_post_checkout_urls_by_feature() { * @return ?string */ public static function get_manage_url() { - return admin_url( 'admin.php?page=jetpack-protect' ); + // check standalone first + if ( static::is_standalone_plugin_active() ) { + return admin_url( 'admin.php?page=jetpack-protect' ); + // otherwise, check for the main Jetpack plugin + } elseif ( static::is_jetpack_plugin_active() ) { + return Redirect::get_url( 'my-jetpack-manage-scan' ); + } } /** From 459d102fae1b2dd05879f62df67b86c2a29ad2da Mon Sep 17 00:00:00 2001 From: Bryan Elliott Date: Fri, 20 Dec 2024 14:56:47 -0500 Subject: [PATCH 6/8] Fix missing placeholder descriptions (%2$s & %3$s) in translation comments. --- .../protect-card/use-protect-tooltip-copy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts index fa19409cbf001..c6bdab86dcfc9 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts @@ -195,7 +195,7 @@ export function useProtectTooltipCopy(): TooltipContent { text: criticalThreatCount ? createInterpolateElement( sprintf( - /* translators: %1$s is the number of threats and %2$s is the numner of critical threats on the site, and %3$s */ + /* translators: %1$s is the number of threats, %2$s is the numner of critical threats on the site, and %3$s is either "Scan" or "Protect" (the type of dashboard). */ __( 'The last scan identified %1$s (%2$d\u00A0critical). But don’t worry, Protect is usually able to “Auto-fix” threats, in most cases. Visit the %3$s dashboard to view more details.', 'jetpack-my-jetpack' @@ -217,7 +217,7 @@ export function useProtectTooltipCopy(): TooltipContent { ) : createInterpolateElement( sprintf( - /* translators: %s is the singular or plural of number of detected threats on the site. */ + /* translators: %1$s is the singular or plural of number of detected threats on the site, and %2$s is either "Scan" or "Protect" (the type of dashboard). */ __( 'The last scan identified %1$s. But don’t worry, Protect is usually able to “Auto-fix” threats, in most cases. Visit the %2$s dashboard to view more details.', 'jetpack-my-jetpack' From f4c64707ec966f1bca95005c764bd0fcea51128f Mon Sep 17 00:00:00 2001 From: Bryan Elliott Date: Fri, 20 Dec 2024 16:28:54 -0500 Subject: [PATCH 7/8] Class Protect, make extend from Hybrid_Product, because can use either the standalone or Jetpack plugin. --- projects/packages/my-jetpack/src/products/class-protect.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/packages/my-jetpack/src/products/class-protect.php b/projects/packages/my-jetpack/src/products/class-protect.php index ce90c19cc0401..3e1f8e30ca829 100644 --- a/projects/packages/my-jetpack/src/products/class-protect.php +++ b/projects/packages/my-jetpack/src/products/class-protect.php @@ -8,7 +8,7 @@ namespace Automattic\Jetpack\My_Jetpack\Products; use Automattic\Jetpack\Connection\Client; -use Automattic\Jetpack\My_Jetpack\Product; +use Automattic\Jetpack\My_Jetpack\Hybrid_Product; use Automattic\Jetpack\My_Jetpack\Wpcom_Products; use Jetpack_Options; use WP_Error; @@ -16,7 +16,7 @@ /** * Class responsible for handling the Protect product */ -class Protect extends Product { +class Protect extends Hybrid_Product { const FREE_TIER_SLUG = 'free'; const UPGRADED_TIER_SLUG = 'upgraded'; From e24d30b748ec58a4e64b5c1df686a6dfdb418b37 Mon Sep 17 00:00:00 2001 From: Bryan Elliott Date: Fri, 20 Dec 2024 16:37:23 -0500 Subject: [PATCH 8/8] Declare class \Automattic\Jetpack\My_Jetpack\Products\Redirect in class-protect.php. --- projects/packages/my-jetpack/src/products/class-protect.php | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/packages/my-jetpack/src/products/class-protect.php b/projects/packages/my-jetpack/src/products/class-protect.php index 3e1f8e30ca829..b3e14499ddda9 100644 --- a/projects/packages/my-jetpack/src/products/class-protect.php +++ b/projects/packages/my-jetpack/src/products/class-protect.php @@ -10,6 +10,7 @@ use Automattic\Jetpack\Connection\Client; use Automattic\Jetpack\My_Jetpack\Hybrid_Product; use Automattic\Jetpack\My_Jetpack\Wpcom_Products; +use Automattic\Jetpack\Redirect; use Jetpack_Options; use WP_Error;