From b011b37dd8c7bb16ef7c75d12ad681f2d3585135 Mon Sep 17 00:00:00 2001 From: Taha Paksu <3295+tpaksu@users.noreply.github.com> Date: Tue, 31 Oct 2023 22:34:41 +0300 Subject: [PATCH] Disable activation of LPM's requiring additional currencies when MC is disabled (#7211) Co-authored-by: Jesse Pearson --- ...-7154-onboarding-lpms-while-mc-is-disabled | 5 + .../add-payment-methods-task.js | 37 +++++- .../test/add-payment-methods-task.test.js | 60 ++++++++- .../currency-information-for-methods/index.js | 27 ++++ .../test/index.test.js | 27 +++- .../payment-method-checkbox.tsx | 6 + client/payment-methods/index.js | 115 ++++++++++-------- client/payment-methods/test/index.js | 53 +++++++- 8 files changed, 273 insertions(+), 57 deletions(-) create mode 100644 changelog/fix-7154-onboarding-lpms-while-mc-is-disabled diff --git a/changelog/fix-7154-onboarding-lpms-while-mc-is-disabled b/changelog/fix-7154-onboarding-lpms-while-mc-is-disabled new file mode 100644 index 00000000000..01b4963690d --- /dev/null +++ b/changelog/fix-7154-onboarding-lpms-while-mc-is-disabled @@ -0,0 +1,5 @@ +Significance: patch +Type: add +Comment: Added notice when a LPM is selected that requires additional currencies when MC is disabled + + diff --git a/client/additional-methods-setup/upe-preview-methods-selector/add-payment-methods-task.js b/client/additional-methods-setup/upe-preview-methods-selector/add-payment-methods-task.js index b619ae96044..02de82a620b 100644 --- a/client/additional-methods-setup/upe-preview-methods-selector/add-payment-methods-task.js +++ b/client/additional-methods-setup/upe-preview-methods-selector/add-payment-methods-task.js @@ -35,11 +35,14 @@ import PaymentMethodCheckboxes from '../../components/payment-methods-checkboxes import PaymentMethodCheckbox from '../../components/payment-methods-checkboxes/payment-method-checkbox'; import { LoadableBlock } from '../../components/loadable'; import LoadableSettingsSection from '../../settings/loadable-settings-section'; -import CurrencyInformationForMethods from '../../components/currency-information-for-methods'; +import CurrencyInformationForMethods, { + BuildMissingCurrenciesTooltipMessage, +} from '../../components/currency-information-for-methods'; import { upeCapabilityStatuses, upeMethods } from '../constants'; import paymentMethodsMap from '../../payment-methods-map'; import ConfirmPaymentMethodActivationModal from 'wcpay/payment-methods/activation-modal'; import './add-payment-methods-task.scss'; +import PAYMENT_METHOD_IDS from 'wcpay/payment-methods/constants'; const usePaymentMethodsCheckboxState = () => { // For UPE, the card payment method is required and always active. @@ -211,9 +214,27 @@ const AddPaymentMethodsTask = () => { }; const prepareUpePaymentMethods = ( upeMethodIds ) => { - return upeMethodIds.map( - ( key ) => - availablePaymentMethods.includes( key ) && ( + return upeMethodIds.map( ( key ) => { + const { label, currencies } = paymentMethodsMap[ key ]; + + if ( availablePaymentMethods.includes( key ) ) { + let isSetupRequired = false; + let setupTooltip = ''; + + if ( + ! wcpaySettings.isMultiCurrencyEnabled && + key !== PAYMENT_METHOD_IDS.CARD + ) { + const currency = wcpaySettings.storeCurrency; + if ( currencies.indexOf( currency ) < 0 ) { + isSetupRequired = true; + setupTooltip = BuildMissingCurrenciesTooltipMessage( + label, + currencies + ); + } + } + return ( { upeCapabilityStatuses.INACTIVE !== getStatusAndRequirements( key ).status } + setupTooltip={ setupTooltip } + isSetupRequired={ isSetupRequired } status={ getStatusAndRequirements( key ).status } locked={ isPoInProgress } onChange={ ( name, status ) => { @@ -228,8 +251,10 @@ const AddPaymentMethodsTask = () => { } } name={ key } /> - ) - ); + ); + } + return ''; + } ); }; const availableBuyNowPayLaterUpeMethods = upeMethods.filter( diff --git a/client/additional-methods-setup/upe-preview-methods-selector/test/add-payment-methods-task.test.js b/client/additional-methods-setup/upe-preview-methods-selector/test/add-payment-methods-task.test.js index 98443119428..2408c60edde 100644 --- a/client/additional-methods-setup/upe-preview-methods-selector/test/add-payment-methods-task.test.js +++ b/client/additional-methods-setup/upe-preview-methods-selector/test/add-payment-methods-task.test.js @@ -2,7 +2,13 @@ * External dependencies */ import React from 'react'; -import { act, render, screen, waitFor } from '@testing-library/react'; +import { + act, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react'; import userEvent from '@testing-library/user-event'; /** @@ -120,6 +126,8 @@ describe( 'AddPaymentMethodsTask', () => { useManualCapture.mockReturnValue( [ false, jest.fn() ] ); useAccountDomesticCurrency.mockReturnValue( 'usd' ); global.wcpaySettings = { + isMultiCurrencyEnabled: true, + storeCurrency: 'USD', accountEmail: 'admin@example.com', }; } ); @@ -443,4 +451,54 @@ describe( 'AddPaymentMethodsTask', () => { jest.useRealTimers(); } ); + + it( "should render the setup tooltip correctly when multi currency is disabled and store currency doesn't support the LPM", () => { + global.wcpaySettings.isMultiCurrencyEnabled = false; + global.wcpaySettings.storeCurrency = 'USD'; + const setCompletedMock = jest.fn(); + useEnabledPaymentMethodIds.mockReturnValue( [ [ 'card' ], jest.fn() ] ); + useGetAvailablePaymentMethodIds.mockReturnValue( [ 'bancontact' ] ); + useGetPaymentMethodStatuses.mockReturnValue( { + bancontact_payments: { + status: upeCapabilityStatuses.ACTIVE, + requirements: [], + }, + } ); + + const { container } = render( + + + + + + ); + + expect( + screen.queryByLabelText( 'Bancontact' ) + ).not.toBeInTheDocument(); + + const svgIcon = container.querySelectorAll( + '.gridicons-notice-outline' + )[ 0 ]; + + expect( svgIcon ).toBeInTheDocument(); + + jest.useFakeTimers(); + + act( () => { + fireEvent.mouseOver( svgIcon, { + view: window, + bubbles: true, + cancelable: true, + } ); + jest.runAllTimers(); + } ); + + expect( + screen.queryByText( /Bancontact requires the EUR currency\./ ) + ).toBeInTheDocument(); + jest.useRealTimers(); + } ); } ); diff --git a/client/components/currency-information-for-methods/index.js b/client/components/currency-information-for-methods/index.js index 38ca0b133cf..adc347c9c16 100644 --- a/client/components/currency-information-for-methods/index.js +++ b/client/components/currency-information-for-methods/index.js @@ -29,6 +29,33 @@ const ListToCommaSeparatedSentencePartConverter = ( items ) => { ); }; +export const BuildMissingCurrenciesTooltipMessage = ( + paymentMethodLabel, + missingCurrencies +) => { + return sprintf( + __( + '%s requires the %s %s. In order to enable ' + + 'the payment method, you must add %s to your store.', + 'woocommerce-payments' + ), + paymentMethodLabel, + ListToCommaSeparatedSentencePartConverter( missingCurrencies ), + _n( + 'currency', + 'currencies', + missingCurrencies.length, + 'woocommerce-payments' + ), + _n( + 'this currency', + 'these currencies', + missingCurrencies.length, + 'woocommerce-payments' + ) + ); +}; + const CurrencyInformationForMethods = ( { selectedMethods } ) => { const { isLoading: isLoadingCurrencyInformation, diff --git a/client/components/currency-information-for-methods/test/index.test.js b/client/components/currency-information-for-methods/test/index.test.js index 0ee012bab21..65934540869 100644 --- a/client/components/currency-information-for-methods/test/index.test.js +++ b/client/components/currency-information-for-methods/test/index.test.js @@ -8,7 +8,9 @@ import { render, screen } from '@testing-library/react'; * Internal dependencies */ import { useCurrencies, useEnabledCurrencies } from '../../../data'; -import CurrencyInformationForMethods from '..'; +import CurrencyInformationForMethods, { + BuildMissingCurrenciesTooltipMessage, +} from '..'; import WCPaySettingsContext from '../../../settings/wcpay-settings-context'; jest.mock( '../../../data', () => ( { @@ -185,4 +187,27 @@ describe( 'CurrencyInformationForMethods', () => { ) ).toBeInTheDocument(); } ); + + it( 'returns correct string with the given LPM label and currency list', () => { + const output = BuildMissingCurrenciesTooltipMessage( 'x', [ 'EUR' ] ); + expect( output ).toBe( + 'x requires the EUR currency. In order to enable the payment method, you must add this currency to your store.' + ); + const outputMultiple = BuildMissingCurrenciesTooltipMessage( 'x', [ + 'EUR', + 'PLN', + ] ); + expect( outputMultiple ).toBe( + 'x requires the EUR and PLN currencies. In order to enable the payment method, you must add these currencies to your store.' + ); + const outputMany = BuildMissingCurrenciesTooltipMessage( 'x', [ + 'EUR', + 'PLN', + 'TRY', + ] ); + expect( outputMany ).toBe( + 'x requires the EUR, PLN, and TRY currencies. ' + + 'In order to enable the payment method, you must add these currencies to your store.' + ); + } ); } ); diff --git a/client/components/payment-methods-checkboxes/payment-method-checkbox.tsx b/client/components/payment-methods-checkboxes/payment-method-checkbox.tsx index 6804795c7a4..40bf3451464 100644 --- a/client/components/payment-methods-checkboxes/payment-method-checkbox.tsx +++ b/client/components/payment-methods-checkboxes/payment-method-checkbox.tsx @@ -65,6 +65,8 @@ type PaymentMethodCheckboxProps = { status: string; required: boolean; locked: boolean; + isSetupRequired?: boolean; + setupTooltip?: string; }; const PaymentMethodCheckbox: React.FC< PaymentMethodCheckboxProps > = ( { @@ -75,6 +77,8 @@ const PaymentMethodCheckbox: React.FC< PaymentMethodCheckboxProps > = ( { status, required, locked, + isSetupRequired, + setupTooltip, } ) => { const { accountFees, @@ -124,6 +128,8 @@ const PaymentMethodCheckbox: React.FC< PaymentMethodCheckboxProps > = ( { delayMsOnCheck={ 1500 } delayMsOnUncheck={ 0 } hideLabel={ true } + isSetupRequired={ isSetupRequired } + setupTooltip={ setupTooltip } isAllowingManualCapture={ paymentMethod.allows_manual_capture } />
diff --git a/client/payment-methods/index.js b/client/payment-methods/index.js index d1b903065f6..816402c854f 100644 --- a/client/payment-methods/index.js +++ b/client/payment-methods/index.js @@ -39,6 +39,7 @@ import ConfirmPaymentMethodDeleteModal from './delete-modal'; import { getPaymentMethodDescription } from 'wcpay/utils/payment-methods'; import CapabilityRequestNotice from './capability-request'; import InlineNotice from 'wcpay/components/inline-notice'; +import { BuildMissingCurrenciesTooltipMessage } from 'wcpay/components/currency-information-for-methods'; const PaymentMethodsDropdownMenu = ( { setOpenModal } ) => { const { isUpeEnabled, upeType } = useContext( WcPayUpeContext ); @@ -258,56 +259,74 @@ const PaymentMethods = () => { allows_manual_capture: isAllowingManualCapture, setup_required: isSetupRequired, setup_tooltip: setupTooltip, - } ) => ( - { + if ( + ! wcpaySettings.isMultiCurrencyEnabled && + id !== PAYMENT_METHOD_IDS.CARD + ) { + const currency = + wcpaySettings.storeCurrency; + if ( currencies.indexOf( currency ) < 0 ) { + isSetupRequired = true; + setupTooltip = BuildMissingCurrenciesTooltipMessage( + label, + currencies + ); + } + } + return ( + { - handleUncheckClick( id ); - } } - onCheckClick={ () => { - handleCheckClick( id ); - } } - isPoEnabled={ - wcpaySettings?.progressiveOnboarding - ?.isEnabled - } - isPoComplete={ - wcpaySettings?.progressiveOnboarding - ?.isComplete - } - /> - ) + } + isSetupRequired={ isSetupRequired } + setupTooltip={ setupTooltip } + isAllowingManualCapture={ + isAllowingManualCapture + } + onUncheckClick={ () => { + handleUncheckClick( id ); + } } + onCheckClick={ () => { + handleCheckClick( id ); + } } + isPoEnabled={ + wcpaySettings?.progressiveOnboarding + ?.isEnabled + } + isPoComplete={ + wcpaySettings?.progressiveOnboarding + ?.isComplete + } + /> + ); + } ) } diff --git a/client/payment-methods/test/index.js b/client/payment-methods/test/index.js index 3ad638bfc2a..7df9aee2b9f 100644 --- a/client/payment-methods/test/index.js +++ b/client/payment-methods/test/index.js @@ -4,7 +4,7 @@ * External dependencies */ import React from 'react'; -import { act, render, screen } from '@testing-library/react'; +import { act, fireEvent, render, screen } from '@testing-library/react'; import user from '@testing-library/user-event'; import { select } from '@wordpress/data'; @@ -82,6 +82,8 @@ describe( 'PaymentMethods', () => { } ); useManualCapture.mockReturnValue( [ false, jest.fn() ] ); global.wcpaySettings = { + isMultiCurrencyEnabled: true, + storeCurrency: 'USD', accountEmail: 'admin@example.com', capabilityRequestNotices: {}, }; @@ -474,4 +476,53 @@ describe( 'PaymentMethods', () => { jest.useRealTimers(); } ); + + it( "should render the setup tooltip correctly when multi currency is disabled and store currency doesn't support the LPM", () => { + global.wcpaySettings.isMultiCurrencyEnabled = false; + global.wcpaySettings.storeCurrency = 'TRY'; + useEnabledPaymentMethodIds.mockReturnValue( [ + [ 'bancontact' ], + jest.fn(), + ] ); + useGetAvailablePaymentMethodIds.mockReturnValue( [ 'bancontact' ] ); + useGetPaymentMethodStatuses.mockReturnValue( { + bancontact_payments: { + status: upeCapabilityStatuses.ACTIVE, + requirements: [], + }, + } ); + + const { container } = render( + + + + ); + + // Checkbox shouldn't be rendered. + expect( + screen.queryByLabelText( 'Bancontact' ) + ).not.toBeInTheDocument(); + + const svgIcon = container.querySelectorAll( + '.gridicons-notice-outline' + )[ 1 ]; + + expect( svgIcon ).toBeInTheDocument(); + + jest.useFakeTimers(); + + act( () => { + fireEvent.mouseOver( svgIcon, { + view: window, + bubbles: true, + cancelable: true, + } ); + jest.runAllTimers(); + } ); + + expect( + screen.queryByText( /Bancontact requires the EUR currency\./ ) + ).toBeInTheDocument(); + jest.useRealTimers(); + } ); } );