Try add payment method selector to onboarding store (https://github.com/woocommerce/woocommerce-admin/pull/6921)

* Added payment method to onboarding data store

* Add reducer test

* Address feedback on reducer naming convention, isResolving, enabledMethods

* Move out types to live in its own island

* Add a comment to remind ourselves for utilizing payments data store for enabled payment gateways

* Update changelog
This commit is contained in:
Ilyas Foo 2021-05-07 18:06:35 +08:00 committed by GitHub
parent f515ed5b6e
commit de9cfb210d
11 changed files with 187 additions and 32 deletions

View File

@ -2,12 +2,11 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import { useDispatch, useSelect } from '@wordpress/data';
import { getHistory, getNewPath } from '@woocommerce/navigation';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
import { OPTIONS_STORE_NAME, ONBOARDING_STORE_NAME } from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { useEffect, useMemo, useState } from '@wordpress/element';
import { useMemo, useState } from '@wordpress/element';
/**
* Internal dependencies
@ -19,27 +18,22 @@ import { sift } from '../../../../utils';
export const RemotePayments = ( { query } ) => {
const { updateOptions } = useDispatch( OPTIONS_STORE_NAME );
const { getOption } = useSelect( ( select ) => {
const {
getOption,
getPaymentMethodRecommendations,
isResolving,
} = useSelect( ( select ) => {
return {
getOption: select( OPTIONS_STORE_NAME ).getOption,
getPaymentMethodRecommendations: select( ONBOARDING_STORE_NAME )
.getPaymentMethodRecommendations,
isResolving: select( ONBOARDING_STORE_NAME ).isResolving(
'getPaymentMethodRecommendations'
),
};
} );
const [ methods, setMethods ] = useState( [] );
const [ isFetching, setIsFetching ] = useState( true );
useEffect( () => {
apiFetch( {
path: '/wc-admin/onboarding/payments',
} )
.then( ( results ) => {
setMethods( results );
setIsFetching( false );
} )
.catch( () => {
setIsFetching( false );
} );
}, [] );
const methods = getPaymentMethodRecommendations();
const recommendedMethod = useMemo( () => {
const method = methods.find(
@ -73,6 +67,29 @@ export const RemotePayments = ( { query } ) => {
} );
};
const getInitiallyEnabledMethods = () =>
methods.reduce( ( acc, method ) => {
acc[ method.key ] = method.isEnabled;
return acc;
}, {} );
// TODO: Ideally when payments data store is merged https://github.com/woocommerce/woocommerce-admin/pull/6918
// we can utilize it for keeping track of enabled payment methods and optimistically update that
// store when enabling methods.
const [ enabledMethods, setEnabledMethods ] = useState(
getInitiallyEnabledMethods()
);
// Keeps enabledMethods up to date with methods fetched from API.
useMemo(
() =>
setEnabledMethods( {
...getInitiallyEnabledMethods(),
...enabledMethods,
} ),
[ methods ]
);
const markConfigured = async ( methodKey, queryParams = {} ) => {
const method = methods.find( ( option ) => option.key === methodKey );
@ -103,7 +120,7 @@ export const RemotePayments = ( { query } ) => {
};
const currentMethod = useMemo( () => {
if ( ! query.method || isFetching ) {
if ( ! query.method || isResolving ) {
return null;
}
@ -114,14 +131,7 @@ export const RemotePayments = ( { query } ) => {
}
return method;
}, [ isFetching, query ] );
const [ enabledMethods, setEnabledMethods ] = useState(
methods.reduce( ( acc, method ) => {
acc[ method.key ] = method.isEnabled;
return acc;
}, {} )
);
}, [ isResolving, query, methods ] );
if ( currentMethod ) {
return (

View File

@ -3,6 +3,7 @@ const TYPES = {
SET_IS_REQUESTING: 'SET_IS_REQUESTING',
SET_PROFILE_ITEMS: 'SET_PROFILE_ITEMS',
SET_TASKS_STATUS: 'SET_TASKS_STATUS',
GET_PAYMENT_METHODS_SUCCESS: 'GET_PAYMENT_METHODS_SUCCESS',
};
export default TYPES;

View File

@ -40,6 +40,13 @@ export function setTasksStatus( tasksStatus ) {
};
}
export function setPaymentMethods( paymentMethods ) {
return {
type: TYPES.GET_PAYMENT_METHODS_SUCCESS,
paymentMethods,
};
}
export function* updateProfileItems( items ) {
yield setIsRequesting( 'updateProfileItems', true );

View File

@ -20,13 +20,23 @@ export const defaultState = {
theme: null,
wccom_connected: null,
},
paymentMethods: [],
requesting: {},
tasksStatus: {},
};
const onboarding = (
state = defaultState,
{ type, profileItems, replace, error, isRequesting, selector, tasksStatus }
{
type,
profileItems,
paymentMethods,
replace,
error,
isRequesting,
selector,
tasksStatus,
}
) => {
switch ( type ) {
case TYPES.SET_PROFILE_ITEMS:
@ -57,6 +67,11 @@ const onboarding = (
[ selector ]: isRequesting,
},
};
case TYPES.GET_PAYMENT_METHODS_SUCCESS:
return {
...state,
paymentMethods,
};
default:
return state;
}

View File

@ -7,7 +7,12 @@ import { apiFetch } from '@wordpress/data-controls';
* Internal dependencies
*/
import { WC_ADMIN_NAMESPACE } from '../constants';
import { setProfileItems, setError, setTasksStatus } from './actions';
import {
setProfileItems,
setError,
setTasksStatus,
setPaymentMethods,
} from './actions';
export function* getProfileItems() {
try {
@ -34,3 +39,16 @@ export function* getTasksStatus() {
yield setError( 'getTasksStatus', error );
}
}
export function* getPaymentMethodRecommendations() {
try {
const results = yield apiFetch( {
path: WC_ADMIN_NAMESPACE + '/onboarding/payments',
method: 'GET',
} );
yield setPaymentMethods( results );
} catch ( error ) {
yield setError( 'getPaymentMethodRecommendations', error );
}
}

View File

@ -1,7 +1,7 @@
/**
* Internal dependencies
*/
import { WPDataSelectors } from '../types';
import { WPDataSelectors, RuleProcessor } from '../types';
export const getProfileItems = (
state: OnboardingState
@ -15,6 +15,12 @@ export const getTasksStatus = (
return state.tasksStatus || {};
};
export const getPaymentMethodRecommendations = (
state: OnboardingState
): PaymentMethodsState[] => {
return state.paymentMethods || [];
};
export const getOnboardingError = (
state: OnboardingState,
selector: string
@ -33,6 +39,9 @@ export const isOnboardingRequesting = (
export type OnboardingSelectors = {
getProfileItems: () => ReturnType< typeof getProfileItems >;
getTasksStatus: () => ReturnType< typeof getTasksStatus >;
getPaymentMethodRecommendations: () => ReturnType<
typeof getPaymentMethodRecommendations
>;
getOnboardingError: () => ReturnType< typeof getOnboardingError >;
isOnboardingRequesting: () => ReturnType< typeof isOnboardingRequesting >;
} & WPDataSelectors;
@ -40,6 +49,7 @@ export type OnboardingSelectors = {
export type OnboardingState = {
profileItems: ProfileItemsState;
tasksStatus: TasksStatusState;
paymentMethods: PaymentMethodsState[];
// TODO clarify what the error record's type is
errors: Record< string, unknown >;
requesting: Record< string, boolean >;
@ -112,3 +122,31 @@ export type ProfileItemsState = {
theme: string | null;
wccom_connected: boolean | null;
};
export type FieldLocale = {
locale: string;
label: string;
};
export type MethodFields = {
name: string;
option?: string;
label?: string;
locales?: FieldLocale[];
type?: string;
value?: string;
};
export type PaymentMethodsState = {
locale: string;
title: string;
content: string;
key: string;
image: string;
is_visible: boolean | RuleProcessor[];
plugins: string[];
is_configured: boolean | RuleProcessor[];
fields: MethodFields[];
api_details_url: string;
manage_url: string;
};

View File

@ -47,6 +47,24 @@ describe( 'plugins reducer', () => {
expect( state.profileItems.propertyName ).toBe( 'value' );
} );
it( 'should handle GET_PAYMENT_METHODS_SUCCESS', () => {
const state = reducer(
{
paymentMethods: [ { previousItem: 'value' } ],
},
{
type: TYPES.GET_PAYMENT_METHODS_SUCCESS,
paymentMethods: [ { newItem: 'changed' } ],
}
);
expect( state.paymentMethods[ 0 ] ).not.toHaveProperty(
'previousItem'
);
expect( state.paymentMethods[ 0 ] ).toHaveProperty( 'newItem' );
expect( state.paymentMethods[ 0 ].newItem ).toBe( 'changed' );
} );
it( 'should handle SET_ERROR', () => {
const state = reducer( defaultState, {
type: TYPES.SET_ERROR,

View File

@ -0,0 +1,2 @@
export * from './wp-data';
export * from './rule-processor';

View File

@ -0,0 +1,45 @@
export type RuleProcessor = {
type: RuleType;
value?: string | number | boolean;
default?: string | number | boolean;
index?: string;
operation?: RuleOperation;
status?: string;
operand?: RuleProcessor;
operands?: RuleProcessor[] | RuleProcessor[][];
option_name?: string;
plugin?: string;
plugins?: string[];
publish_after?: string;
};
export type RuleType =
| 'plugins_activated'
| 'publish_after_time'
| 'publish_before_time'
| 'not'
| 'or'
| 'fail'
| 'pass'
| 'plugin_version'
| 'stored_state'
| 'order_count'
| 'wcadmin_active_for'
| 'product_count'
| 'onboarding_profile'
| 'is_ecommerce'
| 'base_location_country'
| 'base_location_state'
| 'note_status'
| 'option'
| 'wca_updated';
export type RuleOperation =
| '='
| '<'
| '<='
| '>'
| '>='
| '!='
| 'contains'
| '!contains';

View File

@ -90,6 +90,8 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt
- Dev: Fixed storybook build script #6875
- Dev: Removed allowed keys list for adding woocommerce_meta data. #6889 🎉 @xristos3490
- Dev: Delete all products when running product import tests, unskip previously skipped test. #6905
- Dev: Add payment method selector to onboarding store #6921
- Dev: Add disabled prop to SelectControl #6902
- Dev: Do a git clean before the core release. #6945
- Enhancement: Add recommended payment methods in payment settings. #6760
- Enhancement: Add expand/collapse to extendable task list. #6910
@ -133,7 +135,6 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt
- Update: UI updates to Payment Task screen #6766
- Update: Adding setup required icon for non-configured payment methods #6811
- Update: Task list component with new Experimental Task list. #6849
- Dev: Add disabled prop to SelectControl #6902
- Update: Redirect to WC Home after setting up a payment method #6891
- Dev: Fix a bug where trying to load an asset registry causes a crash. #6951