Skip to content

Commit

Permalink
Update show tooltip on card hover (#37858)
Browse files Browse the repository at this point in the history
* Show tooltip on card hover

* changelog

* Add tracks event for tooltip hover

* Ensure the hover event is only triggered once per session
  • Loading branch information
CodeyGuyDylan authored Jun 14, 2024
1 parent c533aa4 commit 5ce0f85
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 71 deletions.
25 changes: 20 additions & 5 deletions projects/packages/my-jetpack/_inc/components/card/index.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,35 @@
import { Text } from '@automattic/jetpack-components';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import React from 'react';
import styles from './style.module.scss';

export const CardWrapper = props => {
const { children, className } = props;
const { children, className, onMouseEnter, onMouseLeave } = props;

const containerClassName = clsx( styles.container, className );

return <div className={ containerClassName }>{ children }</div>;
return (
<div
onMouseEnter={ onMouseEnter }
onMouseLeave={ onMouseLeave }
onFocus={ onMouseEnter }
onBlur={ onMouseLeave }
className={ containerClassName }
>
{ children }
</div>
);
};

const Card = props => {
const { title, headerRightContent, className, children } = props;
const { title, headerRightContent, className, children, onMouseEnter, onMouseLeave } = props;

return (
<CardWrapper className={ className }>
<CardWrapper
className={ className }
onMouseEnter={ onMouseEnter }
onMouseLeave={ onMouseLeave }
>
<div className={ styles.title }>
<div className={ styles.name }>
<Text variant="title-medium">{ title }</Text>
Expand All @@ -33,6 +46,8 @@ Card.propTypes = {
title: PropTypes.string.isRequired,
className: PropTypes.string,
headerRightContent: PropTypes.node,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
};

export default Card;
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const ConnectedProductCard = ( {
secondaryAction = null,
upgradeInInterstitial = false,
primaryActionOverride,
onMouseEnter,
onMouseLeave,
} ) => {
const { isRegistered, isUserConnected } = useConnection();

Expand Down Expand Up @@ -80,8 +82,9 @@ const ConnectedProductCard = ( {
slug={ slug }
onActivate={ handleActivate }
onInstallStandalone={ installStandalonePlugin }
onActivateStandalone={ installStandalonePlugin }
upgradeInInterstitial={ upgradeInInterstitial }
onMouseEnter={ onMouseEnter }
onMouseLeave={ onMouseLeave }
>
{ children }
</ProductCard>
Expand All @@ -96,6 +99,8 @@ ConnectedProductCard.propTypes = {
additionalActions: PropTypes.array,
primaryActionOverride: PropTypes.object,
secondaryAction: PropTypes.object,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
};

export default ConnectedProductCard;
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ const ProductCard = inprops => {
secondaryAction,
children,
onInstallStandalone,
onActivateStandalone,
onMouseEnter,
onMouseLeave,
} = props;

const isError = status === PRODUCT_STATUSES.ERROR;
Expand Down Expand Up @@ -167,21 +168,6 @@ const ProductCard = inprops => {
[ slug, onInstallStandalone, recordEvent ]
);

/**
* Use a Tracks event to count a standalone plugin activation request
*/
// eslint-disable-next-line no-unused-vars
const activateStandaloneHandler = useCallback(
event => {
event.preventDefault();
recordEvent( 'jetpack_myjetpack_product_card_activate_standalone_plugin_click', {
product: slug,
} );
onActivateStandalone();
},
[ slug, onActivateStandalone, recordEvent ]
);

/**
* Sends an event when the card loads
*/
Expand All @@ -197,6 +183,8 @@ const ProductCard = inprops => {
title={ name }
className={ clsx( styles.container, containerClassName ) }
headerRightContent={ null }
onMouseEnter={ onMouseEnter }
onMouseLeave={ onMouseLeave }
>
<Description />

Expand Down Expand Up @@ -265,6 +253,8 @@ ProductCard.propTypes = {
PRODUCT_STATUSES.CAN_UPGRADE,
PRODUCT_STATUSES.MODULE_DISABLED,
] ).isRequired,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
};

export { PRODUCT_STATUSES };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,26 @@ import { Popover } from '@wordpress/components';
import { useViewportMatch } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
import { arrowUp, Icon } from '@wordpress/icons';
import { useCallback, useEffect, useState, useMemo } from 'react';
import { useEffect, useState, useMemo } from 'react';
import { PRODUCT_STATUSES } from '../../../constants';
import useProduct from '../../../data/products/use-product';
import { getMyJetpackWindowInitialState } from '../../../data/utils/get-my-jetpack-window-state';
import useAnalytics from '../../../hooks/use-analytics';
import useMyJetpackConnection from '../../../hooks/use-my-jetpack-connection';
import { useBoostTooltipCopy } from './use-boost-tooltip-copy';
import type { SpeedScores } from './types';
import type { FC, SetStateAction } from 'react';
import type { SpeedScores, BoostSpeedScoreType } from './types';
import type { SetStateAction } from 'react';

import './style.scss';

const BoostSpeedScore: FC = () => {
const BoostSpeedScore: BoostSpeedScoreType = ( { shouldShowTooltip } ) => {
const { recordEvent } = useAnalytics();
const [ isLoading, setIsLoading ] = useState( false );
const [ speedLetterGrade, setSpeedLetterGrade ] = useState( '' );
const [ currentSpeedScore, setCurrentSpeedScore ] = useState< number | null >( null );
const [ previousSpeedScore, setPreviousSpeedScore ] = useState< number | null >( null );
const [ isSpeedScoreError, setIsSpeedScoreError ] = useState( false );
const [ isTooltipVisible, setIsTooltipVisible ] = useState( false );
const [ hasTooltipBeenViewed, setHasTooltipBeenViewed ] = useState( false );
const isMobileViewport: boolean = useViewportMatch( 'medium', '<' );

const { siteUrl = '', latestBoostSpeedScores } = getMyJetpackWindowInitialState();
Expand Down Expand Up @@ -116,14 +116,6 @@ const BoostSpeedScore: FC = () => {

const tooltipCopy = useBoostTooltipCopy( { speedLetterGrade, boostScoreIncrease } );

const handleTooltipMouseEnter = useCallback( () => {
setIsTooltipVisible( true );
}, [ setIsTooltipVisible ] );

const handleTooltipMouseLeave = useCallback( () => {
setIsTooltipVisible( false );
}, [ setIsTooltipVisible ] );

useEffect( () => {
if ( latestBoostSpeedScores ) {
if ( isBoostActive ) {
Expand All @@ -148,6 +140,16 @@ const BoostSpeedScore: FC = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [] );

useEffect( () => {
if ( ! isLoading && shouldShowTooltip && ! hasTooltipBeenViewed ) {
recordEvent( 'jetpack_boost_card_tooltip_viewed', {
feature: 'jetpack-boost',
position: 'my-jetpack',
} );
setHasTooltipBeenViewed( true );
}
}, [ isLoading, shouldShowTooltip, recordEvent, hasTooltipBeenViewed ] );

return (
! isSpeedScoreError && (
<div className="mj-boost-speed-score">
Expand All @@ -158,28 +160,21 @@ const BoostSpeedScore: FC = () => {
<div className="mj-boost-speed-score__grade">
<span>{ __( 'Your website’s overall speed score:', 'jetpack-my-jetpack' ) }</span>
<span className="mj-boost-speed-score__grade--letter">
<button
onMouseEnter={ handleTooltipMouseEnter }
onFocus={ handleTooltipMouseEnter }
onMouseLeave={ handleTooltipMouseLeave }
onBlur={ handleTooltipMouseLeave }
>
{ speedLetterGrade }
{ isTooltipVisible && (
<Popover
placement={ isMobileViewport ? 'top-end' : 'right' }
noArrow={ false }
offset={ 10 }
>
<p className={ 'boost-score-tooltip__heading' }>
{ /* Add the `&nbsp;` at the end to prevent widows. */ }
{ __( 'Site speed performance:', 'jetpack-my-jetpack' ) }&nbsp;
{ speedLetterGrade }
</p>
<p className={ 'boost-score-tooltip__content' }>{ tooltipCopy }</p>
</Popover>
) }
</button>
{ speedLetterGrade }
{ shouldShowTooltip && (
<Popover
placement={ isMobileViewport ? 'top-end' : 'right' }
noArrow={ false }
offset={ 10 }
>
<p className={ 'boost-score-tooltip__heading' }>
{ /* Add the `&nbsp;` at the end to prevent widows. */ }
{ __( 'Site speed performance:', 'jetpack-my-jetpack' ) }&nbsp;
{ speedLetterGrade }
</p>
<p className={ 'boost-score-tooltip__content' }>{ tooltipCopy }</p>
</Popover>
) }
</span>
</div>
<div className="mj-boost-speed-score__bar">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { __ } from '@wordpress/i18n';
import { useState, useCallback } from 'react';
import { PRODUCT_STATUSES } from '../../../constants';
import ProductCard from '../../connected-product-card';
import BoostSpeedScore from './boost-speed-score';
import type { FC } from 'react';

const BoostCard: FC< { admin: boolean } > = ( { admin } ) => {
const [ shouldShowTooltip, setShouldShowTooltip ] = useState( false );
// Override the primary action button to read "Boost your site" instead
// of the default text, "Lern more".
const primaryActionOverride = {
Expand All @@ -13,9 +15,23 @@ const BoostCard: FC< { admin: boolean } > = ( { admin } ) => {
},
};

const handleMouseEnter = useCallback( () => {
setShouldShowTooltip( true );
}, [ setShouldShowTooltip ] );

const handleMouseLeave = useCallback( () => {
setShouldShowTooltip( false );
}, [ setShouldShowTooltip ] );

return (
<ProductCard admin={ admin } slug="boost" primaryActionOverride={ primaryActionOverride }>
<BoostSpeedScore />
<ProductCard
admin={ admin }
slug="boost"
primaryActionOverride={ primaryActionOverride }
onMouseEnter={ handleMouseEnter }
onMouseLeave={ handleMouseLeave }
>
<BoostSpeedScore shouldShowTooltip={ shouldShowTooltip } />
</ProductCard>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,16 @@ $border_radius: math.div($bar-height, 2);
column-gap: 1em;
margin: 8px 0 8px;
&--letter {
> button {
all: unset;
font-size: var(--font-title-small);
font-weight: bold;
line-height: $bar_height;
color: var(--jp-green);
padding: 0 5px;
border-radius: var(--jp-border-radius);
cursor: pointer;
&:focus {
outline: 1px dotted var( --jp-black );
}
all: unset;
font-size: var(--font-title-small);
font-weight: bold;
line-height: $bar_height;
color: var(--jp-green);
padding: 0 5px;
border-radius: var(--jp-border-radius);
cursor: pointer;
&:focus {
outline: 1px dotted var( --jp-black );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
import type { FC } from 'react';

export type SpeedScores = Window[ 'myJetpackInitialState' ][ 'latestBoostSpeedScores' ];

interface BoostSpeedScoreProps {
shouldShowTooltip: boolean;
}

export type BoostSpeedScoreType = FC< BoostSpeedScoreProps >;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Show tooltip on card hover instead of letter hover

0 comments on commit 5ce0f85

Please sign in to comment.