Add Jetpack connection to plugin benefits step (https://github.com/woocommerce/woocommerce-admin/pull/4374)
* Allow updateActivePlugins to add new plugins instead of replace * Add autoConnect prop to Jetpack Connect component * Add connect component to plugin benefits screen * Add onError and missing prop types for Connect * Update redirect URL after Jetpack connection * Add tests for added active plugins * Skip install if plugin error exists * Update active and installed plugins to use replace flag * Update tests to handle replace flag * Refactor plugin install flow pending state
This commit is contained in:
parent
35616d5f22
commit
65145bf92c
|
@ -21,20 +21,44 @@ class Connect extends Component {
|
|||
props.setIsPending( true );
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { autoConnect, jetpackConnectUrl } = this.props;
|
||||
|
||||
if ( autoConnect && jetpackConnectUrl ) {
|
||||
this.connectJetpack();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate( prevProps ) {
|
||||
const { createNotice, error, isRequesting, setIsPending } = this.props;
|
||||
const {
|
||||
autoConnect,
|
||||
createNotice,
|
||||
error,
|
||||
isRequesting,
|
||||
jetpackConnectUrl,
|
||||
onError,
|
||||
setIsPending,
|
||||
} = this.props;
|
||||
|
||||
if ( prevProps.isRequesting && ! isRequesting ) {
|
||||
setIsPending( false );
|
||||
}
|
||||
|
||||
if ( error && error !== prevProps.error ) {
|
||||
if ( onError ) {
|
||||
onError();
|
||||
}
|
||||
createNotice( 'error', error );
|
||||
}
|
||||
|
||||
if ( autoConnect && jetpackConnectUrl ) {
|
||||
this.connectJetpack();
|
||||
}
|
||||
}
|
||||
|
||||
async connectJetpack() {
|
||||
const { jetpackConnectUrl, onConnect } = this.props;
|
||||
|
||||
if ( onConnect ) {
|
||||
onConnect();
|
||||
}
|
||||
|
@ -42,7 +66,17 @@ class Connect extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { hasErrors, isRequesting, onSkip, skipText } = this.props;
|
||||
const {
|
||||
autoConnect,
|
||||
hasErrors,
|
||||
isRequesting,
|
||||
onSkip,
|
||||
skipText,
|
||||
} = this.props;
|
||||
|
||||
if ( autoConnect ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
@ -73,6 +107,10 @@ class Connect extends Component {
|
|||
}
|
||||
|
||||
Connect.propTypes = {
|
||||
/**
|
||||
* If connection should happen automatically, or requires user confirmation.
|
||||
*/
|
||||
autoConnect: PropTypes.bool,
|
||||
/**
|
||||
* Method to create a displayed notice.
|
||||
*/
|
||||
|
@ -93,6 +131,14 @@ Connect.propTypes = {
|
|||
* Generated Jetpack connection URL.
|
||||
*/
|
||||
jetpackConnectUrl: PropTypes.string,
|
||||
/**
|
||||
* Called before the redirect to Jetpack.
|
||||
*/
|
||||
onConnect: PropTypes.func,
|
||||
/**
|
||||
* Called when the plugin has an error retrieving the jetpackConnectUrl.
|
||||
*/
|
||||
onError: PropTypes.func,
|
||||
/**
|
||||
* Called when the plugin connection is skipped.
|
||||
*/
|
||||
|
@ -112,6 +158,7 @@ Connect.propTypes = {
|
|||
};
|
||||
|
||||
Connect.defaultProps = {
|
||||
autoConnect: false,
|
||||
setIsPending: () => {},
|
||||
};
|
||||
|
||||
|
|
|
@ -12,11 +12,13 @@ import { filter } from 'lodash';
|
|||
* WooCommerce dependencies
|
||||
*/
|
||||
import { Card, H, Plugins } from '@woocommerce/components';
|
||||
import { getAdminLink } from '@woocommerce/wc-admin-settings';
|
||||
import { PLUGINS_STORE_NAME } from '@woocommerce/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Connect from 'dashboard/components/connect';
|
||||
import Logo from './logo';
|
||||
import ManagementIcon from './images/management';
|
||||
import SalesTaxIcon from './images/sales_tax';
|
||||
|
@ -30,8 +32,9 @@ class Benefits extends Component {
|
|||
constructor( props ) {
|
||||
super( props );
|
||||
this.state = {
|
||||
isConnecting: false,
|
||||
isInstalling: false,
|
||||
isPending: false,
|
||||
isActioned: false,
|
||||
};
|
||||
|
||||
this.isJetpackActive = props.activePlugins.includes( 'jetpack' );
|
||||
|
@ -55,19 +58,27 @@ class Benefits extends Component {
|
|||
}
|
||||
|
||||
componentDidUpdate( prevProps, prevState ) {
|
||||
const { goToNextStep, isRequesting } = this.props;
|
||||
const { isInstalling, isPending } = this.state;
|
||||
const { goToNextStep } = this.props;
|
||||
const { isActioned } = this.state;
|
||||
|
||||
// No longer pending or updating profile items, go to next step.
|
||||
if (
|
||||
isPending &&
|
||||
! isRequesting &&
|
||||
! isInstalling &&
|
||||
( prevProps.isRequesting || prevState.isInstalling )
|
||||
isActioned &&
|
||||
! this.isPending() &&
|
||||
( prevProps.isRequesting ||
|
||||
prevState.isConnecting ||
|
||||
prevState.isInstalling )
|
||||
) {
|
||||
goToNextStep();
|
||||
}
|
||||
}
|
||||
|
||||
isPending() {
|
||||
const { isActioned, isConnecting, isInstalling } = this.state;
|
||||
const { isRequesting } = this.props;
|
||||
return isActioned && ( isConnecting || isInstalling || isRequesting );
|
||||
}
|
||||
|
||||
async skipPluginInstall() {
|
||||
const {
|
||||
createNotice,
|
||||
|
@ -75,10 +86,9 @@ class Benefits extends Component {
|
|||
updateProfileItems,
|
||||
} = this.props;
|
||||
|
||||
this.setState( { isPending: true } );
|
||||
|
||||
const plugins = this.isJetpackActive ? 'skipped-wcs' : 'skipped';
|
||||
await updateProfileItems( { plugins } );
|
||||
this.setState( { isActioned: true } );
|
||||
|
||||
if ( isProfileItemsError ) {
|
||||
createNotice(
|
||||
|
@ -99,10 +109,7 @@ class Benefits extends Component {
|
|||
async startPluginInstall() {
|
||||
const { updateProfileItems, updateOptions } = this.props;
|
||||
|
||||
this.setState( {
|
||||
isInstalling: true,
|
||||
isPending: true,
|
||||
} );
|
||||
this.setState( { isActioned: true, isInstalling: true } );
|
||||
|
||||
await updateOptions( {
|
||||
woocommerce_setup_jetpack_opted_in: true,
|
||||
|
@ -191,7 +198,8 @@ class Benefits extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { isInstalling, isPending } = this.state;
|
||||
const { isConnecting, isInstalling } = this.state;
|
||||
const { isJetpackConnected, isRequesting } = this.props;
|
||||
|
||||
const pluginNamesString = this.pluginsToInstall
|
||||
.map( ( pluginSlug ) => pluginNames[ pluginSlug ] )
|
||||
|
@ -212,8 +220,10 @@ class Benefits extends Component {
|
|||
<div className="woocommerce-profile-wizard__card-actions">
|
||||
<Button
|
||||
isPrimary
|
||||
isBusy={ isPending && isInstalling }
|
||||
disabled={ isPending }
|
||||
isBusy={
|
||||
this.isPending() && ( isInstalling || isConnecting )
|
||||
}
|
||||
disabled={ this.isPending() }
|
||||
onClick={ this.startPluginInstall }
|
||||
className="woocommerce-profile-wizard__continue"
|
||||
>
|
||||
|
@ -221,8 +231,10 @@ class Benefits extends Component {
|
|||
</Button>
|
||||
<Button
|
||||
isDefault
|
||||
isBusy={ isPending && ! isInstalling }
|
||||
disabled={ isPending }
|
||||
isBusy={
|
||||
this.isPending() && ! isInstalling && ! isConnecting
|
||||
}
|
||||
disabled={ this.isPending() }
|
||||
className="woocommerce-profile-wizard__skip"
|
||||
onClick={ this.skipPluginInstall }
|
||||
>
|
||||
|
@ -233,14 +245,37 @@ class Benefits extends Component {
|
|||
<Plugins
|
||||
autoInstall
|
||||
onComplete={ () =>
|
||||
this.setState( { isInstalling: false } )
|
||||
this.setState( {
|
||||
isInstalling: false,
|
||||
isConnecting: ! isJetpackConnected,
|
||||
} )
|
||||
}
|
||||
onError={ () =>
|
||||
this.setState( { isInstalling: false } )
|
||||
this.setState( {
|
||||
isInstalling: false,
|
||||
} )
|
||||
}
|
||||
pluginSlugs={ this.pluginsToInstall }
|
||||
/>
|
||||
) }
|
||||
|
||||
{ /* Make sure we're finished requesting since this will auto redirect us. */ }
|
||||
{ isConnecting && ! isJetpackConnected && ! isRequesting && (
|
||||
<Connect
|
||||
autoConnect
|
||||
onConnect={ () => {
|
||||
recordEvent(
|
||||
'storeprofiler_jetpack_connect_redirect'
|
||||
);
|
||||
} }
|
||||
onError={ () =>
|
||||
this.setState( { isConnecting: false } )
|
||||
}
|
||||
redirectUrl={ getAdminLink(
|
||||
'admin.php?page=wc-admin&reset_profiler=0'
|
||||
) }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
|
||||
<p className="woocommerce-profile-wizard__benefits-install-notice">
|
||||
|
@ -271,7 +306,9 @@ export default compose(
|
|||
isGetProfileItemsRequesting,
|
||||
} = select( 'wc-api' );
|
||||
|
||||
const { getActivePlugins } = select( PLUGINS_STORE_NAME );
|
||||
const { getActivePlugins, isJetpackConnected } = select(
|
||||
PLUGINS_STORE_NAME
|
||||
);
|
||||
|
||||
const isProfileItemsError = Boolean( getProfileItemsError() );
|
||||
const activePlugins = getActivePlugins();
|
||||
|
@ -281,6 +318,7 @@ export default compose(
|
|||
activePlugins,
|
||||
isProfileItemsError,
|
||||
profileItems,
|
||||
isJetpackConnected: isJetpackConnected(),
|
||||
isRequesting: isGetProfileItemsRequesting(),
|
||||
};
|
||||
} ),
|
||||
|
|
|
@ -12,18 +12,19 @@ import TYPES from './action-types';
|
|||
import { WC_ADMIN_NAMESPACE } from '../constants';
|
||||
import { pluginNames } from './constants';
|
||||
|
||||
export function updateActivePlugins( active ) {
|
||||
export function updateActivePlugins( active, replace = false ) {
|
||||
return {
|
||||
type: TYPES.UPDATE_ACTIVE_PLUGINS,
|
||||
active,
|
||||
replace,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateInstalledPlugins( installed, added ) {
|
||||
export function updateInstalledPlugins( installed, replace = false ) {
|
||||
return {
|
||||
type: TYPES.UPDATE_INSTALLED_PLUGINS,
|
||||
installed,
|
||||
added,
|
||||
replace,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -21,20 +21,20 @@ const plugins = (
|
|||
type,
|
||||
active,
|
||||
installed,
|
||||
added,
|
||||
selector,
|
||||
isRequesting,
|
||||
error,
|
||||
jetpackConnection,
|
||||
redirectUrl,
|
||||
jetpackConnectUrl,
|
||||
replace,
|
||||
}
|
||||
) => {
|
||||
switch ( type ) {
|
||||
case TYPES.UPDATE_ACTIVE_PLUGINS:
|
||||
state = {
|
||||
...state,
|
||||
active,
|
||||
active: replace ? active : concat( state.active, active ),
|
||||
requesting: {
|
||||
...state.requesting,
|
||||
getActivePlugins: false,
|
||||
|
@ -50,7 +50,9 @@ const plugins = (
|
|||
case TYPES.UPDATE_INSTALLED_PLUGINS:
|
||||
state = {
|
||||
...state,
|
||||
installed: added ? concat( state.installed, added ) : installed,
|
||||
installed: replace
|
||||
? installed
|
||||
: concat( state.installed, installed ),
|
||||
requesting: {
|
||||
...state.requesting,
|
||||
getInstalledPlugins: false,
|
||||
|
|
|
@ -27,7 +27,7 @@ export function* getActivePlugins() {
|
|||
method: 'GET',
|
||||
} );
|
||||
|
||||
yield updateActivePlugins( results.plugins );
|
||||
yield updateActivePlugins( results.plugins, true );
|
||||
} catch ( error ) {
|
||||
yield setError( 'getActivePlugins', error );
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ export function* getInstalledPlugins() {
|
|||
method: 'GET',
|
||||
} );
|
||||
|
||||
yield updateInstalledPlugins( results );
|
||||
yield updateInstalledPlugins( results, true );
|
||||
} catch ( error ) {
|
||||
yield setError( 'getInstalledPlugins', error );
|
||||
}
|
||||
|
|
|
@ -19,11 +19,17 @@ describe( 'plugins reducer', () => {
|
|||
expect( state ).not.toBe( defaultState );
|
||||
} );
|
||||
|
||||
it( 'should handle UPDATE_ACTIVE_PLUGINS', () => {
|
||||
const state = reducer( defaultState, {
|
||||
type: TYPES.UPDATE_ACTIVE_PLUGINS,
|
||||
active: [ 'jetpack' ],
|
||||
} );
|
||||
it( 'should handle UPDATE_ACTIVE_PLUGINS with replace', () => {
|
||||
const state = reducer(
|
||||
{
|
||||
active: [ 'plugins', 'to', 'overwrite' ],
|
||||
},
|
||||
{
|
||||
type: TYPES.UPDATE_ACTIVE_PLUGINS,
|
||||
active: [ 'jetpack' ],
|
||||
replace: true,
|
||||
}
|
||||
);
|
||||
|
||||
/* eslint-disable dot-notation */
|
||||
|
||||
|
@ -35,11 +41,42 @@ describe( 'plugins reducer', () => {
|
|||
expect( state.active[ 0 ] ).toBe( 'jetpack' );
|
||||
} );
|
||||
|
||||
it( 'should handle UPDATE_INSTALLED_PLUGINS', () => {
|
||||
const state = reducer( defaultState, {
|
||||
type: TYPES.UPDATE_INSTALLED_PLUGINS,
|
||||
installed: [ 'jetpack' ],
|
||||
} );
|
||||
it( 'should handle UPDATE_ACTIVE_PLUGINS with active plugins', () => {
|
||||
const state = reducer(
|
||||
{
|
||||
active: [ 'jetpack' ],
|
||||
installed: [ 'jetpack' ],
|
||||
requesting: {},
|
||||
errors: {},
|
||||
},
|
||||
{
|
||||
type: TYPES.UPDATE_ACTIVE_PLUGINS,
|
||||
installed: null,
|
||||
active: [ 'woocommerce-services' ],
|
||||
}
|
||||
);
|
||||
|
||||
/* eslint-disable dot-notation */
|
||||
|
||||
expect( state.requesting[ 'getActivePlugins' ] ).toBe( false );
|
||||
expect( state.errors[ 'getActivePlugins' ] ).toBe( false );
|
||||
/* eslint-enable dot-notation */
|
||||
|
||||
expect( state.active ).toHaveLength( 2 );
|
||||
expect( state.active[ 1 ] ).toBe( 'woocommerce-services' );
|
||||
} );
|
||||
|
||||
it( 'should handle UPDATE_INSTALLED_PLUGINS with replace', () => {
|
||||
const state = reducer(
|
||||
{
|
||||
active: [ 'plugins', 'to', 'overwrite' ],
|
||||
},
|
||||
{
|
||||
type: TYPES.UPDATE_INSTALLED_PLUGINS,
|
||||
installed: [ 'jetpack' ],
|
||||
replace: true,
|
||||
}
|
||||
);
|
||||
|
||||
/* eslint-disable dot-notation */
|
||||
|
||||
|
@ -51,7 +88,7 @@ describe( 'plugins reducer', () => {
|
|||
expect( state.installed[ 0 ] ).toBe( 'jetpack' );
|
||||
} );
|
||||
|
||||
it( 'should handle UPDATE_INSTALLED_PLUGINS with added plugins', () => {
|
||||
it( 'should handle UPDATE_INSTALLED_PLUGINS with installed plugins', () => {
|
||||
const state = reducer(
|
||||
{
|
||||
active: [ 'jetpack' ],
|
||||
|
@ -61,8 +98,7 @@ describe( 'plugins reducer', () => {
|
|||
},
|
||||
{
|
||||
type: TYPES.UPDATE_INSTALLED_PLUGINS,
|
||||
installed: null,
|
||||
added: [ 'woocommerce-services' ],
|
||||
installed: [ 'woocommerce-services' ],
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -34,8 +34,11 @@ export const withPluginsHydration = ( data ) => ( OriginalComponent ) => {
|
|||
startResolution( 'getActivePlugins', [] );
|
||||
startResolution( 'getInstalledPlugins', [] );
|
||||
startResolution( 'isJetpackConnected', [] );
|
||||
updateActivePlugins( dataRef.current.activePlugins );
|
||||
updateInstalledPlugins( dataRef.current.installedPlugins );
|
||||
updateActivePlugins( dataRef.current.activePlugins, true );
|
||||
updateInstalledPlugins(
|
||||
dataRef.current.installedPlugins,
|
||||
true
|
||||
);
|
||||
updateIsJetpackConnected(
|
||||
dataRef.current.jetpackStatus &&
|
||||
dataRef.current.jetpackStatus.isActive
|
||||
|
|
Loading…
Reference in New Issue