From bfbeaacccae0cc79ff0350f8440e0421f396cce7 Mon Sep 17 00:00:00 2001
From: Dylan Munson <65001528+CodeyGuyDylan@users.noreply.github.com>
Date: Thu, 19 Dec 2024 10:33:48 -0700
Subject: [PATCH] Fix/connection status inconsistencies (#40632)
* Update connection status card to align with connection banner
* unify the way we connect sites on My Jetpack
* Unify user connection flows in My Jetpack
* changelog
* Fix tests
* Add comment
* Scroll to top all the time when connecting site
* Add slight delay to scroll to top
---
projects/packages/my-jetpack/_inc/admin.jsx | 88 ++++++++-----------
.../connected-product-card/index.tsx | 2 +-
.../components/connection-screen/index.tsx | 4 +
.../connection-status-card/index.tsx | 41 +++++++--
.../connection-status-card/test/component.tsx | 41 +++++++--
.../connection-status-card/types.ts | 5 +-
.../components/connections-section/index.jsx | 2 +-
.../_inc/components/plans-section/index.tsx | 2 +-
.../components/product-card/action-button.tsx | 25 ++++--
.../_inc/components/product-card/index.tsx | 30 ++++++-
.../product-card/secondary-button.tsx | 4 +-
.../welcome-flow/ConnectionStep.tsx | 63 ++-----------
.../_inc/components/welcome-flow/index.tsx | 14 +--
.../packages/my-jetpack/_inc/constants.ts | 1 +
.../_inc/context/notices/noticeTemplates.ts | 11 ---
.../_inc/hooks/use-analytics/index.ts | 8 +-
.../_inc/hooks/use-connect-site/index.ts | 81 +++++++++++++++++
.../hooks/use-my-jetpack-navigate/index.ts | 14 ++-
.../use-site-connection-notice.tsx | 29 +++---
.../packages/my-jetpack/_inc/providers.tsx | 25 ++++++
.../fix-connection-status-inconsistencies | 4 +
21 files changed, 305 insertions(+), 189 deletions(-)
create mode 100644 projects/packages/my-jetpack/_inc/hooks/use-connect-site/index.ts
create mode 100644 projects/packages/my-jetpack/_inc/providers.tsx
create mode 100644 projects/packages/my-jetpack/changelog/fix-connection-status-inconsistencies
diff --git a/projects/packages/my-jetpack/_inc/admin.jsx b/projects/packages/my-jetpack/_inc/admin.jsx
index 5bcaddd417d6d..4d2830eeefb76 100644
--- a/projects/packages/my-jetpack/_inc/admin.jsx
+++ b/projects/packages/my-jetpack/_inc/admin.jsx
@@ -1,8 +1,6 @@
/**
* External dependencies
*/
-import { ThemeProvider } from '@automattic/jetpack-components';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createRoot } from '@wordpress/element';
import { useEffect } from 'react';
import { HashRouter, Navigate, Routes, Route, useLocation } from 'react-router-dom';
@@ -32,10 +30,9 @@ import {
import JetpackAiProductPage from './components/product-interstitial/jetpack-ai/product-page';
import RedeemTokenScreen from './components/redeem-token-screen';
import { MyJetpackRoutes } from './constants';
-import NoticeContextProvider from './context/notices/noticeContext';
-import ValueStoreContextProvider from './context/value-store/valueStoreContext';
import { getMyJetpackWindowInitialState } from './data/utils/get-my-jetpack-window-state';
import './style.module.scss';
+import Providers from './providers';
/**
* Component to scroll window to top on route change.
@@ -50,57 +47,44 @@ function ScrollToTop() {
}
const MyJetpack = () => {
- const queryClient = new QueryClient();
const { loadAddLicenseScreen } = getMyJetpackWindowInitialState();
return (
-
-
-
-
-
-
-
- } />
- } />
- } />
- { /* Redirect the old route for Anti Spam */ }
- }
- />
- } />
- } />
- } />
- }
- />
- } />
- } />
- } />
- } />
- } />
- }
- />
- } />
- { loadAddLicenseScreen && (
- } />
- ) }
- } />
- } />
- } />
- } />
- } />
- } />
-
-
-
-
-
-
+
+
+
+
+ } />
+ } />
+ } />
+ { /* Redirect the old route for Anti Spam */ }
+ }
+ />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ { loadAddLicenseScreen && (
+ } />
+ ) }
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
);
};
diff --git a/projects/packages/my-jetpack/_inc/components/connected-product-card/index.tsx b/projects/packages/my-jetpack/_inc/components/connected-product-card/index.tsx
index 0f2218763f4a8..5953fda2cb767 100644
--- a/projects/packages/my-jetpack/_inc/components/connected-product-card/index.tsx
+++ b/projects/packages/my-jetpack/_inc/components/connected-product-card/index.tsx
@@ -66,7 +66,7 @@ const ConnectedProductCard: FC< ConnectedProductCardProps > = ( {
manageUrl,
} = detail;
- const navigateToConnectionPage = useMyJetpackNavigate( MyJetpackRoutes.Connection );
+ const navigateToConnectionPage = useMyJetpackNavigate( MyJetpackRoutes.ConnectionSkipPricing );
/*
* Redirect only if connected
diff --git a/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx b/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx
index 52954e68d9a06..f8da3fddc6c45 100644
--- a/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx
+++ b/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx
@@ -1,5 +1,6 @@
import { Container, Col, AdminPage } from '@automattic/jetpack-components';
import { __ } from '@wordpress/i18n';
+import { useSearchParams } from 'react-router-dom';
import useMyJetpackConnection from '../../hooks/use-my-jetpack-connection';
import useMyJetpackReturnToPage from '../../hooks/use-my-jetpack-return-to-page';
import CloseLink from '../close-link';
@@ -9,6 +10,8 @@ import styles from './styles.module.scss';
import type { FC } from 'react';
const ConnectionScreen: FC = () => {
+ const [ searchParams ] = useSearchParams();
+ const shouldSkipPricing = searchParams.get( 'skip_pricing' ) === 'true';
const returnToPage = useMyJetpackReturnToPage();
const { apiRoot, apiNonce, registrationNonce } = useMyJetpackConnection();
@@ -28,6 +31,7 @@ const ConnectionScreen: FC = () => {
apiRoot={ apiRoot }
apiNonce={ apiNonce }
registrationNonce={ registrationNonce }
+ skipPricingPage={ shouldSkipPricing }
footer={ }
/>
diff --git a/projects/packages/my-jetpack/_inc/components/connection-status-card/index.tsx b/projects/packages/my-jetpack/_inc/components/connection-status-card/index.tsx
index 404d0fb5bbc95..f2e8827c01da6 100644
--- a/projects/packages/my-jetpack/_inc/components/connection-status-card/index.tsx
+++ b/projects/packages/my-jetpack/_inc/components/connection-status-card/index.tsx
@@ -9,6 +9,7 @@ import { useAllProducts } from '../../data/products/use-product';
import { getMyJetpackWindowInitialState } from '../../data/utils/get-my-jetpack-window-state';
import getProductSlugsThatRequireUserConnection from '../../data/utils/get-product-slugs-that-require-user-connection';
import useAnalytics from '../../hooks/use-analytics';
+import useConnectSite from '../../hooks/use-connect-site';
import useMyJetpackConnection from '../../hooks/use-my-jetpack-connection';
import cloud from './cloud.svg';
import emptyAvatar from './empty-avatar.svg';
@@ -33,6 +34,11 @@ const ConnectionListItem: ConnectionListItemType = ( {
let icon = check;
let statusStyles = '';
+ if ( status === 'info' ) {
+ icon = null;
+ statusStyles = '';
+ }
+
if ( status === 'success' ) {
icon = check;
statusStyles = styles.success;
@@ -50,13 +56,13 @@ const ConnectionListItem: ConnectionListItemType = ( {
if ( status === 'unlock' ) {
icon = lockOutline;
- statusStyles = styles.unlock;
+ statusStyles = '';
}
return (
-
+ { icon && }
{ text }
{ actionText && status !== 'success' && (
@@ -77,9 +83,17 @@ const ConnectionItemButton: ConnectionItemButtonType = ( { actionText, onClick }
const getSiteConnectionLineData: getSiteConnectionLineDataType = ( {
isRegistered,
hasSiteConnectionBrokenModules,
- handleConnectUser,
+ handleConnectSite,
+ siteIsRegistering,
openManageSiteConnectionDialog,
} ) => {
+ if ( siteIsRegistering ) {
+ return {
+ text: __( 'Connecting your siteā¦', 'jetpack-my-jetpack' ),
+ status: 'info',
+ };
+ }
+
if ( isRegistered ) {
return {
onClick: openManageSiteConnectionDialog,
@@ -91,7 +105,7 @@ const getSiteConnectionLineData: getSiteConnectionLineDataType = ( {
if ( hasSiteConnectionBrokenModules ) {
return {
- onClick: handleConnectUser,
+ onClick: handleConnectSite,
text: __( 'Missing site connection to enable some features.', 'jetpack-my-jetpack' ),
actionText: __( 'Connect', 'jetpack-my-jetpack' ),
status: 'error',
@@ -99,7 +113,7 @@ const getSiteConnectionLineData: getSiteConnectionLineDataType = ( {
}
return {
- onClick: handleConnectUser,
+ onClick: handleConnectSite,
text: __( 'Start with Jetpack.', 'jetpack-my-jetpack' ),
actionText: __( 'Connect your site with one click', 'jetpack-my-jetpack' ),
status: 'warning',
@@ -194,13 +208,16 @@ const ConnectionStatusCard: ConnectionStatusCardType = ( {
const { isRegistered, isUserConnected, userConnectionData } = useMyJetpackConnection( {
redirectUri,
} );
-
+ const { siteIsRegistering } = useMyJetpackConnection( {
+ skipUserConnection: true,
+ redirectUri,
+ } );
+ const { lifecycleStats } = getMyJetpackWindowInitialState();
const { recordEvent } = useAnalytics();
const [ isManageConnectionDialogOpen, setIsManageConnectionDialogOpen ] = useState( false );
const { setConnectionStatus, setUserIsConnecting } = useDispatch( CONNECTION_STORE_ID );
const connectUserFn = onConnectUser || setUserIsConnecting;
const avatar = userConnectionData.currentUser?.wpcomUser?.avatar;
- const { lifecycleStats } = getMyJetpackWindowInitialState();
const { brokenModules } = lifecycleStats || {};
const products = useAllProducts();
const hasProductsThatRequireUserConnection =
@@ -272,6 +289,13 @@ const ConnectionStatusCard: ConnectionStatusCardType = ( {
[ connectUserFn, recordEvent, tracksEventData ]
);
+ const { connectSite: handleConnectSite } = useConnectSite( {
+ tracksInfo: {
+ event: 'jetpack_myjetpack_connection_connect_site',
+ properties: tracksEventData,
+ },
+ } );
+
const getConnectionLineStyles = () => {
if ( isRegistered ) {
return '';
@@ -283,7 +307,8 @@ const ConnectionStatusCard: ConnectionStatusCardType = ( {
const siteConnectionLineData = getSiteConnectionLineData( {
isRegistered,
hasSiteConnectionBrokenModules,
- handleConnectUser,
+ handleConnectSite,
+ siteIsRegistering,
openManageSiteConnectionDialog,
} );
diff --git a/projects/packages/my-jetpack/_inc/components/connection-status-card/test/component.tsx b/projects/packages/my-jetpack/_inc/components/connection-status-card/test/component.tsx
index d5e88d4dd6ce2..6f3a89ad32ebc 100644
--- a/projects/packages/my-jetpack/_inc/components/connection-status-card/test/component.tsx
+++ b/projects/packages/my-jetpack/_inc/components/connection-status-card/test/component.tsx
@@ -2,6 +2,7 @@ import '@testing-library/jest-dom';
import { CONNECTION_STORE_ID } from '@automattic/jetpack-connection';
import { render, renderHook, screen } from '@testing-library/react';
import { useSelect } from '@wordpress/data';
+import Providers from '../../../providers';
import ConnectionStatusCard from '../index';
import type { StateProducts, MyJetpackInitialState } from '../../../data/types';
@@ -58,7 +59,9 @@ const setConnectionStore = ( {
hasConnectedOwner = false,
} = {} ) => {
let storeSelect;
- renderHook( () => useSelect( select => ( storeSelect = select( CONNECTION_STORE_ID ) ), [] ) );
+ renderHook( () => useSelect( select => ( storeSelect = select( CONNECTION_STORE_ID ) ), [] ), {
+ wrapper: Providers,
+ } );
jest
.spyOn( storeSelect, 'getConnectionStatus' )
.mockReset()
@@ -80,7 +83,11 @@ describe( 'ConnectionStatusCard', () => {
describe( 'When the site is not registered and has no broken modules', () => {
const setup = () => {
- return render(
);
+ return render(
+
+
+
+ );
};
it( 'renders the correct copy for the site connection line item', () => {
@@ -103,7 +110,11 @@ describe( 'ConnectionStatusCard', () => {
window.myJetpackInitialState.lifecycleStats.brokenModules.needs_site_connection = [
'anti-spam',
];
- return render(
);
+ return render(
+
+
+
+ );
};
it( 'renders the correct copy for the site connection line item', () => {
@@ -125,7 +136,11 @@ describe( 'ConnectionStatusCard', () => {
describe( 'There are no products that require user connection', () => {
const setup = () => {
setConnectionStore( { isRegistered: true } );
- return render(
);
+ return render(
+
+
+
+ );
};
it( 'renders the correct site connection line item', () => {
@@ -145,7 +160,11 @@ describe( 'ConnectionStatusCard', () => {
const setup = () => {
setConnectionStore( { isRegistered: true } );
window.myJetpackInitialState.products.items[ 'anti-spam' ].requires_user_connection = true;
- return render(
);
+ return render(
+
+
+
+ );
};
it( 'renders the correct site connection line item', () => {
@@ -168,7 +187,11 @@ describe( 'ConnectionStatusCard', () => {
window.myJetpackInitialState.lifecycleStats.brokenModules.needs_user_connection = [
'anti-spam',
];
- return render(
);
+ return render(
+
+
+
+ );
};
it( 'renders the correct site connection line item', () => {
@@ -189,7 +212,11 @@ describe( 'ConnectionStatusCard', () => {
describe( 'When the user has connected their WordPress.com account', () => {
const setup = () => {
setConnectionStore( { isRegistered: true, isUserConnected: true, hasConnectedOwner: true } );
- return render(
);
+ return render(
+
+
+
+ );
};
it( 'renders the correct site connection line item', () => {
diff --git a/projects/packages/my-jetpack/_inc/components/connection-status-card/types.ts b/projects/packages/my-jetpack/_inc/components/connection-status-card/types.ts
index d236b530e9cfa..3691852f4cc5e 100644
--- a/projects/packages/my-jetpack/_inc/components/connection-status-card/types.ts
+++ b/projects/packages/my-jetpack/_inc/components/connection-status-card/types.ts
@@ -1,6 +1,6 @@
import type { FC, MouseEvent } from 'react';
-type StatusType = 'warning' | 'error' | 'unlock' | 'success';
+type StatusType = 'warning' | 'error' | 'unlock' | 'success' | 'info';
interface ConnectionListItemProps {
text: string;
@@ -19,7 +19,8 @@ export type ConnectionItemButtonType = FC< {
interface getSiteConnectionLineDataProps {
isRegistered: boolean;
hasSiteConnectionBrokenModules: boolean;
- handleConnectUser: ( e: MouseEvent< HTMLButtonElement > ) => void;
+ siteIsRegistering: boolean;
+ handleConnectSite: ( e: MouseEvent< HTMLButtonElement > ) => void;
openManageSiteConnectionDialog: ( e: MouseEvent ) => void;
}
diff --git a/projects/packages/my-jetpack/_inc/components/connections-section/index.jsx b/projects/packages/my-jetpack/_inc/components/connections-section/index.jsx
index ffb1da1e64cd9..a1ba26eff727f 100644
--- a/projects/packages/my-jetpack/_inc/components/connections-section/index.jsx
+++ b/projects/packages/my-jetpack/_inc/components/connections-section/index.jsx
@@ -12,7 +12,7 @@ import ConnectionStatusCard from '../connection-status-card';
*/
export default function ConnectionsSection() {
const { apiRoot, apiNonce, topJetpackMenuItemUrl, connectedPlugins } = useMyJetpackConnection();
- const navigate = useMyJetpackNavigate( MyJetpackRoutes.Connection );
+ const navigate = useMyJetpackNavigate( MyJetpackRoutes.ConnectionSkipPricing );
const products = useAllProducts();
const onDisconnected = () => document?.location?.reload( true ); // TODO: replace with a better experience.
const productsThatRequireUserConnection = getProductSlugsThatRequireUserConnection( products );
diff --git a/projects/packages/my-jetpack/_inc/components/plans-section/index.tsx b/projects/packages/my-jetpack/_inc/components/plans-section/index.tsx
index b4b3aef6dba03..59922bcd6ef4f 100644
--- a/projects/packages/my-jetpack/_inc/components/plans-section/index.tsx
+++ b/projects/packages/my-jetpack/_inc/components/plans-section/index.tsx
@@ -178,7 +178,7 @@ const PlanSectionFooter: FC< PlanSectionHeaderAndFooterProps > = ( { numberOfPur
recordEvent( 'jetpack_myjetpack_plans_purchase_click' );
}, [ recordEvent ] );
- const navigateToConnectionPage = useMyJetpackNavigate( MyJetpackRoutes.Connection );
+ const navigateToConnectionPage = useMyJetpackNavigate( MyJetpackRoutes.ConnectionSkipPricing );
const activateLicenseClickHandler = useCallback( () => {
recordEvent( 'jetpack_myjetpack_activate_license_click' );
if ( ! isUserConnected ) {
diff --git a/projects/packages/my-jetpack/_inc/components/product-card/action-button.tsx b/projects/packages/my-jetpack/_inc/components/product-card/action-button.tsx
index 82878556ba7c6..eb77e4e25c744 100644
--- a/projects/packages/my-jetpack/_inc/components/product-card/action-button.tsx
+++ b/projects/packages/my-jetpack/_inc/components/product-card/action-button.tsx
@@ -7,14 +7,16 @@ import { useCallback, useState, useEffect, useMemo, useRef } from 'react';
import { PRODUCT_STATUSES } from '../../constants';
import useProduct from '../../data/products/use-product';
import useAnalytics from '../../hooks/use-analytics';
+import useMyJetpackConnection from '../../hooks/use-my-jetpack-connection';
import useOutsideAlerter from '../../hooks/use-outside-alerter';
import styles from './style.module.scss';
import { ProductCardProps } from '.';
import type { SecondaryButtonProps } from './secondary-button';
-import type { FC, ComponentProps } from 'react';
+import type { FC, ComponentProps, MouseEvent } from 'react';
type ActionButtonProps< A = () => void > = ProductCardProps & {
- onFixConnection?: A;
+ onFixUserConnection?: A;
+ onFixSiteConnection?: ( { e }: { e: MouseEvent< HTMLButtonElement > } ) => void;
onManage?: A;
onAdd?: A;
onInstall?: A;
@@ -34,7 +36,8 @@ const ActionButton: FC< ActionButtonProps > = ( {
additionalActions,
primaryActionOverride,
onManage,
- onFixConnection,
+ onFixUserConnection,
+ onFixSiteConnection,
isFetching,
isInstallingStandalone,
className,
@@ -50,6 +53,7 @@ const ActionButton: FC< ActionButtonProps > = ( {
const [ currentAction, setCurrentAction ] = useState< ComponentProps< typeof Button > >( {} );
const { detail } = useProduct( slug );
const { manageUrl, purchaseUrl, managePaidPlanPurchaseUrl, renewPaidPlanPurchaseUrl } = detail;
+ const { siteIsRegistering } = useMyJetpackConnection();
const isManageDisabled = ! manageUrl;
const dropdownRef = useRef( null );
const chevronRef = useRef( null );
@@ -57,7 +61,10 @@ const ActionButton: FC< ActionButtonProps > = ( {
slug === 'jetpack-ai' && debug( slug, detail );
- const isBusy = isFetching || isInstallingStandalone;
+ const isBusy =
+ isFetching ||
+ isInstallingStandalone ||
+ ( siteIsRegistering && status === PRODUCT_STATUSES.SITE_CONNECTION_ERROR );
const hasAdditionalActions = additionalActions?.length > 0;
const buttonState = useMemo< Partial< SecondaryButtonProps > >( () => {
@@ -160,20 +167,19 @@ const ActionButton: FC< ActionButtonProps > = ( {
case PRODUCT_STATUSES.SITE_CONNECTION_ERROR:
return {
...buttonState,
- href: '#/connection',
variant: 'primary',
label: __( 'Connect', 'jetpack-my-jetpack' ),
- onClick: onFixConnection,
+ onClick: onFixSiteConnection,
...( primaryActionOverride &&
PRODUCT_STATUSES.SITE_CONNECTION_ERROR in primaryActionOverride &&
primaryActionOverride[ PRODUCT_STATUSES.SITE_CONNECTION_ERROR ] ),
};
case PRODUCT_STATUSES.USER_CONNECTION_ERROR:
return {
- href: '#/connection',
+ href: '#/connection?skip_pricing=true',
variant: 'primary',
label: __( 'Connect', 'jetpack-my-jetpack' ),
- onClick: onFixConnection,
+ onClick: onFixUserConnection,
...( primaryActionOverride &&
PRODUCT_STATUSES.USER_CONNECTION_ERROR in primaryActionOverride &&
primaryActionOverride[ PRODUCT_STATUSES.USER_CONNECTION_ERROR ] ),
@@ -228,7 +234,8 @@ const ActionButton: FC< ActionButtonProps > = ( {
buttonState,
slug,
onAdd,
- onFixConnection,
+ onFixUserConnection,
+ onFixSiteConnection,
onActivate,
onInstall,
onLearnMore,
diff --git a/projects/packages/my-jetpack/_inc/components/product-card/index.tsx b/projects/packages/my-jetpack/_inc/components/product-card/index.tsx
index 5fbe96fd6f7e5..ba235dfd9fa8b 100644
--- a/projects/packages/my-jetpack/_inc/components/product-card/index.tsx
+++ b/projects/packages/my-jetpack/_inc/components/product-card/index.tsx
@@ -4,6 +4,8 @@ import { useCallback, useEffect } from 'react';
import { PRODUCT_STATUSES } from '../../constants';
import { getMyJetpackWindowInitialState } from '../../data/utils/get-my-jetpack-window-state';
import useAnalytics from '../../hooks/use-analytics';
+import useConnectSite from '../../hooks/use-connect-site';
+import useMyJetpackConnection from '../../hooks/use-my-jetpack-connection';
import Card from '../card';
import ActionButton from './action-button';
import PriceComponent from './pricing-component';
@@ -13,7 +15,7 @@ import Status from './status';
import styles from './style.module.scss';
import type { AdditionalAction, SecondaryAction } from './types';
import type { MutateCallback } from '../../data/use-simple-mutation';
-import type { FC, MouseEventHandler, ReactNode } from 'react';
+import type { FC, MouseEvent, MouseEventHandler, ReactNode } from 'react';
export type ProductCardProps = {
children?: ReactNode;
@@ -86,6 +88,15 @@ const ProductCard: FC< ProductCardProps > = props => {
} );
const { recordEvent } = useAnalytics();
+ const { siteIsRegistering } = useMyJetpackConnection();
+ const isLoading =
+ isFetching || ( siteIsRegistering && status === PRODUCT_STATUSES.SITE_CONNECTION_ERROR );
+ const { connectSite } = useConnectSite( {
+ tracksInfo: {
+ event: 'jetpack_myjetpack_product_card_fix_site_connection',
+ properties: {},
+ },
+ } );
/**
* Calls the passed function onActivate after firing Tracks event
@@ -118,12 +129,22 @@ const ProductCard: FC< ProductCardProps > = props => {
/**
* Calls the passed function onFixConnection after firing Tracks event
*/
- const fixConnectionHandler = useCallback( () => {
+ const fixUserConnectionHandler = useCallback( () => {
recordEvent( 'jetpack_myjetpack_product_card_fixconnection_click', {
product: slug,
} );
}, [ slug, recordEvent ] );
+ /**
+ * Calls the passed function onFixSiteConnection after firing Tracks event
+ */
+ const fixSiteConnectionHandler = useCallback(
+ ( { e }: { e: MouseEvent< HTMLButtonElement > } ) => {
+ connectSite( e );
+ },
+ [ connectSite ]
+ );
+
/**
* Calls when the "Learn more" button is clicked
*/
@@ -182,7 +203,8 @@ const ProductCard: FC< ProductCardProps > = props => {
= props => {