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:
Chi-Hsuan Huang 2022-07-21 12:57:46 +08:00 committed by GitHub
parent 9620704d42
commit d09d59186d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 148 additions and 139 deletions

View File

@ -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,6 +436,7 @@ class BusinessDetails extends Component {
<Form
initialValues={ this.state.savedValues.businessDetailsTab }
onSubmit={ ( values ) => {
if ( this.props.hasInstallableExtensions ) {
this.setState( {
savedValues: {
...this.state.savedValues,
@ -437,6 +444,11 @@ class BusinessDetails extends Component {
},
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,
} );
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 );

View File

@ -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,37 +233,11 @@ export const createInstallExtensionOptions = (
}, prevInstallExtensionOptions );
};
export const SelectiveExtensionsBundle = ( {
isInstallingActivating,
onSubmit,
export const getInstallableExtensions = ( {
freeExtensionBundleByCategory,
country,
productTypes,
industry,
setInstallExtensionOptions,
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 &&
@ -282,7 +255,16 @@ export const SelectiveExtensionsBundle = ( {
}
return ALLOWED_PLUGIN_CATEGORIES.includes( extensionBundle.key );
} );
}, [ freeExtensionBundleByCategory, productTypes, country ] );
};
export const SelectiveExtensionsBundle = ( {
isInstallingActivating,
onSubmit,
setInstallExtensionOptions,
installableExtensions,
installExtensionOptions = { install_extensions: true },
} ) => {
const [ showExtensions, setShowExtensions ] = useState( false );
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' ) }

View File

@ -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() }
/>
);

View File

@ -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';
/**

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Fix free features is still rendered when there is no recommendation