Skip to content

Commit

Permalink
Update/at a glance unify connection ctas (#39585)
Browse files Browse the repository at this point in the history
* Unify connection notices on At A Glance

* changelog

* Fix nudges on settings page

* Fix tests
  • Loading branch information
CodeyGuyDylan authored Oct 1, 2024
1 parent 75f87b5 commit d4ab770
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 73 deletions.
12 changes: 2 additions & 10 deletions projects/plugins/jetpack/_inc/client/at-a-glance/connections.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ export class DashConnections extends Component {
<Gridicon icon="globe" size={ 64 } />
) }
<div className="jp-connection-settings__text">
{ __( 'Your site is connected to WordPress.com.', 'jetpack' ) }
{ this.props.isConnectionOwner && (
<span className="jp-connection-settings__is-owner">
{ __( 'You are the Jetpack owner.', 'jetpack' ) }
Expand Down Expand Up @@ -100,7 +99,7 @@ export class DashConnections extends Component {
*/
userConnection() {
const maybeShowLinkUnlinkBtn = this.props.isConnectionOwner ? null : (
<ConnectButton asLink connectUser={ true } from="connection-settings" />
<ConnectButton asBanner connectUser={ true } from="connection-settings" />
);

let cardContent = '';
Expand Down Expand Up @@ -131,14 +130,7 @@ export class DashConnections extends Component {
}

if ( ! this.props.isLinked ) {
cardContent = (
<div>
<div className="jp-connection-settings__info">
{ __( 'Get the most out of Jetpack.', 'jetpack' ) }
</div>
<div className="jp-connection-settings__actions">{ maybeShowLinkUnlinkBtn }</div>
</div>
);
cardContent = <div className="jp-connection-settings__info">{ maybeShowLinkUnlinkBtn }</div>;
} else if ( this.props.isFetchingUserData ) {
cardContent = __( 'Loading…', 'jetpack' );
} else if ( ! this.props.wpComConnectedUser?.email ) {
Expand Down
34 changes: 19 additions & 15 deletions projects/plugins/jetpack/_inc/client/at-a-glance/monitor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { createInterpolateElement } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import Button from 'components/button';
import DashItem from 'components/dash-item';
import JetpackBanner from 'components/jetpack-banner';
import analytics from 'lib/analytics';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Component } from 'react';
import { connect } from 'react-redux';
import { isOfflineMode, hasConnectedOwner, connectUser } from 'state/connection';
import { isModuleAvailable } from 'state/modules';
Expand Down Expand Up @@ -78,26 +79,29 @@ class DashMonitor extends Component {
support={ support }
className="jp-dash-item__is-inactive"
noToggle={ ! this.props.hasConnectedOwner }
overrideContent={
( ! this.props.hasConnectedOwner && ! this.props.isOfflineMode && (
<JetpackBanner
title={ __(
'Connect your WordPress.com account to enable alerts if your site goes down.',
'jetpack'
) }
noIcon
callToAction={ __( 'Connect', 'jetpack' ) }
onClick={ this.props.connectUser }
eventFeature="monitor"
path="dashboard"
eventProps={ { type: 'connect' } }
/>
) ) ||
null
}
>
<p className="jp-dash-item__description">
{ this.props.isOfflineMode
? __( 'Unavailable in Offline Mode.', 'jetpack' )
: activateMessage }
</p>

{ ! this.props.isOfflineMode && ! this.props.hasConnectedOwner && (
<p className="jp-dash-item__description jp-dash-item__connect">
{ createInterpolateElement(
__(
'<Button>Connect your WordPress.com</Button> account to use this feature.',
'jetpack'
),
{
Button: <Button className="jp-link-button" onClick={ this.connect } />,
}
) }
</p>
) }
</DashItem>
);
}
Expand Down
34 changes: 34 additions & 0 deletions projects/plugins/jetpack/_inc/client/at-a-glance/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -717,3 +717,37 @@ a.jp-dash-item__manage-in-wpcom,
border-top: 1px solid $gray-5;
}
}

.jp-dash-item.jp-connection-type {
border: 1px solid transparent;
border-radius: 4px;

.jp-dash-item__card {
padding: 16px 16px 16px 24px;
}

.jp-dash-item__content {
display: block;
}

.jp-connection-settings__text {
flex-grow: 1;
}

.jp-dash-item__content,
.jp-connection-settings__text {
align-self: center;
}

.dops-banner {
padding: 0;

&__title {
padding: 0 0.5rem 0 0;
}
}

.dops-banner.dops-card {
display: block;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe( 'Connections', () => {
it( 'shows a disconnection link', () => {
render( <DashConnections { ...testProps } />, { initialState: buildInitialState() } );
expect(
withinCard( 'Site connection' ).getByRole( 'button', { name: 'Manage site connection' } )
withinCard( 'Site connection' ).getByRole( 'button', { name: 'Manage' } )
).toBeInTheDocument();
} );

Expand Down Expand Up @@ -128,8 +128,8 @@ describe( 'Connections', () => {
initialState: buildInitialState( { userIsLinked: false } ),
} );
expect(
withinCard( 'Account connection' ).getByRole( 'link', {
name: 'Connect your WordPress.com account',
withinCard( 'Account connection' ).getByRole( 'button', {
name: 'Connect',
} )
).toBeInTheDocument();
} );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { __ } from '@wordpress/i18n';
import { getFragment } from '@wordpress/url';
import Button from 'components/button';
import QuerySiteBenefits from 'components/data/query-site-benefits';
import JetpackBanner from 'components/jetpack-banner';
import analytics from 'lib/analytics';
import PropTypes from 'prop-types';
import React from 'react';
Expand Down Expand Up @@ -47,6 +48,7 @@ export class ConnectButton extends React.Component {
connectUser: PropTypes.bool,
from: PropTypes.string,
asLink: PropTypes.bool,
asBanner: PropTypes.bool,
connectLegend: PropTypes.string,
connectInPlace: PropTypes.bool,
customConnect: PropTypes.func,
Expand Down Expand Up @@ -74,8 +76,11 @@ export class ConnectButton extends React.Component {
}

handleOpenModal = e => {
if ( e ) {
e.preventDefault();
}

analytics.tracks.recordJetpackClick( 'manage_site_connection' );
e.preventDefault();
this.toggleVisibility();
};

Expand All @@ -89,7 +94,9 @@ export class ConnectButton extends React.Component {
};

loadConnectionScreen = e => {
e.preventDefault();
if ( e ) {
e.preventDefault();
}
// If the iframe is already loaded or we don't have a connectUrl yet, return.
if ( this.props.isAuthorizing || this.props.fetchingConnectUrl ) {
return;
Expand Down Expand Up @@ -131,6 +138,23 @@ export class ConnectButton extends React.Component {
);
}

if ( this.props.asBanner ) {
return (
<JetpackBanner
title={ __(
'Get the most out of Jetpack by connecting your WordPress.com account',
'jetpack'
) }
noIcon
callToAction={ __( 'Connect', 'jetpack' ) }
onClick={ this.loadConnectionScreen }
eventFeature="connect-account"
path="dashboard"
eventProps={ { type: 'connect' } }
/>
);
}

let connectUrl = this.props.connectUrl;
if ( this.props.from ) {
connectUrl += `&from=${ this.props.from }`;
Expand Down Expand Up @@ -162,15 +186,15 @@ export class ConnectButton extends React.Component {

if ( this.props.isSiteConnected ) {
return (
<a
role="button"
tabIndex="0"
onKeyDown={ onKeyDownCallback( this.handleOpenModal ) }
<JetpackBanner
title={ __( 'Your site is connected to WordPress.com.', 'jetpack' ) }
noIcon
callToAction={ this.props.connectLegend || __( 'Manage', 'jetpack' ) }
onClick={ this.handleOpenModal }
disabled={ this.props.isDisconnecting }
>
{ this.props.connectLegend || __( 'Manage site connection', 'jetpack' ) }
</a>
eventFeature="manage-site-connection"
path="dashboard"
eventProps={ { type: 'manage' } }
/>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { jest } from '@jest/globals';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { render, screen } from 'test/test-utils';
import { ConnectButton } from '../index';
import { buildInitialState } from './fixtures';

// Mock components that do fetches in the background. We supply needed state directly.
jest.mock( 'components/data/query-site-benefits', () => ( {
Expand All @@ -25,30 +25,15 @@ describe( 'ConnectButton', () => {
asLink: false,
connectInPlace: false,
doNotUseConnectionIframe: false,
asBanner: true,
};

describe( 'Initially', () => {
it( 'renders a button to connect or link', () => {
render( <ConnectButton { ...testProps } fetchingConnectUrl={ true } /> );
expect(
screen.getByRole( 'link', { name: 'Connect your WordPress.com account' } )
).toBeInTheDocument();
} );

it( 'disables the button while fetching the connect URL', () => {
render( <ConnectButton { ...testProps } fetchingConnectUrl={ true } /> );
expect( screen.getByRole( 'link', { name: 'Connect your WordPress.com account' } ) )
// eslint-disable-next-line jest-dom/prefer-enabled-disabled -- `.toBeDisabled()` doesn't work on links.
.toHaveAttribute( 'disabled' );
} );
} );

describe( 'When it is used to link a user', () => {
it( 'has a link to jetpack.wordpress.com', () => {
render( <ConnectButton { ...testProps } /> );
expect(
screen.getByRole( 'link', { name: 'Connect your WordPress.com account' } )
).toHaveAttribute( 'href', 'https://jetpack.wordpress.com/jetpack.authorize/1/' );
render( <ConnectButton { ...testProps } fetchingConnectUrl={ true } />, {
initialState: buildInitialState(),
} );
expect( screen.getByRole( 'button', { name: 'Connect' } ) ).toBeInTheDocument();
} );
} );

Expand All @@ -62,15 +47,15 @@ describe( 'ConnectButton', () => {
};

it( 'has a link to jetpack.wordpress.com', () => {
render( <ConnectButton { ...currentTestProps } /> );
expect(
screen.getByRole( 'link', { name: 'Link your account to WordPress.com' } )
).toHaveAttribute( 'href', 'https://jetpack.wordpress.com/jetpack.authorize/1/' );
render( <ConnectButton { ...currentTestProps } />, {
initialState: buildInitialState(),
} );
expect( screen.getByRole( 'button', { name: 'Connect' } ) ).toBeInTheDocument();
} );

it( 'when clicked, loadConnectionScreen() is called once', async () => {
const user = userEvent.setup();
const loadConnectionScreen = jest.fn( e => e.preventDefault() );
const loadConnectionScreen = jest.fn();

class ConnectButtonMock extends ConnectButton {
constructor( props ) {
Expand All @@ -79,10 +64,10 @@ describe( 'ConnectButton', () => {
}
}

render( <ConnectButtonMock { ...currentTestProps } /> );
await user.click(
screen.getByRole( 'link', { name: 'Link your account to WordPress.com' } )
);
render( <ConnectButtonMock { ...currentTestProps } />, {
initialState: buildInitialState(),
} );
await user.click( screen.getByRole( 'button', { name: 'Connect' } ) );
expect( loadConnectionScreen ).toHaveBeenCalledTimes( 1 );
} );
} );
Expand All @@ -96,7 +81,9 @@ describe( 'ConnectButton', () => {
};

it( 'does not link to a URL', () => {
render( <ConnectButton { ...currentTestProps } /> );
render( <ConnectButton { ...currentTestProps } />, {
initialState: buildInitialState(),
} );
expect(
screen.getByRole( 'button', { name: 'Unlink your account from WordPress.com' } )
).not.toHaveAttribute( 'href' );
Expand Down Expand Up @@ -152,15 +139,17 @@ describe( 'ConnectButton', () => {
};

it( 'does not link to a URL', () => {
render( <ConnectButton { ...currentTestProps } /> );
render( <ConnectButton { ...currentTestProps } />, {
initialState: buildInitialState(),
} );
expect(
screen.getByRole( 'button', { name: 'Disconnect your site from WordPress.com' } )
).not.toHaveAttribute( 'href' );
} );

it( 'when clicked, handleOpenModal() is called once', async () => {
const user = userEvent.setup();
const handleOpenModal = jest.fn( e => e.preventDefault() );
const handleOpenModal = jest.fn();

class ConnectButtonMock extends ConnectButton {
constructor( props ) {
Expand All @@ -169,7 +158,9 @@ describe( 'ConnectButton', () => {
}
}

render( <ConnectButtonMock { ...currentTestProps } /> );
render( <ConnectButtonMock { ...currentTestProps } />, {
initialState: buildInitialState(),
} );
await user.click(
screen.getByRole( 'button', { name: 'Disconnect your site from WordPress.com' } )
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Build an object that can be used as a Redux store initial state.
*
* @return {object} – initial Redux state
*/
export function buildInitialState() {
return {
jetpack: {
initialState: {
userData: {
currentUser: {
permissions: {
manage_modules: true,
},
},
},
},
connection: {
user: {
currentUser: {
isConnected: true,
},
},
},
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: other

Unify connection related CTAs on At A Glance
Loading

0 comments on commit d4ab770

Please sign in to comment.