Skip to content

Commit

Permalink
Merge branch 'develop' into fix/fallback-to-cc-when-hiding-pm
Browse files Browse the repository at this point in the history
  • Loading branch information
timur27 committed Jun 5, 2024
2 parents ae2e5ce + 82b2515 commit 7bc5a95
Show file tree
Hide file tree
Showing 23 changed files with 374 additions and 85 deletions.
4 changes: 4 additions & 0 deletions changelog/add-1527-survey-modal-on-deactivation
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Add a feedback survey modal upon deactivation.
5 changes: 5 additions & 0 deletions changelog/add-deposit-currency-param-for-payment-widget
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: add
Comment: Behind feature flag. Adds few changes to support displaying multiple currencies for Payment Activity Widget.


5 changes: 5 additions & 0 deletions changelog/fix-klarna-e2e-tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: Fix Klarna E2E Tests.


4 changes: 4 additions & 0 deletions changelog/fix-specific-field-checkout-failures-e2e-tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: fix

Fix input-specific credit card errors.
14 changes: 7 additions & 7 deletions client/checkout/blocks/payment-processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const PaymentProcessor = ( {
} ) => {
const stripe = useStripe();
const elements = useElements();
const isPaymentInformationCompleteRef = useRef( false );
const hasLoadErrorRef = useRef( false );

const paymentMethodsConfig = getUPEConfig( 'paymentMethodsConfig' );
const isTestMode = getUPEConfig( 'testMode' );
Expand Down Expand Up @@ -140,11 +140,11 @@ const PaymentProcessor = ( {
return;
}

if ( ! isPaymentInformationCompleteRef.current ) {
if ( hasLoadErrorRef.current ) {
return {
type: 'error',
message: __(
'Your payment information is incomplete.',
'Invalid or missing payment details. Please ensure the provided payment method is correctly entered.',
'woocommerce-payments'
),
};
Expand Down Expand Up @@ -237,8 +237,9 @@ const PaymentProcessor = ( {
shouldSavePayment
);

const setPaymentInformationCompletionStatus = ( event ) => {
isPaymentInformationCompleteRef.current = event.complete;
const setHasLoadError = ( event ) => {
hasLoadErrorRef.current = true;
onLoadError( event );
};

return (
Expand All @@ -256,8 +257,7 @@ const PaymentProcessor = ( {
shouldSavePayment,
paymentMethodsConfig
) }
onLoadError={ onLoadError }
onChange={ setPaymentInformationCompletionStatus }
onLoadError={ setHasLoadError }
className="wcpay-payment-element"
/>
</>
Expand Down
24 changes: 12 additions & 12 deletions client/checkout/blocks/test/payment-processor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,12 @@ jest.mock( '@stripe/react-stripe-js', () => ( {
useStripe: jest.fn(),
} ) );

const MockPaymentElement = ( { onChange } ) => {
useEffect( () => {
onChange( { complete: true } );
}, [ onChange ] );

return null;
};

describe( 'PaymentProcessor', () => {
let mockApi;
let mockCreatePaymentMethod;
beforeEach( () => {
global.wcpay_upe_config = { paymentMethodsConfig: {} };
PaymentElement.mockImplementation( MockPaymentElement );
PaymentElement.mockImplementation( () => null );
mockCreatePaymentMethod = jest
.fn()
.mockResolvedValue( { paymentMethod: {} } );
Expand Down Expand Up @@ -97,8 +89,14 @@ describe( 'PaymentProcessor', () => {
).not.toBeInTheDocument();
} );

it( 'should return an error when the payment information is incomplete', async () => {
PaymentElement.mockImplementation( () => null );
it( 'should return an error if the payment method could not be loaded', async () => {
PaymentElement.mockImplementation( ( { onLoadError } ) => {
useEffect( () => {
onLoadError();
}, [ onLoadError ] );

return null;
} );
let onPaymentSetupCallback;
render(
<PaymentProcessor
Expand All @@ -113,12 +111,14 @@ describe( 'PaymentProcessor', () => {
fingerprint=""
shouldSavePayment={ false }
upeMethods={ { card: 'woocommerce_payments' } }
onLoadError={ jest.fn() }
/>
);

expect( await onPaymentSetupCallback() ).toEqual( {
type: 'error',
message: 'Your payment information is incomplete.',
message:
'Invalid or missing payment details. Please ensure the provided payment method is correctly entered.',
} );
expect( mockCreatePaymentMethod ).not.toHaveBeenCalled();
} );
Expand Down
19 changes: 8 additions & 11 deletions client/checkout/classic/payment-processing.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ for ( const paymentMethodType in getUPEConfig( 'paymentMethodsConfig' ) ) {
gatewayUPEComponents[ paymentMethodType ] = {
elements: null,
upeElement: null,
isPaymentInformationComplete: false,
hasLoadError: false,
};
}

Expand Down Expand Up @@ -406,11 +406,9 @@ export async function mountStripePaymentElement( api, domElement ) {
gatewayUPEComponents[ paymentMethodType ].upeElement ||
( await createStripePaymentElement( api, paymentMethodType ) );
upeElement.mount( domElement );
upeElement.on( 'change', ( e ) => {
gatewayUPEComponents[ paymentMethodType ].isPaymentInformationComplete =
e.complete;
} );
upeElement.on( 'loaderror', ( e ) => {
// setting the flag to true to prevent the form from being submitted.
gatewayUPEComponents[ paymentMethodType ].hasLoadError = true;
// unset any styling to ensure the WC error message wrapper can take more width.
domElement.style.padding = '0';
// creating a new element to be added to the DOM, so that the message can be displayed.
Expand Down Expand Up @@ -524,15 +522,14 @@ export const processPayment = (
try {
await blockUI( $form );

const {
elements,
isPaymentInformationComplete,
} = gatewayUPEComponents[ paymentMethodType ];
const { elements, hasLoadError } = gatewayUPEComponents[
paymentMethodType
];

if ( ! isPaymentInformationComplete ) {
if ( hasLoadError ) {
throw new Error(
__(
'Your payment information is incomplete.',
'Invalid or missing payment details. Please ensure the provided payment method is correctly entered.',
'woocommerce-payments'
)
);
Expand Down
14 changes: 0 additions & 14 deletions client/checkout/classic/test/payment-processing.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,6 @@ const mockCreateFunction = jest.fn( () => ( {
eventHandlersFromElementsCreate[ event ].push( handler );
},
} ) );
const callAllCreateHandlersWith = ( event, ...args ) => {
eventHandlersFromElementsCreate[ event ]?.forEach( ( handler ) => {
handler.apply( null, args );
} );
};
const markAllPaymentElementsAsComplete = () => {
callAllCreateHandlersWith( 'change', { complete: true } );
};

const mockSubmit = jest.fn( () => ( {
then: jest.fn(),
Expand Down Expand Up @@ -396,7 +388,6 @@ describe( 'Payment processing', () => {
mockDomElement.dataset.paymentMethodType = 'card';

await mountStripePaymentElement( apiMock, mockDomElement );
markAllPaymentElementsAsComplete();

const mockJqueryForm = {
submit: jest.fn(),
Expand Down Expand Up @@ -443,7 +434,6 @@ describe( 'Payment processing', () => {
mockDomElement.dataset.paymentMethodType = 'card';

await mountStripePaymentElement( apiMock, mockDomElement );
markAllPaymentElementsAsComplete();

const checkoutForm = {
submit: jest.fn(),
Expand Down Expand Up @@ -487,7 +477,6 @@ describe( 'Payment processing', () => {
mockDomElement.dataset.paymentMethodType = 'card';

await mountStripePaymentElement( apiMock, mockDomElement );
markAllPaymentElementsAsComplete();

const checkoutForm = {
submit: jest.fn(),
Expand Down Expand Up @@ -527,7 +516,6 @@ describe( 'Payment processing', () => {
mockDomElement.dataset.paymentMethodType = 'card';

await mountStripePaymentElement( apiMock, mockDomElement );
markAllPaymentElementsAsComplete();

const checkoutForm = {
submit: jest.fn(),
Expand Down Expand Up @@ -564,7 +552,6 @@ describe( 'Payment processing', () => {
mockDomElement.dataset.paymentMethodType = 'card';

await mountStripePaymentElement( apiMock, mockDomElement );
markAllPaymentElementsAsComplete();

const checkoutForm = {
submit: jest.fn(),
Expand Down Expand Up @@ -599,7 +586,6 @@ describe( 'Payment processing', () => {
mockDomElement.dataset.paymentMethodType = 'card';

await mountStripePaymentElement( apiMock, mockDomElement );
markAllPaymentElementsAsComplete();

const addPaymentMethodForm = {
submit: jest.fn(),
Expand Down
3 changes: 3 additions & 0 deletions client/components/payment-activity/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ const PaymentActivity: React.FC = () => {
wcpaySettings.isOverviewSurveySubmitted ?? false;

const { paymentActivityData, isLoading } = usePaymentActivityData( {
// In future this will be bound to currency picker via useSelectedCurrency().
// Can hard-code other store settings to test.
currency: wcpaySettings.accountDefaultCurrency,
...getDateRange(),
timezone: moment( new Date() ).format( 'Z' ),
} );
Expand Down
1 change: 1 addition & 0 deletions client/data/payment-activity/test/hooks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe( 'usePaymentActivityData', () => {
);

const result = usePaymentActivityData( {
currency: 'jpy',
date_start: '2021-01-01',
date_end: '2021-01-31',
timezone: 'UTC',
Expand Down
3 changes: 2 additions & 1 deletion client/data/payment-activity/test/resolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { updatePaymentActivity } from '../actions';
import { getPaymentActivityData } from '../resolvers';

const query = {
currency: 'usd',
date_start: '2020-04-29T04:00:00',
date_end: '2020-04-29T03:59:59',
timezone: '+2:30',
Expand All @@ -21,7 +22,7 @@ const query = {
describe( 'getPaymentActivityData resolver', () => {
const successfulResponse: any = { amount: 3000 };
const expectedQueryString =
'date_start=2020-04-29T04%3A00%3A00&date_end=2020-04-29T03%3A59%3A59&timezone=%2B2%3A30';
'currency=usd&date_start=2020-04-29T04%3A00%3A00&date_end=2020-04-29T03%3A59%3A59&timezone=%2B2%3A30';
const errorResponse = new Error(
'Error retrieving payment activity data.'
);
Expand Down
9 changes: 9 additions & 0 deletions client/data/payment-activity/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,20 @@ export interface PaymentActivityAction {
data: PaymentActivityData;
}

/**
* Query parameters for fetching payment activity data for overview widget.
* Note that these are must match the query parameters for the REST API endpoint.
*
* @see Reporting_Service::get_payment_activity_totals() on WooPayments service.
* Musing: we could move all rest endpoint typedefs to a single place to make it clear that they are coupled to server code.
*/
export interface PaymentActivityQuery {
/** The date range start datetime used to calculate transaction data, e.g. 2024-04-29T16:19:29 */
date_start: string;
/** The date range end datetime used to calculate transaction data, e.g. 2024-04-29T16:19:29 */
date_end: string;
/** The timezone used to calculate the transaction data date range, e.g. 'UTC' */
timezone: string;
/** The currency to display */
currency: string;
}
50 changes: 50 additions & 0 deletions client/plugins-page/deactivation-survey/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* External dependencies
*/
import React, { useState } from 'react';
import { __ } from '@wordpress/i18n';
import { Modal } from '@wordpress/components';

/**
* Internal dependencies
*/
import './style.scss';
import Loadable from 'wcpay/components/loadable';
import WooPaymentsIcon from 'assets/images/woopayments.svg?asset';

const PluginDisableSurvey = ( { onRequestClose } ) => {
const [ isLoading, setIsLoading ] = useState( true );

return (
<Modal
title={
<img
src={ WooPaymentsIcon }
alt={ __( 'WooPayments Logo', 'woocommerce-payments' ) }
className="woopayments-disable-survey-logo"
/>
}
isDismissible={ true }
shouldCloseOnClickOutside={ false } // Should be false because of the iframe.
shouldCloseOnEsc={ true }
onRequestClose={ onRequestClose }
className="woopayments-disable-survey"
>
<Loadable isLoading={ isLoading }>
<iframe
title={ __(
'WooPayments Disable Survey',
'woocommerce-payments'
) }
src="https://automattic.survey.fm/woopayments-exit-feedback"
className="woopayments-disable-survey-iframe"
onLoad={ () => {
setIsLoading( false );
} }
/>
</Loadable>
</Modal>
);
};

export default PluginDisableSurvey;
38 changes: 38 additions & 0 deletions client/plugins-page/deactivation-survey/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.woopayments-disable-survey {
@media ( min-width: 960px ) {
max-height: calc( 100% - 120px );
}

.components-modal__content {
padding: 0;
overflow: hidden;
}

&-iframe {
width: 100%;
height: 100%;

@media ( min-width: 600px ) {
width: 600px;
height: 650px;
}
}

&-logo {
height: 40px;
}
}

/**
* There is a bug with the Modal component that when the close X is hovered or focused, a tooltip
* appears outside of the view of the modal causing scrollbars. This is a work around to hide the
* tooltip until the bug is fixed.
* TODO: remove rule ones bug is closed
* https://github.com/WordPress/gutenberg/issues/15434
*/
.components-modal__content
.components-modal__header
.components-button
.components-tooltip {
display: none;
}
Loading

0 comments on commit 7bc5a95

Please sign in to comment.