Fix free features is still rendered when no recommendation (#33923)
* Fix free features is still rendered when there is no recommendation * Add changelog * Update BusinessDetails tab to go to next step if no installable extensions * Update spinner
This commit is contained in:
parent
9620704d42
commit
d09d59186d
|
@ -13,6 +13,7 @@ import {
|
|||
__experimentalText as Text,
|
||||
FlexItem,
|
||||
CheckboxControl,
|
||||
Spinner,
|
||||
} from '@wordpress/components';
|
||||
import { withDispatch, withSelect } from '@wordpress/data';
|
||||
import { SelectControl, Form, TextControl } from '@woocommerce/components';
|
||||
|
@ -35,7 +36,10 @@ import { employeeOptions } from '../../data/employee-options';
|
|||
import { sellingVenueOptions } from '../../data/selling-venue-options';
|
||||
import { getRevenueOptions } from '../../data/revenue-options';
|
||||
import { getProductCountOptions } from '../../data/product-options';
|
||||
import { SelectiveExtensionsBundle } from './selective-extensions-bundle';
|
||||
import {
|
||||
SelectiveExtensionsBundle,
|
||||
getInstallableExtensions,
|
||||
} from './selective-extensions-bundle';
|
||||
import { getPluginSlug, getPluginTrackKey, getTimeFrame } from '~/utils';
|
||||
import './style.scss';
|
||||
|
||||
|
@ -146,6 +150,8 @@ class BusinessDetails extends Component {
|
|||
this.state.savedValues,
|
||||
this.persistValues
|
||||
);
|
||||
// Refetch the free extensions data
|
||||
props.invalidateResolutionForStoreSelector( 'getFreeExtensions' );
|
||||
}
|
||||
|
||||
async onContinue(
|
||||
|
@ -430,13 +436,19 @@ class BusinessDetails extends Component {
|
|||
<Form
|
||||
initialValues={ this.state.savedValues.businessDetailsTab }
|
||||
onSubmit={ ( values ) => {
|
||||
this.setState( {
|
||||
savedValues: {
|
||||
...this.state.savedValues,
|
||||
businessDetailsTab: values,
|
||||
},
|
||||
currentTab: BUSINESS_FEATURES_TAB_NAME,
|
||||
} );
|
||||
if ( this.props.hasInstallableExtensions ) {
|
||||
this.setState( {
|
||||
savedValues: {
|
||||
...this.state.savedValues,
|
||||
businessDetailsTab: values,
|
||||
},
|
||||
currentTab: BUSINESS_FEATURES_TAB_NAME,
|
||||
} );
|
||||
} else {
|
||||
goToNextStep( {
|
||||
step: BUSINESS_FEATURES_TAB_NAME,
|
||||
} );
|
||||
}
|
||||
|
||||
this.trackBusinessDetailsStep( values );
|
||||
recordEvent( 'storeprofiler_step_view', {
|
||||
|
@ -645,13 +657,10 @@ class BusinessDetails extends Component {
|
|||
}
|
||||
|
||||
renderFreeFeaturesStep() {
|
||||
const { isInstallingActivating, settings, profileItems } = this.props;
|
||||
const { isInstallingActivating } = this.props;
|
||||
const {
|
||||
savedValues: { freeFeaturesTab },
|
||||
} = this.state;
|
||||
const country = settings.woocommerce_default_country
|
||||
? settings.woocommerce_default_country
|
||||
: null;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -681,9 +690,7 @@ class BusinessDetails extends Component {
|
|||
<SelectiveExtensionsBundle
|
||||
isInstallingActivating={ isInstallingActivating }
|
||||
onSubmit={ this.onContinue }
|
||||
country={ country }
|
||||
industry={ profileItems.industry }
|
||||
productTypes={ profileItems.product_types }
|
||||
installableExtensions={ this.props.installableExtensions }
|
||||
installExtensionOptions={
|
||||
freeFeaturesTab.installExtensionOptions
|
||||
}
|
||||
|
@ -700,10 +707,17 @@ class BusinessDetails extends Component {
|
|||
this.setState( {
|
||||
savedValues,
|
||||
} );
|
||||
this.props.updateCurrentStepValues(
|
||||
this.props.step.key,
|
||||
savedValues
|
||||
);
|
||||
|
||||
if (
|
||||
// Only update current step values when current state's installExtensionOptions is not undefined.
|
||||
this.state.savedValues.freeFeaturesTab
|
||||
.installExtensionOptions
|
||||
) {
|
||||
this.props.updateCurrentStepValues(
|
||||
this.props.step.key,
|
||||
savedValues
|
||||
);
|
||||
}
|
||||
} }
|
||||
/>
|
||||
</>
|
||||
|
@ -711,6 +725,36 @@ class BusinessDetails extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const tabs = [];
|
||||
if ( ! this.props.hasFinishedGetFreeExtensionsResolution ) {
|
||||
return (
|
||||
<div className="woocommerce-admin__business-details__spinner">
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
tabs.push( {
|
||||
name:
|
||||
this.state.currentTab === BUSINESS_DETAILS_TAB_NAME
|
||||
? 'current-tab'
|
||||
: BUSINESS_DETAILS_TAB_NAME,
|
||||
id: BUSINESS_DETAILS_TAB_NAME,
|
||||
title: __( 'Business details', 'woocommerce' ),
|
||||
} );
|
||||
|
||||
if ( this.props.hasInstallableExtensions ) {
|
||||
tabs.push( {
|
||||
name:
|
||||
this.state.currentTab === BUSINESS_FEATURES_TAB_NAME
|
||||
? 'current-tab'
|
||||
: BUSINESS_FEATURES_TAB_NAME,
|
||||
id: BUSINESS_FEATURES_TAB_NAME,
|
||||
title: __( 'Free features', 'woocommerce' ),
|
||||
className: this.state.isValid ? '' : 'is-disabled',
|
||||
} );
|
||||
}
|
||||
|
||||
// There is a hack here to help us manage the selected tab programmatically.
|
||||
// We set the tab name "current-tab". when its the one we want selected. This tricks
|
||||
// the logic in the TabPanel and allows us to switch which tab has the name "current-tab"
|
||||
|
@ -733,25 +777,7 @@ class BusinessDetails extends Component {
|
|||
} );
|
||||
}
|
||||
} }
|
||||
tabs={ [
|
||||
{
|
||||
name:
|
||||
this.state.currentTab === BUSINESS_DETAILS_TAB_NAME
|
||||
? 'current-tab'
|
||||
: BUSINESS_DETAILS_TAB_NAME,
|
||||
id: BUSINESS_DETAILS_TAB_NAME,
|
||||
title: __( 'Business details', 'woocommerce' ),
|
||||
},
|
||||
{
|
||||
name:
|
||||
this.state.currentTab === BUSINESS_FEATURES_TAB_NAME
|
||||
? 'current-tab'
|
||||
: BUSINESS_FEATURES_TAB_NAME,
|
||||
id: BUSINESS_FEATURES_TAB_NAME,
|
||||
title: __( 'Free features', 'woocommerce' ),
|
||||
className: this.state.isValid ? '' : 'is-disabled',
|
||||
},
|
||||
] }
|
||||
tabs={ tabs }
|
||||
>
|
||||
{ ( tab ) => <>{ this.getTab( tab.id ) }</> }
|
||||
</TabPanel>
|
||||
|
@ -771,29 +797,54 @@ BusinessDetails.contextType = CurrencyContext;
|
|||
export const BusinessFeaturesList = compose(
|
||||
withSelect( ( select ) => {
|
||||
const { getSettings, getSettingsError } = select( SETTINGS_STORE_NAME );
|
||||
const { getProfileItems, getOnboardingError } = select(
|
||||
ONBOARDING_STORE_NAME
|
||||
);
|
||||
const {
|
||||
getProfileItems,
|
||||
getOnboardingError,
|
||||
getFreeExtensions,
|
||||
hasFinishedResolution,
|
||||
} = select( ONBOARDING_STORE_NAME );
|
||||
const { getPluginsError, isPluginsRequesting } =
|
||||
select( PLUGINS_STORE_NAME );
|
||||
const { general: settings = {} } = getSettings( 'general' );
|
||||
|
||||
const freeExtensions = getFreeExtensions();
|
||||
const profileItems = getProfileItems();
|
||||
const country = settings.woocommerce_default_country
|
||||
? settings.woocommerce_default_country
|
||||
: null;
|
||||
|
||||
const installableExtensions = freeExtensions
|
||||
? getInstallableExtensions( {
|
||||
freeExtensionBundleByCategory: freeExtensions,
|
||||
country,
|
||||
productTypes: profileItems.product_types,
|
||||
} )
|
||||
: [];
|
||||
const hasInstallableExtensions = installableExtensions.some(
|
||||
( { plugins } ) => plugins.length > 0
|
||||
);
|
||||
|
||||
return {
|
||||
hasInstallActivateError:
|
||||
getPluginsError( 'installPlugins' ) ||
|
||||
getPluginsError( 'activatePlugins' ),
|
||||
hasInstallableExtensions,
|
||||
hasFinishedGetFreeExtensionsResolution:
|
||||
hasFinishedResolution( 'getFreeExtensions' ),
|
||||
installableExtensions,
|
||||
isError: Boolean( getOnboardingError( 'updateProfileItems' ) ),
|
||||
profileItems: getProfileItems(),
|
||||
isSettingsError: Boolean( getSettingsError( 'general' ) ),
|
||||
settings,
|
||||
isInstallingActivating:
|
||||
isPluginsRequesting( 'installPlugins' ) ||
|
||||
isPluginsRequesting( 'activatePlugins' ) ||
|
||||
isPluginsRequesting( 'getJetpackConnectUrl' ),
|
||||
profileItems,
|
||||
settings,
|
||||
};
|
||||
} ),
|
||||
withDispatch( ( dispatch ) => {
|
||||
const { updateProfileItems } = dispatch( ONBOARDING_STORE_NAME );
|
||||
const { updateProfileItems, invalidateResolutionForStoreSelector } =
|
||||
dispatch( ONBOARDING_STORE_NAME );
|
||||
const { installAndActivatePlugins } = dispatch( PLUGINS_STORE_NAME );
|
||||
const { createNotice } = dispatch( 'core/notices' );
|
||||
|
||||
|
@ -801,6 +852,7 @@ export const BusinessFeaturesList = compose(
|
|||
createNotice,
|
||||
installAndActivatePlugins,
|
||||
updateProfileItems,
|
||||
invalidateResolutionForStoreSelector,
|
||||
};
|
||||
} )
|
||||
)( BusinessDetails );
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useEffect, useMemo, useState } from '@wordpress/element';
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
import { Button, Card, CheckboxControl, Spinner } from '@wordpress/components';
|
||||
import { Text } from '@woocommerce/experimental';
|
||||
import { Link } from '@woocommerce/components';
|
||||
import { __, _n, sprintf } from '@wordpress/i18n';
|
||||
import { Icon, chevronDown, chevronUp } from '@wordpress/icons';
|
||||
import interpolateComponents from '@automattic/interpolate-components';
|
||||
import { pluginNames, ONBOARDING_STORE_NAME } from '@woocommerce/data';
|
||||
import { pluginNames } from '@woocommerce/data';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -234,55 +233,38 @@ export const createInstallExtensionOptions = (
|
|||
}, prevInstallExtensionOptions );
|
||||
};
|
||||
|
||||
export const getInstallableExtensions = ( {
|
||||
freeExtensionBundleByCategory,
|
||||
country,
|
||||
productTypes,
|
||||
} ) => {
|
||||
return freeExtensionBundleByCategory.filter( ( extensionBundle ) => {
|
||||
if (
|
||||
window.wcAdminFeatures &&
|
||||
window.wcAdminFeatures.subscriptions &&
|
||||
getCountryCode( country ) === 'US'
|
||||
) {
|
||||
if ( productTypes.includes( 'subscriptions' ) ) {
|
||||
extensionBundle.plugins = extensionBundle.plugins.filter(
|
||||
( extension ) =>
|
||||
extension.key !== 'woocommerce-payments' ||
|
||||
( extension.key === 'woocommerce-payments' &&
|
||||
! extension.is_activated )
|
||||
);
|
||||
}
|
||||
}
|
||||
return ALLOWED_PLUGIN_CATEGORIES.includes( extensionBundle.key );
|
||||
} );
|
||||
};
|
||||
|
||||
export const SelectiveExtensionsBundle = ( {
|
||||
isInstallingActivating,
|
||||
onSubmit,
|
||||
country,
|
||||
productTypes,
|
||||
industry,
|
||||
setInstallExtensionOptions,
|
||||
installableExtensions,
|
||||
installExtensionOptions = { install_extensions: true },
|
||||
} ) => {
|
||||
const [ showExtensions, setShowExtensions ] = useState( false );
|
||||
const { freeExtensions: freeExtensionBundleByCategory, isResolving } =
|
||||
useSelect( ( select ) => {
|
||||
const { getFreeExtensions, hasFinishedResolution } = select(
|
||||
ONBOARDING_STORE_NAME
|
||||
);
|
||||
return {
|
||||
freeExtensions: getFreeExtensions(),
|
||||
isResolving: ! hasFinishedResolution( 'getFreeExtensions' ),
|
||||
};
|
||||
} );
|
||||
|
||||
const { invalidateResolutionForStoreSelector } = useDispatch(
|
||||
ONBOARDING_STORE_NAME
|
||||
);
|
||||
|
||||
useEffect( () => {
|
||||
invalidateResolutionForStoreSelector( 'getFreeExtensions' );
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ country, industry ] );
|
||||
|
||||
const installableExtensions = useMemo( () => {
|
||||
return freeExtensionBundleByCategory.filter( ( extensionBundle ) => {
|
||||
if (
|
||||
window.wcAdminFeatures &&
|
||||
window.wcAdminFeatures.subscriptions &&
|
||||
getCountryCode( country ) === 'US'
|
||||
) {
|
||||
if ( productTypes.includes( 'subscriptions' ) ) {
|
||||
extensionBundle.plugins = extensionBundle.plugins.filter(
|
||||
( extension ) =>
|
||||
extension.key !== 'woocommerce-payments' ||
|
||||
( extension.key === 'woocommerce-payments' &&
|
||||
! extension.is_activated )
|
||||
);
|
||||
}
|
||||
}
|
||||
return ALLOWED_PLUGIN_CATEGORIES.includes( extensionBundle.key );
|
||||
} );
|
||||
}, [ freeExtensionBundleByCategory, productTypes, country ] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( isInstallingActivating || installableExtensions.length === 0 ) {
|
||||
|
@ -401,8 +383,8 @@ export const SelectiveExtensionsBundle = ( {
|
|||
installableExtensions
|
||||
);
|
||||
} }
|
||||
isBusy={ isInstallingActivating || isResolving }
|
||||
disabled={ isInstallingActivating || isResolving }
|
||||
isBusy={ isInstallingActivating }
|
||||
disabled={ isInstallingActivating }
|
||||
isPrimary
|
||||
>
|
||||
{ __( 'Continue', 'woocommerce' ) }
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { render } from '@testing-library/react';
|
||||
import { useSelect, useDispatch } from '@wordpress/data';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { pluginNames } from '@woocommerce/data';
|
||||
|
||||
|
@ -15,25 +14,21 @@ jest.mock( '../../app-illustration', () => ( {
|
|||
AppIllustration: jest.fn().mockReturnValue( '[illustration]' ),
|
||||
} ) );
|
||||
|
||||
jest.mock( '@wordpress/data', () => ( {
|
||||
...jest.requireActual( '@wordpress/data' ),
|
||||
useSelect: jest.fn(),
|
||||
useDispatch: jest.fn().mockImplementation( () => ( {
|
||||
updateOptions: jest.fn(),
|
||||
installAndActivatePlugins: jest.fn(),
|
||||
} ) ),
|
||||
} ) );
|
||||
|
||||
jest.mock( '@woocommerce/data', () => ( {
|
||||
pluginNames: {
|
||||
'woocommerce-payments': 'WooCommerce Payments',
|
||||
mailpoet: 'Mailpoet',
|
||||
random: 'Random',
|
||||
'google-listings-and-ads': 'Google Listings and Ads',
|
||||
},
|
||||
} ) );
|
||||
|
||||
const freeExtensions = [
|
||||
{
|
||||
key: 'task-list/reach',
|
||||
title: 'Reach out to customers',
|
||||
plugins: [
|
||||
{
|
||||
key: 'random',
|
||||
name: 'Random',
|
||||
description: 'Random description',
|
||||
manage_url: 'admin.php?page=mailpoet-newsletters',
|
||||
is_visible: true,
|
||||
is_installed: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'obw/basics',
|
||||
title: 'Get the basics',
|
||||
|
@ -53,20 +48,6 @@ const freeExtensions = [
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'task-list/reach',
|
||||
title: 'Reach out to customers',
|
||||
plugins: [
|
||||
{
|
||||
key: 'random',
|
||||
name: 'Random',
|
||||
description: 'Random description',
|
||||
manage_url: 'admin.php?page=mailpoet-newsletters',
|
||||
is_visible: true,
|
||||
is_installed: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'obw/grow',
|
||||
title: 'Grow your store',
|
||||
|
@ -84,23 +65,13 @@ const freeExtensions = [
|
|||
];
|
||||
|
||||
describe( 'Selective extensions bundle', () => {
|
||||
beforeAll( () => {
|
||||
useSelect.mockReturnValue( {
|
||||
freeExtensions,
|
||||
isResolving: false,
|
||||
} );
|
||||
|
||||
useDispatch.mockReturnValue( {
|
||||
invalidateResolutionForStoreSelector: () => {},
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'should list installable free extensions from obw/basics and obw/grow', () => {
|
||||
const mockSetInstallExtensionOptions = jest.fn();
|
||||
// Render once to get installExtensionOptions
|
||||
render(
|
||||
<SelectiveExtensionsBundle
|
||||
isInstallingActivating={ false }
|
||||
installableExtensions={ freeExtensions.slice( 1 ) }
|
||||
setInstallExtensionOptions={ mockSetInstallExtensionOptions }
|
||||
/>
|
||||
);
|
||||
|
@ -109,6 +80,7 @@ describe( 'Selective extensions bundle', () => {
|
|||
<SelectiveExtensionsBundle
|
||||
isInstallingActivating={ false }
|
||||
setInstallExtensionOptions={ mockSetInstallExtensionOptions }
|
||||
installableExtensions={ freeExtensions.slice( 1 ) }
|
||||
installExtensionOptions={
|
||||
mockSetInstallExtensionOptions.mock.calls[ 0 ][ 0 ]
|
||||
}
|
||||
|
@ -126,15 +98,14 @@ describe( 'Selective extensions bundle', () => {
|
|||
new RegExp( pluginNames[ 'google-listings-and-ads' ] )
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
queryByText( new RegExp( pluginNames.random ) )
|
||||
).not.toBeInTheDocument();
|
||||
expect( queryByText( new RegExp( 'Random' ) ) ).not.toBeInTheDocument();
|
||||
} );
|
||||
|
||||
it( 'should list installable extensions when dropdown is clicked', () => {
|
||||
const { getAllByRole, getByText, queryByText } = render(
|
||||
<SelectiveExtensionsBundle
|
||||
isInstallingActivating={ false }
|
||||
installableExtensions={ freeExtensions }
|
||||
setInstallExtensionOptions={ jest.fn() }
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { useMemo } from '@wordpress/element';
|
||||
import { Spinner } from '@woocommerce/components';
|
||||
import { Spinner } from '@wordpress/components';
|
||||
import { ONBOARDING_STORE_NAME, SETTINGS_STORE_NAME } from '@woocommerce/data';
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
|
||||
Fix free features is still rendered when there is no recommendation
|
Loading…
Reference in New Issue