Fixing local WCPay payment task and refactoring for dumber components (https://github.com/woocommerce/woocommerce-admin/pull/7151)

This commit is contained in:
Joel Thiessen 2021-06-10 10:10:42 -07:00 committed by GitHub
parent 14fe529ebc
commit 01588f2168
12 changed files with 191 additions and 175 deletions

View File

@ -17,7 +17,7 @@ import { useMemo, useState } from '@wordpress/element';
* Internal dependencies
*/
import { PaymentMethodList } from './components/PaymentMethodList';
import { WCPayMethodCard } from './components/WCPayMethodCard';
import { WCPaySuggestion } from './components/WCPaySuggestion';
import { getCountryCode } from '../../../dashboard/utils';
import { getPaymentMethods } from './methods';
import { PaymentSetup } from './components/PaymentSetup';
@ -219,12 +219,21 @@ export const LocalPayments = ( { query } ) => {
const wcPayMethod =
wcPayIndex === -1
? null
: additionalCardMethods.splice( wcPayIndex, 1 );
: additionalCardMethods.splice( wcPayIndex, 1 )[ 0 ];
return (
<div className="woocommerce-task-payments">
{ !! wcPayMethod && (
<WCPayMethodCard method={ wcPayMethod[ 0 ] } />
<WCPaySuggestion
onSetupCallback={ wcPayMethod.onClick }
paymentGateway={ {
...wcPayMethod,
description: __(
'Try the new way to get paid. Securely accept credit and debit cards on your site. Manage transactions without leaving your WordPress dashboard. Only with WooCommerce Payments.',
'wc-admin'
),
} }
/>
) }
{ !! enabledCardMethods.length && (

View File

@ -18,31 +18,32 @@ import './List.scss';
export const Item = ( {
isRecommended,
markConfigured,
paymentGateways,
suggestion,
suggestionKeys,
paymentGateway,
gatewayIds,
} ) => {
const { image, content, id, plugins = [], title, loading } = suggestion;
const {
image,
content,
id,
plugins = [],
title,
loading,
enabled: isEnabled = false,
needsSetup = false,
requiredSettings,
settingsUrl: manageUrl,
} = paymentGateway;
const connectSlot = useSlot(
`woocommerce_payment_gateway_connect_${ id }`
);
const setupSlot = useSlot( `woocommerce_payment_gateway_setup_${ id }` );
const paymentGateway = paymentGateways[ id ] || {};
const {
enabled: isEnabled = false,
needs_setup: needsSetup = false,
required_settings_keys: requiredSettingsKeys = [],
settings_url: manageUrl,
} = paymentGateway;
const hasFills =
Boolean( connectSlot?.fills?.length ) ||
Boolean( setupSlot?.fills?.length );
const hasSetup = Boolean(
plugins.length || requiredSettingsKeys.length || hasFills
plugins.length || requiredSettings.length || hasFills
);
const showRecommendedRibbon = isRecommended && needsSetup;
@ -84,7 +85,7 @@ export const Item = ( {
markConfigured={ markConfigured }
onSetup={ () =>
recordEvent( 'tasklist_payment_setup', {
options: suggestionKeys,
options: gatewayIds,
selected: id,
} )
}

View File

@ -13,23 +13,23 @@ import './List.scss';
export const List = ( {
heading,
markConfigured,
paymentGateways,
recommendation,
suggestions,
paymentGateways,
} ) => {
return (
<Card>
<CardHeader as="h2">{ heading }</CardHeader>
{ Array.from( suggestions.values() ).map( ( suggestion ) => {
const { id } = suggestion;
{ paymentGateways.map( ( paymentGateway ) => {
const { id } = paymentGateway;
return (
<Item
key={ id }
isRecommended={ recommendation === id }
markConfigured={ markConfigured }
paymentGateways={ paymentGateways }
suggestion={ suggestion }
suggestionKeys={ suggestions.keys() }
paymentGateway={ paymentGateway }
gatewayIds={ paymentGateways.map(
( gateway ) => gateway.id
) }
/>
);
} ) }

View File

@ -21,23 +21,17 @@ export const Connect = ( {
} ) => {
const {
id,
connection_url: connectionUrl,
setup_help_text: setupHelpText,
required_settings_keys: settingKeys,
settings,
settings_url: settingsUrl,
connectionUrl,
setupHelpText,
settingsUrl,
title,
requiredSettings: fields,
} = paymentGateway;
const { createNotice } = useDispatch( 'core/notices' );
const { updatePaymentGateway } = useDispatch( PAYMENT_GATEWAYS_STORE_NAME );
const slot = useSlot( `woocommerce_payment_gateway_connect_${ id }` );
const hasFills = Boolean( slot?.fills?.length );
const fields = settingKeys
? settingKeys
.map( ( settingKey ) => settings[ settingKey ] )
.filter( Boolean )
: [];
const { isUpdating } = useSelect( ( select ) => {
const { isPaymentGatewayUpdating } = select(

View File

@ -14,7 +14,7 @@ import { Plugins, Stepper } from '@woocommerce/components';
import { WooPaymentGatewaySetup } from '@woocommerce/onboarding';
import { recordEvent } from '@woocommerce/tracks';
import { useEffect, useState, useMemo, useCallback } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { useSelect, useDispatch } from '@wordpress/data';
import { useSlot } from '@woocommerce/experimental';
/**
@ -26,10 +26,16 @@ import './Setup.scss';
export const Setup = ( {
markConfigured,
suggestion,
paymentGateway,
recordConnectStartEvent,
} ) => {
const { id, plugins = [], title } = suggestion;
const {
id,
plugins = [],
title,
postInstallScripts,
installed: gatewayInstalled,
} = paymentGateway;
const slot = useSlot( `woocommerce_payment_gateway_setup_${ id }` );
const hasFills = Boolean( slot?.fills?.length );
const [ isPluginLoaded, setIsPluginLoaded ] = useState( false );
@ -40,16 +46,17 @@ export const Setup = ( {
} );
}, [] );
const { invalidateResolutionForStoreSelector } = useDispatch(
PAYMENT_GATEWAYS_STORE_NAME
);
const {
isOptionUpdating,
isPaymentGatewayResolving,
needsPluginInstall,
paymentGateway,
} = useSelect( ( select ) => {
const { isOptionsUpdating } = select( OPTIONS_STORE_NAME );
const { getPaymentGateway, isResolving } = select(
PAYMENT_GATEWAYS_STORE_NAME
);
const { isResolving } = select( PAYMENT_GATEWAYS_STORE_NAME );
const activePlugins = select( PLUGINS_STORE_NAME ).getActivePlugins();
const pluginsToInstall = plugins.filter(
( m ) => ! activePlugins.includes( m )
@ -57,22 +64,16 @@ export const Setup = ( {
return {
isOptionUpdating: isOptionsUpdating(),
isPaymentGatewayResolving: isResolving( 'getPaymentGateway', [
id,
] ),
paymentGateway: ! pluginsToInstall.length
? getPaymentGateway( id )
: null,
isPaymentGatewayResolving: isResolving( 'getPaymentGateways' ),
needsPluginInstall: !! pluginsToInstall.length,
};
} );
useEffect( () => {
if ( ! paymentGateway ) {
if ( needsPluginInstall ) {
return;
}
const { post_install_scripts: postInstallScripts } = paymentGateway;
if ( postInstallScripts && postInstallScripts.length ) {
const scriptPromises = postInstallScripts.map( ( script ) =>
enqueueScript( script )
@ -84,7 +85,7 @@ export const Setup = ( {
}
setIsPluginLoaded( true );
}, [ paymentGateway ] );
}, [ postInstallScripts, needsPluginInstall ] );
const pluginNamesString = plugins
.map( ( pluginSlug ) => pluginNames[ pluginSlug ] )
@ -103,6 +104,9 @@ export const Setup = ( {
<Plugins
onComplete={ ( installedPlugins, response ) => {
createNoticesFromResponse( response );
invalidateResolutionForStoreSelector(
'getPaymentGateways'
);
recordEvent(
'tasklist_payment_install_method',
{
@ -122,22 +126,25 @@ export const Setup = ( {
: null;
}, [ needsPluginInstall ] );
const connectStep = {
key: 'connect',
label: sprintf(
__( 'Connect your %(title)s account', 'woocommerce-admin' ),
{
title,
}
),
content: paymentGateway ? (
<Connect
markConfigured={ markConfigured }
paymentGateway={ paymentGateway }
recordConnectStartEvent={ recordConnectStartEvent }
/>
) : null,
};
const connectStep = useMemo(
() => ( {
key: 'connect',
label: sprintf(
__( 'Connect your %(title)s account', 'woocommerce-admin' ),
{
title,
}
),
content: gatewayInstalled ? (
<Connect
markConfigured={ markConfigured }
paymentGateway={ paymentGateway }
recordConnectStartEvent={ recordConnectStartEvent }
/>
) : null,
} ),
[ gatewayInstalled ]
);
const stepperPending =
! installStep?.isComplete ||
@ -155,7 +162,7 @@ export const Setup = ( {
{ ...props }
/>
),
[ stepperPending, installStep ]
[ stepperPending, installStep, connectStep ]
);
return (

View File

@ -17,7 +17,7 @@ import { useMemo, useCallback } from '@wordpress/element';
*/
import { List, Placeholder as ListPlaceholder } from './components/List';
import { Setup, Placeholder as SetupPlaceholder } from './components/Setup';
import { WCPayMethodCard } from '../components/WCPayMethodCard';
import { WCPaySuggestion } from '../components/WCPaySuggestion';
import './plugins/Bacs';
const RECOMMENDED_GATEWAY_IDS = [
@ -28,63 +28,65 @@ const RECOMMENDED_GATEWAY_IDS = [
export const PaymentGatewaySuggestions = ( { query } ) => {
const { updatePaymentGateway } = useDispatch( PAYMENT_GATEWAYS_STORE_NAME );
const {
additionalSuggestions,
enabledSuggestions,
getPaymentGateway,
paymentGateways,
suggestions,
isResolving,
wcPaySuggestion,
} = useSelect( ( select ) => {
const gateways = select( PAYMENT_GATEWAYS_STORE_NAME )
.getPaymentGateways()
.reduce( ( map, gateway ) => {
map[ gateway.id ] = gateway;
return map;
}, {} );
const enabled = new Map();
const additional = new Map();
let wcPay = null;
const mappedSuggestions = select( ONBOARDING_STORE_NAME )
.getPaymentGatewaySuggestions()
.reduce( ( map, suggestion ) => {
const { id } = suggestion;
map.set( id, suggestion );
// WCPay is handled separately when not installed and configured
if (
id === 'woocommerce_payments' &&
! ( gateways[ id ] && ! gateways[ id ].needs_setup )
) {
wcPay = suggestion;
const { getPaymentGateway, paymentGateways, isResolving } = useSelect(
( select ) => {
const installedPaymentGateways = select(
PAYMENT_GATEWAYS_STORE_NAME
)
.getPaymentGateways()
.reduce( ( map, gateway ) => {
map[ gateway.id ] = gateway;
return map;
}
}, {} );
if ( gateways[ id ] && gateways[ id ].enabled ) {
enabled.set( id, suggestion );
} else {
additional.set( id, suggestion );
}
const mappedSuggestions = select( ONBOARDING_STORE_NAME )
.getPaymentGatewaySuggestions()
.reduce( ( map, suggestion ) => {
const { id } = suggestion;
const installedGateway = installedPaymentGateways[
suggestion.id
]
? installedPaymentGateways[ id ]
: {};
return map;
}, new Map() );
const enrichedSuggestion = {
installed: !! installedPaymentGateways[ id ],
postInstallScripts:
installedGateway.post_install_scripts,
enabled: installedGateway.enabled,
needsSetup: installedGateway.needs_setup,
settingsUrl: installedGateway.settings_url,
connectionUrl: installedGateway.connection_url,
setupHelpText: installedGateway.setup_help_text,
title: installedGateway.title,
requiredSettings: installedGateway.required_settings_keys
? installedGateway.required_settings_keys
.map(
( settingKey ) =>
installedGateway.settings[
settingKey
]
)
.filter( Boolean )
: [],
...suggestion,
};
return {
additionalSuggestions: additional,
enabledSuggestions: enabled,
getPaymentGateway: select( PAYMENT_GATEWAYS_STORE_NAME )
.getPaymentGateway,
getOption: select( OPTIONS_STORE_NAME ).getOption,
isResolving: select( ONBOARDING_STORE_NAME ).isResolving(
'getPaymentGatewaySuggestions'
),
paymentGateways: gateways,
suggestions: mappedSuggestions,
wcPaySuggestion: wcPay,
};
} );
map.set( id, enrichedSuggestion );
return map;
}, new Map() );
return {
getPaymentGateway: select( PAYMENT_GATEWAYS_STORE_NAME )
.getPaymentGateway,
getOption: select( OPTIONS_STORE_NAME ).getOption,
isResolving: select( ONBOARDING_STORE_NAME ).isResolving(
'getPaymentGatewaySuggestions'
),
paymentGateways: mappedSuggestions,
};
}
);
const enablePaymentGateway = ( id ) => {
if ( ! id ) {
@ -104,7 +106,7 @@ export const PaymentGatewaySuggestions = ( { query } ) => {
const markConfigured = useCallback(
async ( id, queryParams = {} ) => {
if ( ! suggestions.get( id ) ) {
if ( ! paymentGateways.get( id ) ) {
throw `Payment gateway ${ id } not found in available gateways list`;
}
@ -118,47 +120,72 @@ export const PaymentGatewaySuggestions = ( { query } ) => {
getNewPath( { ...queryParams, task: 'payments' }, '/', {} )
);
},
[ paymentGateways, suggestions ]
[ paymentGateways ]
);
const recordConnectStartEvent = useCallback( ( gatewayKey ) => {
const recordConnectStartEvent = useCallback( ( gatewayId ) => {
recordEvent( 'tasklist_payment_connect_start', {
payment_method: gatewayKey,
payment_method: gatewayId,
} );
}, [] );
const recommendation = useMemo( () => {
for ( const id in RECOMMENDED_GATEWAY_IDS ) {
const gateway = suggestions.get( id );
const gateway = paymentGateways.get( id );
if ( gateway ) {
return gateway;
}
}
return null;
}, [ suggestions ] );
}, [ paymentGateways ] );
const currentSuggestion = useMemo( () => {
if ( ! query.id || isResolving || ! suggestions.size ) {
const currentGateway = useMemo( () => {
if ( ! query.id || isResolving || ! paymentGateways.size ) {
return null;
}
const gateway = suggestions.get( query.id );
const gateway = paymentGateways.get( query.id );
if ( ! gateway ) {
throw `Current gateway ${ query.id } not found in available gateways list`;
}
return gateway;
}, [ isResolving, query, suggestions ] );
}, [ isResolving, query, paymentGateways ] );
if ( query.id && ! currentSuggestion ) {
const [ wcPayGateway, enabledGateways, additionalGateways ] = useMemo(
() =>
Array.from( paymentGateways.values() ).reduce(
( all, gateway ) => {
const [ wcPay, enabled, additional ] = all;
// WCPay is handled separately when not installed and configured
if (
gateway.id === 'woocommerce_payments' &&
! ( gateway.installed && ! gateway.needsSetup )
) {
wcPay.push( gateway );
} else if ( gateway.enabled ) {
enabled.push( gateway );
} else {
additional.push( gateway );
}
return all;
},
[ [], [], [] ]
),
[ paymentGateways ]
);
if ( query.id && ! currentGateway ) {
return <SetupPlaceholder />;
}
if ( currentSuggestion ) {
if ( currentGateway ) {
return (
<Setup
suggestion={ currentSuggestion }
paymentGateway={ currentGateway }
markConfigured={ markConfigured }
recordConnectStartEvent={ recordConnectStartEvent }
/>
@ -167,33 +194,31 @@ export const PaymentGatewaySuggestions = ( { query } ) => {
return (
<div className="woocommerce-task-payments">
{ ! suggestions.size && <ListPlaceholder /> }
{ ! paymentGateways.size && <ListPlaceholder /> }
{ !! wcPaySuggestion && (
<WCPayMethodCard suggestion={ wcPaySuggestion } />
{ !! wcPayGateway.length && (
<WCPaySuggestion paymentGateway={ wcPayGateway[ 0 ] } />
) }
{ !! enabledSuggestions.size && (
{ !! enabledGateways.length && (
<List
heading={ __(
'Enabled payment gateways',
'woocommerce-admin'
) }
paymentGateways={ paymentGateways }
recommendation={ recommendation }
suggestions={ enabledSuggestions }
paymentGateways={ enabledGateways }
/>
) }
{ !! additionalSuggestions.size && (
{ !! additionalGateways.length && (
<List
heading={ __(
'Additional payment gateways',
'woocommerce-admin'
) }
paymentGateways={ paymentGateways }
recommendation={ recommendation }
suggestions={ additionalSuggestions }
paymentGateways={ additionalGateways }
markConfigured={ markConfigured }
/>
) }

View File

@ -4,10 +4,8 @@
import { __ } from '@wordpress/i18n';
import interpolateComponents from 'interpolate-components';
import { Link, Pill } from '@woocommerce/components';
import { PAYMENT_GATEWAYS_STORE_NAME } from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { Text } from '@woocommerce/experimental';
import { useSelect } from '@wordpress/data';
import {
WCPayCard,
WCPayCardHeader,
@ -39,14 +37,11 @@ const TosPrompt = () =>
},
} );
export const WCPayMethodCard = ( { suggestion } ) => {
const { description, id } = suggestion;
const paymentGateway = useSelect( ( select ) => {
return (
select( PAYMENT_GATEWAYS_STORE_NAME ).getPaymentGateway( id ) || {}
);
} );
const { enabled: isEnabled, needs_setup: needsSetup } = paymentGateway;
export const WCPaySuggestion = ( {
paymentGateway,
onSetupCallback = null,
} ) => {
const { description, id, needsSetup, isEnabled } = paymentGateway;
return (
<WCPayCard>
@ -64,6 +59,7 @@ export const WCPayMethodCard = ( { suggestion } ) => {
recordEvent( 'tasklist_payment_learn_more' );
} }
/>
<WCPayCardFooter>
<>
<Text>
@ -79,6 +75,7 @@ export const WCPayMethodCard = ( { suggestion } ) => {
'Get started',
'woocommerce-admin'
) }
onSetupCallback={ onSetupCallback }
/>
</>
</WCPayCardFooter>

View File

@ -578,7 +578,7 @@ export function getPaymentMethods( {
),
before: (
<img
src={ wcAdminAssetUrl + 'onboarding/wcpay.png' }
src={ wcAdminAssetUrl + 'onboarding/wcpay.svg' }
alt={ __( 'WooCommerce Payments', 'woocommerce-admin' ) }
/>
),

View File

@ -22,6 +22,7 @@ This is the fill component. You must provide the `id` prop to identify the slot
| `defaultSubmit` | Function | The default submit handler that is provided to the <Form> component |
| `defaultFields` | Array | An array of the field configuration objects provided by the API |
| `markConfigured` | Function | A helper function that will mark your gateway as configured |
| `paymentGateway` | Object | An object describing all of the relevant data pertaining to this payment gateway |
### WooPaymentGatewayConnect.Slot (slot)

View File

@ -21,6 +21,7 @@ This is the fill component. You must provide the `id` prop to identify the slot
| `defaultStepper` | Component | The default instance of the <Stepper> component. Any provided props will override the given defaults |
| `defaultInstallStep` | Object | The object that describes the default step configuration for installation of the gateway |
| `defaultConnectStep` | Object | The object that describes the default step configuration for configuration of the gateway |
| `paymentGateway` | Object | An object describing all of the relevant data pertaining to this payment gateway |
### WooPaymentGatewaySetup.Slot (slot)

View File

@ -75,6 +75,7 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt
== Unreleased ==
- Fix: WCPay not working in local payments task #7151
- Dev: Add Jetpack Backup admin note #6738
- Add: Adding WCPay payment configuration defaults. #7097
- Fix: Transformer casing is incorrect and creates an error on case-sensitive systems #7104

View File

@ -106,26 +106,6 @@ class Init {
$data = (object) array_merge( (array) $locale, (array) $spec );
unset( $data->locales );
$data->fields = array();
// Loop over and localize fields.
foreach ( $spec->fields as $field ) {
if ( ! isset( $field->locales ) ) {
continue;
}
$locale = SpecRunner::get_locale( $field->locales );
if ( ! $locale ) {
continue;
}
$field_data = (object) array_merge( (array) $field, (array) $locale );
unset( $field_data->locale );
unset( $field_data->locales );
$data->fields[] = $field_data;
}
$localized_specs[] = $data;
}