From cbeab483aaa1dec835fb46cfc8eff629ae085ac0 Mon Sep 17 00:00:00 2001
From: Mike Moore
Date: Thu, 19 Dec 2024 15:02:11 -0500
Subject: [PATCH 01/13] Add payment method logos to blocks card label
---
.../checkout/blocks/payment-method-label.js | 48 ++++--
.../blocks/payment-methods-logos/index.ts | 1 +
.../payment-methods-logos/logo-popover.tsx | 139 +++++++++++++++++
.../payment-methods-logos.tsx | 143 ++++++++++++++++++
.../blocks/payment-methods-logos/style.scss | 47 ++++++
.../blocks/test/payment-method-logos.test.tsx | 115 ++++++++++++++
6 files changed, 481 insertions(+), 12 deletions(-)
create mode 100644 client/checkout/blocks/payment-methods-logos/index.ts
create mode 100644 client/checkout/blocks/payment-methods-logos/logo-popover.tsx
create mode 100644 client/checkout/blocks/payment-methods-logos/payment-methods-logos.tsx
create mode 100644 client/checkout/blocks/payment-methods-logos/style.scss
create mode 100644 client/checkout/blocks/test/payment-method-logos.test.tsx
diff --git a/client/checkout/blocks/payment-method-label.js b/client/checkout/blocks/payment-method-label.js
index 752a9b830db..38a0ccf9d88 100644
--- a/client/checkout/blocks/payment-method-label.js
+++ b/client/checkout/blocks/payment-method-label.js
@@ -5,6 +5,11 @@ import {
Elements,
PaymentMethodMessagingElement,
} from '@stripe/react-stripe-js';
+import { PaymentMethodsLogos } from './payment-methods-logos';
+import Visa from 'assets/images/payment-method-icons/visa.svg?asset';
+import Mastercard from 'assets/images/payment-method-icons/mastercard.svg?asset';
+import Amex from 'assets/images/payment-method-icons/amex.svg?asset';
+import Discover from 'assets/images/payment-method-icons/discover.svg?asset';
import { normalizeCurrencyToMinorUnit } from '../utils';
import { useStripeForUPE } from 'wcpay/hooks/use-stripe-async';
import { getUPEConfig } from 'wcpay/utils/checkout';
@@ -13,6 +18,32 @@ import './style.scss';
import { useEffect, useState } from '@wordpress/element';
import { getAppearance } from 'wcpay/checkout/upe-styles';
+const paymentMethods = [
+ {
+ name: 'visa',
+ component: Visa,
+ },
+ {
+ name: 'mastercard',
+ component: Mastercard,
+ },
+ {
+ name: 'amex',
+ component: Amex,
+ },
+ {
+ name: 'discover',
+ component: Discover,
+ },
+ // TODO: Missing Diners Club
+ // TODO: What other card payment methods should be here?
+];
+const breakpointConfigs = [
+ { breakpoint: 550, maxElements: 2 },
+ { breakpoint: 833, maxElements: 4 },
+ { breakpoint: 960, maxElements: 2 },
+];
+
const bnplMethods = [ 'affirm', 'afterpay_clearpay', 'klarna' ];
const PaymentMethodMessageWrapper = ( {
upeName,
@@ -47,16 +78,12 @@ const PaymentMethodMessageWrapper = ( {
);
};
-export default ( { api, title, countries, iconLight, iconDark, upeName } ) => {
+export default ( { api, title, countries, upeName } ) => {
const cartData = wp.data.select( 'wc/store/cart' ).getCartData();
const isTestMode = getUPEConfig( 'testMode' );
const [ appearance, setAppearance ] = useState(
getUPEConfig( 'wcBlocksUPEAppearance' )
);
- const [ upeAppearanceTheme, setUpeAppearanceTheme ] = useState(
- getUPEConfig( 'wcBlocksUPEAppearanceTheme' )
- );
-
// Stripe expects the amount to be sent as the minor unit of 2 digits.
const amount = parseInt(
normalizeCurrencyToMinorUnit(
@@ -81,7 +108,6 @@ export default ( { api, title, countries, iconLight, iconDark, upeName } ) => {
'blocks_checkout'
);
setAppearance( upeAppearance );
- setUpeAppearanceTheme( upeAppearance.theme );
}
if ( ! appearance ) {
@@ -104,12 +130,10 @@ export default ( { api, title, countries, iconLight, iconDark, upeName } ) => {
{ __( 'Test Mode', 'woocommerce-payments' ) }
) }
-
void;
+ dataTestId?: string;
+}
+
+export const LogoPopover: React.FC< LogoPopoverProps > = ( {
+ id,
+ className,
+ children,
+ anchor,
+ open,
+ onClose,
+ dataTestId,
+} ) => {
+ const popoverRef = useRef< HTMLDivElement >( null );
+ const [ isPositioned, setIsPositioned ] = useState( false );
+
+ const updatePosition = useCallback( () => {
+ const popover = popoverRef.current;
+ if ( ! popover || ! anchor ) {
+ return;
+ }
+
+ // Get the most up-to-date anchor rect
+ const anchorRect = anchor.getBoundingClientRect();
+
+ // Temporarily make the popover visible to get correct dimensions
+ popover.style.visibility = 'hidden';
+ popover.style.display = 'block';
+ const popoverRect = popover.getBoundingClientRect();
+ popover.style.display = '';
+ popover.style.visibility = '';
+
+ const offset = 7;
+ const left = anchorRect.left;
+ // Position the popover above the anchor
+ const top = anchorRect.top - popoverRect.height - offset;
+
+ popover.style.position = 'fixed';
+ popover.style.width = `${ anchorRect.width }px`;
+ popover.style.left = `${ left }px`;
+ popover.style.top = `${ top }px`;
+
+ // Adjust position if popover goes off-screen
+ if ( top < 0 ) {
+ // If there's not enough space above, position it below the anchor
+ popover.style.top = `${ anchorRect.bottom + offset }px`;
+ }
+
+ setIsPositioned( true );
+ }, [ anchor ] );
+
+ useLayoutEffect( () => {
+ if ( open && anchor ) {
+ // Use requestAnimationFrame to ensure the DOM has updated before positioning
+ requestAnimationFrame( updatePosition );
+ }
+ }, [ open, anchor, updatePosition ] );
+
+ useEffect( () => {
+ if ( open && anchor ) {
+ const observer = new MutationObserver( updatePosition );
+ observer.observe( anchor, {
+ attributes: true,
+ childList: true,
+ subtree: true,
+ } );
+
+ window.addEventListener( 'resize', updatePosition );
+ window.addEventListener( 'scroll', updatePosition );
+
+ const handleOutsideClick = ( event: MouseEvent ) => {
+ if (
+ popoverRef.current &&
+ ! popoverRef.current.contains( event.target as Node ) &&
+ ! anchor.contains( event.target as Node )
+ ) {
+ onClose?.();
+ }
+ };
+
+ const handleEscapeKey = ( event: KeyboardEvent ) => {
+ if ( event.key === 'Escape' ) {
+ onClose?.();
+ }
+ };
+
+ document.addEventListener( 'mousedown', handleOutsideClick );
+ document.addEventListener( 'keydown', handleEscapeKey );
+
+ return () => {
+ observer.disconnect();
+ window.removeEventListener( 'resize', updatePosition );
+ window.removeEventListener( 'scroll', updatePosition );
+ document.removeEventListener( 'mousedown', handleOutsideClick );
+ document.removeEventListener( 'keydown', handleEscapeKey );
+ };
+ }
+ }, [ open, anchor, updatePosition, onClose ] );
+
+ if ( ! open ) {
+ return null;
+ }
+
+ return (
+
+ { children }
+
+ );
+};
diff --git a/client/checkout/blocks/payment-methods-logos/payment-methods-logos.tsx b/client/checkout/blocks/payment-methods-logos/payment-methods-logos.tsx
new file mode 100644
index 00000000000..0fb54df3755
--- /dev/null
+++ b/client/checkout/blocks/payment-methods-logos/payment-methods-logos.tsx
@@ -0,0 +1,143 @@
+/**
+ * External dependencies
+ */
+import React, { useState, useEffect, useCallback, useRef } from 'react';
+/**
+ * Internal dependencies
+ */
+import { LogoPopover } from './logo-popover';
+import './style.scss';
+
+interface BreakpointConfig {
+ breakpoint: number;
+ maxElements: number;
+}
+
+interface PaymentMethodsLogosProps {
+ maxElements: number;
+ paymentMethods: { name: string; component: string }[];
+ breakpointConfigs?: BreakpointConfig[];
+}
+
+const breakpointConfigsDefault = [
+ { breakpoint: 480, maxElements: 5 },
+ { breakpoint: 768, maxElements: 7 },
+];
+const paymentMethodsDefault: never[] = [];
+export const PaymentMethodsLogos: React.FC< PaymentMethodsLogosProps > = ( {
+ maxElements = 10,
+ paymentMethods = paymentMethodsDefault,
+ breakpointConfigs = breakpointConfigsDefault,
+} ) => {
+ const [ maxShownElements, setMaxShownElements ] = useState( maxElements );
+ const [
+ popoverAnchor,
+ setPopoverAnchor,
+ ] = useState< HTMLDivElement | null >( null );
+ const [ popoverOpen, setPopoverOpen ] = useState( false );
+ const [ shouldHavePopover, setShouldHavePopover ] = useState( false );
+
+ const togglePopover = () => setPopoverOpen( ! popoverOpen );
+
+ const anchorRef = useCallback( ( node: HTMLDivElement | null ) => {
+ if ( node !== null ) {
+ setPopoverAnchor( node );
+ }
+ }, [] );
+
+ const buttonRef = useRef< HTMLDivElement | null >( null );
+
+ const handlePopoverClose = useCallback( () => {
+ setPopoverOpen( false );
+ buttonRef.current?.focus();
+ }, [] );
+
+ useEffect( () => {
+ const updateMaxElements = () => {
+ const sortedConfigs = [ ...breakpointConfigs ].sort(
+ ( a, b ) => a.breakpoint - b.breakpoint
+ );
+ const config = sortedConfigs.find(
+ ( cfg ) => window.innerWidth <= cfg.breakpoint
+ );
+
+ setMaxShownElements( config ? config.maxElements : maxElements );
+ };
+
+ updateMaxElements();
+ window.addEventListener( 'resize', updateMaxElements );
+
+ return () => window.removeEventListener( 'resize', updateMaxElements );
+ }, [ breakpointConfigs, maxElements ] );
+
+ useEffect( () => {
+ if ( popoverAnchor ) {
+ buttonRef.current = popoverAnchor;
+ }
+ }, [ popoverAnchor ] );
+
+ useEffect( () => {
+ setShouldHavePopover( paymentMethods.length > maxShownElements );
+ }, [ maxShownElements, paymentMethods.length ] );
+
+ return (
+ <>
+
+
{
+ if ( e.key === 'Enter' || e.key === ' ' ) {
+ e.preventDefault();
+ togglePopover();
+ }
+ },
+ role: 'button',
+ tabIndex: 0,
+ 'aria-expanded': popoverOpen,
+ 'aria-controls': 'payment-methods-popover',
+ } ) }
+ data-testid="payment-methods-logos"
+ >
+ { paymentMethods
+ .slice( 0, maxShownElements )
+ .map( ( pm ) => (
+
![{]({)
+ ) ) }
+ { shouldHavePopover && (
+
+ + { paymentMethods.length - maxShownElements }
+
+ ) }
+
+
+ { shouldHavePopover && popoverOpen && (
+
+ { paymentMethods.slice( maxShownElements ).map( ( pm ) => (
+
+ ) ) }
+
+ ) }
+ >
+ );
+};
diff --git a/client/checkout/blocks/payment-methods-logos/style.scss b/client/checkout/blocks/payment-methods-logos/style.scss
new file mode 100644
index 00000000000..b5f08d2ef8d
--- /dev/null
+++ b/client/checkout/blocks/payment-methods-logos/style.scss
@@ -0,0 +1,47 @@
+.payment-methods--logos {
+ > div {
+ display: flex;
+ align-items: center;
+
+ img {
+ width: 37px;
+ height: 24px;
+ margin-right: 4px;
+ border: 1px solid $gray-300;
+ border-radius: 3px;
+ }
+ }
+
+ &-count {
+ width: 38px;
+ height: 24px;
+ background-color: rgba( $gray-700, 0.1 );
+ color: $gray-900;
+ text-align: center;
+ line-height: 24px;
+ border-radius: 3px;
+ font-size: 11px;
+ font-weight: 600;
+ }
+}
+
+.logo-popover {
+ background-color: #fff;
+ border: 1px solid $gray-300;
+ border-radius: 3px;
+ padding: 10px;
+ box-sizing: border-box;
+ box-shadow: 0 0 10px 0 rgba( 0, 0, 0, 0.1 );
+ display: grid;
+ grid-template-columns: repeat( auto-fit, minmax( 38px, 1fr ) );
+ gap: 10px;
+ justify-items: center;
+ align-items: center;
+ cursor: pointer;
+
+ > img {
+ box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.1 );
+ max-width: 100%;
+ height: auto;
+ }
+}
diff --git a/client/checkout/blocks/test/payment-method-logos.test.tsx b/client/checkout/blocks/test/payment-method-logos.test.tsx
new file mode 100644
index 00000000000..2e946884312
--- /dev/null
+++ b/client/checkout/blocks/test/payment-method-logos.test.tsx
@@ -0,0 +1,115 @@
+/**
+ * External dependencies
+ */
+import React from 'react';
+import { render, screen, fireEvent } from '@testing-library/react';
+import { describe, test, expect } from '@jest/globals';
+/**
+ * Internal dependencies
+ */
+import { PaymentMethodsLogos } from '../payment-methods-logos';
+
+const mockPaymentMethods = [
+ { name: 'Visa', component: 'visa.png' },
+ { name: 'MasterCard', component: 'mastercard.png' },
+ { name: 'PayPal', component: 'paypal.png' },
+ { name: 'Amex', component: 'amex.png' },
+ { name: 'Discover', component: 'discover.png' },
+];
+
+describe( 'PaymentMethodsLogos', () => {
+ test( 'renders without crashing', () => {
+ render(
+
+ );
+ const logoContainer = screen.getByTestId( 'payment-methods-logos' );
+ expect( logoContainer ).toBeTruthy();
+ } );
+
+ test( 'displays correct number of logos based on maxElements', () => {
+ render(
+
+ );
+ const logos = screen.queryAllByRole( 'img' );
+ expect( logos ).toHaveLength( 3 );
+ } );
+
+ test( 'shows popover indicator when there are more payment methods than maxElements', () => {
+ render(
+
+ );
+ const popoverIndicator = screen.queryByText(
+ `+ ${ mockPaymentMethods.length - 3 }`
+ );
+ expect( popoverIndicator ).toBeTruthy();
+ } );
+
+ test( 'opens popover on button click', async () => {
+ render(
+
+ );
+ const button = screen.getByTestId( 'payment-methods-logos' );
+
+ fireEvent.click( button );
+
+ const popover = await screen.findByTestId( 'payment-methods-popover' );
+ expect( popover ).toBeTruthy();
+ } );
+
+ test( 'handles keyboard navigation', async () => {
+ render(
+
+ );
+ const button = screen.getByTestId( 'payment-methods-logos' );
+
+ fireEvent.keyDown( button, { key: 'Enter' } );
+
+ const popover = await screen.findByTestId( 'payment-methods-popover' );
+ expect( popover ).toBeTruthy();
+ } );
+
+ test( 'does not show popover indicator when there are fewer payment methods than maxElements', () => {
+ render(
+
+ );
+ const popoverIndicator = screen.queryByText( /^\+\s*\d+$/ );
+ expect( popoverIndicator ).toBeNull();
+
+ const logos = screen.getAllByRole( 'img' );
+ expect( logos ).toHaveLength( mockPaymentMethods.length );
+ } );
+
+ test( 'does not show popover when there are fewer payment methods than maxElements', async () => {
+ render(
+
+ );
+
+ const button = screen.getByTestId( 'payment-methods-logos' );
+
+ fireEvent.click( button );
+
+ const popover = screen.queryByTestId( 'payment-methods-popover' );
+ expect( popover ).toBeNull();
+ } );
+} );
From cf0720177a0e4bf4def705e4a6f53f48f5c4e158 Mon Sep 17 00:00:00 2001
From: Mike Moore
Date: Fri, 20 Dec 2024 16:30:32 -0500
Subject: [PATCH 02/13] Changelog
---
changelog/add-9826-payment-methods-logos-component | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 changelog/add-9826-payment-methods-logos-component
diff --git a/changelog/add-9826-payment-methods-logos-component b/changelog/add-9826-payment-methods-logos-component
new file mode 100644
index 00000000000..51f66c6e070
--- /dev/null
+++ b/changelog/add-9826-payment-methods-logos-component
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add payment method logos to checkout block card label.
From a71bb0b234d43ab1067f092d126b75d60aca3da4 Mon Sep 17 00:00:00 2001
From: Mike Moore
Date: Wed, 8 Jan 2025 16:31:45 -0500
Subject: [PATCH 03/13] Re-add non-card logos
---
.../checkout/blocks/payment-method-label.js | 25 ++++++++++++++-----
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/client/checkout/blocks/payment-method-label.js b/client/checkout/blocks/payment-method-label.js
index 51df261b50b..6b5918de763 100644
--- a/client/checkout/blocks/payment-method-label.js
+++ b/client/checkout/blocks/payment-method-label.js
@@ -78,7 +78,7 @@ const PaymentMethodMessageWrapper = ( {
);
};
-export default ( { api, title, countries, upeName } ) => {
+export default ( { api, title, countries, iconLight, iconDark, upeName } ) => {
const cartData = wp.data.select( 'wc/store/cart' ).getCartData();
const isTestMode = getUPEConfig( 'testMode' );
const [ appearance, setAppearance ] = useState(
@@ -115,6 +115,7 @@ export default ( { api, title, countries, upeName } ) => {
'blocks_checkout'
);
setAppearance( upeAppearance );
+ setUpeAppearanceTheme( upeAppearance.theme );
}
if ( ! appearance ) {
@@ -137,11 +138,23 @@ export default ( { api, title, countries, upeName } ) => {
{ __( 'Test Mode', 'woocommerce-payments' ) }
) }
-
+ { upeName === 'card' ? (
+
+ ) : (
+
+ ) }
Date: Tue, 21 Jan 2025 08:56:01 -0500
Subject: [PATCH 04/13] Add card brand logos to classic checkout
---
client/checkout/classic/event-handlers.js | 205 ++++++++++++++++++++++
client/checkout/classic/style.scss | 7 +
2 files changed, 212 insertions(+)
diff --git a/client/checkout/classic/event-handlers.js b/client/checkout/classic/event-handlers.js
index 4ed1912250f..c866524f974 100644
--- a/client/checkout/classic/event-handlers.js
+++ b/client/checkout/classic/event-handlers.js
@@ -33,6 +33,10 @@ import { isPreviewing } from 'wcpay/checkout/preview';
import { recordUserEvent } from 'tracks';
import '../utils/copy-test-number';
import { SHORTCODE_BILLING_ADDRESS_FIELDS } from '../constants';
+import Visa from 'assets/images/payment-method-icons/visa.svg?asset';
+import Mastercard from 'assets/images/payment-method-icons/mastercard.svg?asset';
+import Amex from 'assets/images/payment-method-icons/amex.svg?asset';
+import Discover from 'assets/images/payment-method-icons/discover.svg?asset';
jQuery( function ( $ ) {
enqueueFraudScripts( getUPEConfig( 'fraudServices' ) );
@@ -73,6 +77,7 @@ jQuery( function ( $ ) {
$( document.body ).on( 'updated_checkout', () => {
maybeMountStripePaymentElement( 'shortcode_checkout' );
injectStripePMMEContainers();
+ injectPaymentMethodLogos();
} );
$checkoutForm.on( generateCheckoutEventNames(), function () {
@@ -239,6 +244,206 @@ jQuery( function ( $ ) {
}
}
+ async function injectPaymentMethodLogos() {
+ const cardLabel = document.querySelector(
+ 'label[for="payment_method_woocommerce_payments"]'
+ );
+ if ( ! cardLabel ) return;
+
+ const target = cardLabel.querySelector( 'img' );
+ if ( ! target ) return;
+
+ // Create container div
+ const logosContainer = document.createElement( 'div' );
+ logosContainer.className = 'payment-methods--logos';
+
+ // Create inner div for flex layout
+ const innerContainer = document.createElement( 'div' );
+ innerContainer.setAttribute( 'role', 'button' );
+ innerContainer.setAttribute( 'tabindex', '0' );
+ innerContainer.setAttribute( 'data-testid', 'payment-methods-logos' );
+
+ const paymentMethods = [
+ { name: 'visa', component: Visa },
+ { name: 'mastercard', component: Mastercard },
+ { name: 'amex', component: Amex },
+ { name: 'discover', component: Discover },
+ ];
+
+ function getMaxElements() {
+ return window.innerWidth <= 330 ? 2 : 4;
+ }
+
+ function shouldHavePopover() {
+ return paymentMethods.length > getMaxElements();
+ }
+
+ function createPopover( remainingMethods ) {
+ const popover = document.createElement( 'div' );
+ popover.className = 'logo-popover';
+ popover.setAttribute( 'role', 'dialog' );
+ popover.setAttribute(
+ 'aria-label',
+ 'Supported Credit Card Brands'
+ );
+
+ remainingMethods.forEach( ( pm ) => {
+ const img = document.createElement( 'img' );
+ img.src = pm.component;
+ img.alt = pm.name;
+ img.width = 38;
+ img.height = 24;
+ popover.appendChild( img );
+ } );
+
+ return popover;
+ }
+
+ function positionPopover( popover, anchor ) {
+ const label = anchor.closest( 'label' );
+ if ( ! label ) return;
+
+ const labelRect = label.getBoundingClientRect();
+ const anchorRect = anchor.getBoundingClientRect();
+
+ if ( ! popover.dataset.labelOffset ) {
+ popover.style.visibility = 'hidden';
+ popover.style.display = 'block';
+ const popoverRect = popover.getBoundingClientRect();
+ popover.style.display = '';
+ popover.style.visibility = '';
+
+ const offset = 7;
+ const initialTop = labelRect.top - popoverRect.height - offset;
+
+ // Store the offset from the label
+ popover.dataset.labelOffset = (
+ labelRect.top - initialTop
+ ).toString();
+ }
+
+ const labelOffset = parseFloat( popover.dataset.labelOffset );
+
+ popover.style.position = 'fixed';
+ popover.style.width = `${ anchorRect.width }px`;
+ popover.style.left = `${ anchorRect.left }px`; // Use anchor's left position
+ popover.style.top = `${ labelRect.top - labelOffset }px`;
+ popover.style.zIndex = '1000';
+ }
+
+ function updateLogos() {
+ innerContainer.innerHTML = ''; // Clear existing logos
+ const maxElements = getMaxElements();
+ const visibleMethods = paymentMethods.slice( 0, maxElements );
+ const remainingCount = paymentMethods.length - maxElements;
+
+ // Add visible logos
+ visibleMethods.forEach( ( pm ) => {
+ const brandImg = document.createElement( 'img' );
+ brandImg.src = pm.component;
+ brandImg.alt = pm.name;
+ brandImg.width = 38;
+ brandImg.height = 24;
+ innerContainer.appendChild( brandImg );
+ } );
+
+ // Add count indicator if we should have a popover
+ if ( shouldHavePopover() ) {
+ const countDiv = document.createElement( 'div' );
+ countDiv.className = 'payment-methods--logos-count';
+ countDiv.textContent = `+ ${ remainingCount }`;
+ innerContainer.appendChild( countDiv );
+ }
+
+ // Remove existing popover if we no longer need it
+ const existingPopover = cardLabel.querySelector( '.logo-popover' );
+ if ( existingPopover && ! shouldHavePopover() ) {
+ existingPopover.remove();
+ }
+ }
+
+ function setupPopover() {
+ const popover = createPopover(
+ paymentMethods.slice( getMaxElements() )
+ );
+ cardLabel.appendChild( popover );
+ positionPopover( popover, innerContainer );
+
+ const handleResize = () =>
+ positionPopover( popover, innerContainer );
+ window.addEventListener( 'resize', handleResize );
+ window.addEventListener( 'scroll', handleResize );
+
+ const handlers = {};
+
+ const cleanup = () => {
+ popover.remove();
+ window.removeEventListener( 'resize', handleResize );
+ window.removeEventListener( 'scroll', handleResize );
+ document.removeEventListener(
+ 'mousedown',
+ handlers.handleOutsideClick
+ );
+ document.removeEventListener(
+ 'keydown',
+ handlers.handleEscapeKey
+ );
+ };
+
+ handlers.handleOutsideClick = ( e ) => {
+ if (
+ ! popover.contains( e.target ) &&
+ ! innerContainer.contains( e.target )
+ ) {
+ cleanup();
+ }
+ };
+
+ handlers.handleEscapeKey = ( e ) => {
+ if ( e.key === 'Escape' ) {
+ cleanup();
+ }
+ };
+
+ document.addEventListener(
+ 'mousedown',
+ handlers.handleOutsideClick
+ );
+ document.addEventListener( 'keydown', handlers.handleEscapeKey );
+ }
+
+ function togglePopover() {
+ if ( ! shouldHavePopover() ) return;
+
+ const existingPopover = cardLabel.querySelector( '.logo-popover' );
+ if ( existingPopover ) {
+ existingPopover.remove();
+ return;
+ }
+
+ setupPopover();
+ }
+
+ // Click handler
+ innerContainer.addEventListener( 'click', togglePopover );
+
+ // Keyboard handler
+ innerContainer.addEventListener( 'keydown', ( e ) => {
+ if ( e.key === 'Enter' || e.key === ' ' ) {
+ e.preventDefault();
+ togglePopover();
+ }
+ } );
+
+ // Initial setup
+ logosContainer.appendChild( innerContainer );
+ target.replaceWith( logosContainer );
+ updateLogos();
+
+ // Update on window resize
+ window.addEventListener( 'resize', updateLogos );
+ }
+
function processPaymentIfNotUsingSavedMethod( $form ) {
const paymentMethodType = getSelectedUPEGatewayPaymentMethod();
if ( ! isUsingSavedPaymentMethod( paymentMethodType ) ) {
diff --git a/client/checkout/classic/style.scss b/client/checkout/classic/style.scss
index e1fd24e3bdc..2b937fb2dae 100644
--- a/client/checkout/classic/style.scss
+++ b/client/checkout/classic/style.scss
@@ -35,6 +35,13 @@
#payment .payment_methods {
li[class*='payment_method_woocommerce_payments'] label {
display: inline;
+ .payment-methods--logos {
+ float: right;
+
+ img:last-of-type {
+ margin-right: 0;
+ }
+ }
img {
float: right;
border: 0;
From 5d980b7162070145ac708c1adcc3c3e6ac0c44e8 Mon Sep 17 00:00:00 2001
From: Guilherme Pressutto
Date: Wed, 22 Jan 2025 14:11:44 -0300
Subject: [PATCH 05/13] Update Credit Card / Debit Card label to Cards (#9769)
---
changelog/update-cards-label | 4 ++++
client/checkout/blocks/style.scss | 10 ----------
includes/class-wc-payment-gateway-wcpay.php | 2 +-
includes/payment-methods/class-cc-payment-method.php | 2 +-
.../payment-methods/test-class-upe-payment-gateway.php | 2 +-
.../test-class-upe-split-payment-gateway.php | 2 +-
tests/unit/test-class-wc-payment-gateway-wcpay.php | 2 +-
tests/unit/test-class-wc-payments-checkout.php | 2 +-
8 files changed, 10 insertions(+), 16 deletions(-)
create mode 100644 changelog/update-cards-label
diff --git a/changelog/update-cards-label b/changelog/update-cards-label
new file mode 100644
index 00000000000..ce09cd5fc3a
--- /dev/null
+++ b/changelog/update-cards-label
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Update Credit Card / Debit Card label to Cards
diff --git a/client/checkout/blocks/style.scss b/client/checkout/blocks/style.scss
index 3e400b6bfff..369a1141d75 100644
--- a/client/checkout/blocks/style.scss
+++ b/client/checkout/blocks/style.scss
@@ -95,16 +95,6 @@ button.wcpay-stripelink-modal-trigger:hover {
display: none;
}
- @include breakpoint( '<480px' ) {
- grid-template-areas: 'label logos' 'badge badge';
- grid-template-columns: 1fr auto;
- align-items: start;
-
- .payment-methods--logos {
- justify-self: end;
- }
- }
-
&__pmme-container {
width: 100%;
pointer-events: none;
diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php
index d1be21241b9..45d80db04db 100644
--- a/includes/class-wc-payment-gateway-wcpay.php
+++ b/includes/class-wc-payment-gateway-wcpay.php
@@ -1582,7 +1582,7 @@ public function process_payment_for_order( $cart, $payment_information, $schedul
do_action( 'woocommerce_payments_changed_subscription_payment_method', $order, $payment_token );
}
- $order->set_payment_method_title( __( 'Credit / Debit Card', 'woocommerce-payments' ) );
+ $order->set_payment_method_title( __( 'Credit / Debit Cards', 'woocommerce-payments' ) );
$order->save();
return [
diff --git a/includes/payment-methods/class-cc-payment-method.php b/includes/payment-methods/class-cc-payment-method.php
index 58d7d733a77..9e7f3ba6860 100644
--- a/includes/payment-methods/class-cc-payment-method.php
+++ b/includes/payment-methods/class-cc-payment-method.php
@@ -39,7 +39,7 @@ public function __construct( $token_service ) {
*/
public function get_title( ?string $account_country = null, $payment_details = false ) {
if ( ! $payment_details ) {
- return __( 'Credit card / debit card', 'woocommerce-payments' );
+ return __( 'Cards', 'woocommerce-payments' );
}
$details = $payment_details[ $this->stripe_id ];
diff --git a/tests/unit/payment-methods/test-class-upe-payment-gateway.php b/tests/unit/payment-methods/test-class-upe-payment-gateway.php
index 000f5eade08..4df9604fa2e 100644
--- a/tests/unit/payment-methods/test-class-upe-payment-gateway.php
+++ b/tests/unit/payment-methods/test-class-upe-payment-gateway.php
@@ -698,7 +698,7 @@ public function test_payment_methods_show_correct_default_outputs() {
$afterpay_method = $this->mock_payment_methods['afterpay_clearpay'];
$this->assertEquals( 'card', $card_method->get_id() );
- $this->assertEquals( 'Credit card / debit card', $card_method->get_title( 'US' ) );
+ $this->assertEquals( 'Cards', $card_method->get_title( 'US' ) );
$this->assertEquals( 'Visa debit card', $card_method->get_title( 'US', $mock_visa_details ) );
$this->assertEquals( 'Mastercard credit card', $card_method->get_title( 'US', $mock_mastercard_details ) );
$this->assertTrue( $card_method->is_enabled_at_checkout( 'US' ) );
diff --git a/tests/unit/payment-methods/test-class-upe-split-payment-gateway.php b/tests/unit/payment-methods/test-class-upe-split-payment-gateway.php
index a3284a840ce..673aaf4bdd0 100644
--- a/tests/unit/payment-methods/test-class-upe-split-payment-gateway.php
+++ b/tests/unit/payment-methods/test-class-upe-split-payment-gateway.php
@@ -893,7 +893,7 @@ public function test_payment_methods_show_correct_default_outputs() {
$becs_method = $this->mock_payment_methods['au_becs_debit'];
$this->assertEquals( 'card', $card_method->get_id() );
- $this->assertEquals( 'Credit card / debit card', $card_method->get_title( 'US' ) );
+ $this->assertEquals( 'Cards', $card_method->get_title( 'US' ) );
$this->assertEquals( 'Visa debit card', $card_method->get_title( 'US', $mock_visa_details ) );
$this->assertEquals( 'Mastercard credit card', $card_method->get_title( 'US', $mock_mastercard_details ) );
$this->assertTrue( $card_method->is_enabled_at_checkout( 'US' ) );
diff --git a/tests/unit/test-class-wc-payment-gateway-wcpay.php b/tests/unit/test-class-wc-payment-gateway-wcpay.php
index 1827041a1fc..039f229e3a7 100644
--- a/tests/unit/test-class-wc-payment-gateway-wcpay.php
+++ b/tests/unit/test-class-wc-payment-gateway-wcpay.php
@@ -623,7 +623,7 @@ public function test_payment_methods_show_correct_default_outputs() {
$afterpay_method = $this->payment_methods['afterpay_clearpay'];
$this->assertEquals( 'card', $card_method->get_id() );
- $this->assertEquals( 'Credit card / debit card', $card_method->get_title() );
+ $this->assertEquals( 'Cards', $card_method->get_title() );
$this->assertEquals( 'Visa debit card', $card_method->get_title( 'US', $mock_visa_details ) );
$this->assertEquals( 'Mastercard credit card', $card_method->get_title( 'US', $mock_mastercard_details ) );
$this->assertTrue( $card_method->is_enabled_at_checkout( 'US' ) );
diff --git a/tests/unit/test-class-wc-payments-checkout.php b/tests/unit/test-class-wc-payments-checkout.php
index 1fcbe1093ff..0153ecd7c51 100644
--- a/tests/unit/test-class-wc-payments-checkout.php
+++ b/tests/unit/test-class-wc-payments-checkout.php
@@ -377,7 +377,7 @@ public function test_link_payment_method_provided_when_card_enabled() {
[
'card' => [
'isReusable' => true,
- 'title' => 'Credit card / debit card',
+ 'title' => 'Cards',
'icon' => $icon_url,
'darkIcon' => $dark_icon_url,
'showSaveOption' => true,
From 83dc299b2f8ec56b0894df3ccb52e06f8262747b Mon Sep 17 00:00:00 2001
From: Mike Moore
Date: Wed, 22 Jan 2025 12:20:10 -0500
Subject: [PATCH 06/13] Update settings UI text for credit / debit cards
---
client/payment-methods-map.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/payment-methods-map.tsx b/client/payment-methods-map.tsx
index b22ca1f9ae2..69fa3e0f74a 100644
--- a/client/payment-methods-map.tsx
+++ b/client/payment-methods-map.tsx
@@ -44,7 +44,7 @@ const PaymentMethodInformationObject: Record<
> = {
card: {
id: 'card',
- label: __( 'Credit / Debit card', 'woocommerce-payments' ),
+ label: __( 'Credit / Debit Cards', 'woocommerce-payments' ),
description: __(
'Let your customers pay with major credit and debit cards without leaving your store.',
'woocommerce-payments'
From 858b9a281f0913069cff53e0c1d9df523c0e0940 Mon Sep 17 00:00:00 2001
From: Mike Moore
Date: Wed, 22 Jan 2025 18:51:15 -0500
Subject: [PATCH 07/13] Update tests
---
.../payment-methods-checkboxes/test/index.test.tsx | 2 +-
.../__tests__/__snapshots__/activation-modal.test.js.snap | 4 ++--
.../__tests__/__snapshots__/delete-modal.test.js.snap | 6 +++---
.../__tests__/payment-methods-section.test.js | 2 +-
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/client/components/payment-methods-checkboxes/test/index.test.tsx b/client/components/payment-methods-checkboxes/test/index.test.tsx
index 59b231190e0..de5891f5954 100644
--- a/client/components/payment-methods-checkboxes/test/index.test.tsx
+++ b/client/components/payment-methods-checkboxes/test/index.test.tsx
@@ -195,7 +195,7 @@ describe( 'PaymentMethodsCheckboxes', () => {
);
const cardCheckbox = screen.getByRole( 'checkbox', {
- name: 'Credit / Debit card',
+ name: 'Credit / Debit Cards',
} );
expect( cardCheckbox ).not.toBeChecked();
userEvent.click( cardCheckbox );
diff --git a/client/settings/payment-methods-list/__tests__/__snapshots__/activation-modal.test.js.snap b/client/settings/payment-methods-list/__tests__/__snapshots__/activation-modal.test.js.snap
index 6382c0dbaa0..c76c4ca8683 100644
--- a/client/settings/payment-methods-list/__tests__/__snapshots__/activation-modal.test.js.snap
+++ b/client/settings/payment-methods-list/__tests__/__snapshots__/activation-modal.test.js.snap
@@ -55,7 +55,7 @@ exports[`Activation Modal matches the snapshot 1`] = `
class="components-modal__header-heading"
id="components-modal-header-0"
>
- One more step to enable Credit / Debit card
+ One more step to enable Credit / Debit Cards
You can add it again at any time in
diff --git a/client/settings/payment-methods-section/__tests__/payment-methods-section.test.js b/client/settings/payment-methods-section/__tests__/payment-methods-section.test.js
index d64f2ded639..8e5c8b15097 100644
--- a/client/settings/payment-methods-section/__tests__/payment-methods-section.test.js
+++ b/client/settings/payment-methods-section/__tests__/payment-methods-section.test.js
@@ -110,7 +110,7 @@ describe( 'PaymentMethodsSection', () => {
render( );
const card = screen.getByRole( 'checkbox', {
- name: 'Credit / Debit card',
+ name: 'Credit / Debit Cards',
} );
const becs = screen.getByRole( 'checkbox', {
name: 'BECS Direct Debit',
From 2d30b89e21fc4ccb882fb437838c0bafe5fe36ce Mon Sep 17 00:00:00 2001
From: Mike Moore
Date: Mon, 27 Jan 2025 13:58:10 -0500
Subject: [PATCH 08/13] Better responsive handling
---
.../checkout/blocks/payment-method-label.js | 3 +-
.../payment-methods-logos/logo-popover.tsx | 38 +++++------
.../blocks/payment-methods-logos/style.scss | 13 ++--
client/checkout/classic/event-handlers.js | 68 +++++++++++--------
client/checkout/classic/style.scss | 18 +++++
5 files changed, 82 insertions(+), 58 deletions(-)
diff --git a/client/checkout/blocks/payment-method-label.js b/client/checkout/blocks/payment-method-label.js
index 6b5918de763..ecc339b0a32 100644
--- a/client/checkout/blocks/payment-method-label.js
+++ b/client/checkout/blocks/payment-method-label.js
@@ -40,8 +40,7 @@ const paymentMethods = [
];
const breakpointConfigs = [
{ breakpoint: 550, maxElements: 2 },
- { breakpoint: 833, maxElements: 4 },
- { breakpoint: 960, maxElements: 2 },
+ { breakpoint: 330, maxElements: 1 },
];
const bnplMethods = [ 'affirm', 'afterpay_clearpay', 'klarna' ];
diff --git a/client/checkout/blocks/payment-methods-logos/logo-popover.tsx b/client/checkout/blocks/payment-methods-logos/logo-popover.tsx
index f1ebde5c300..36d7aa1c761 100644
--- a/client/checkout/blocks/payment-methods-logos/logo-popover.tsx
+++ b/client/checkout/blocks/payment-methods-logos/logo-popover.tsx
@@ -37,31 +37,19 @@ export const LogoPopover: React.FC< LogoPopoverProps > = ( {
return;
}
- // Get the most up-to-date anchor rect
- const anchorRect = anchor.getBoundingClientRect();
+ const label = anchor.closest( 'label' );
+ if ( ! label ) return;
- // Temporarily make the popover visible to get correct dimensions
- popover.style.visibility = 'hidden';
- popover.style.display = 'block';
- const popoverRect = popover.getBoundingClientRect();
- popover.style.display = '';
- popover.style.visibility = '';
-
- const offset = 7;
- const left = anchorRect.left;
- // Position the popover above the anchor
- const top = anchorRect.top - popoverRect.height - offset;
+ const labelRect = label.getBoundingClientRect();
+ const labelStyle = window.getComputedStyle( label );
+ const labelPaddingRight = parseInt( labelStyle.paddingRight, 10 );
popover.style.position = 'fixed';
- popover.style.width = `${ anchorRect.width }px`;
- popover.style.left = `${ left }px`;
- popover.style.top = `${ top }px`;
-
- // Adjust position if popover goes off-screen
- if ( top < 0 ) {
- // If there's not enough space above, position it below the anchor
- popover.style.top = `${ anchorRect.bottom + offset }px`;
- }
+ popover.style.right = `${
+ window.innerWidth - ( labelRect.right - labelPaddingRight )
+ }px`;
+ popover.style.top = `${ labelRect.top - 30 }px`;
+ popover.style.left = 'auto';
setIsPositioned( true );
}, [ anchor ] );
@@ -128,6 +116,12 @@ export const LogoPopover: React.FC< LogoPopoverProps > = ( {
zIndex: 1000,
opacity: isPositioned ? 1 : 0,
transition: 'opacity 0.2s',
+ gridTemplateColumns: `repeat(${
+ React.Children.count( children ) > 5
+ ? 5
+ : React.Children.count( children )
+ }, 38px)`,
+ left: 'auto',
} }
role="dialog"
aria-label="Supported Credit Card Brands"
diff --git a/client/checkout/blocks/payment-methods-logos/style.scss b/client/checkout/blocks/payment-methods-logos/style.scss
index b5f08d2ef8d..4c7f113427a 100644
--- a/client/checkout/blocks/payment-methods-logos/style.scss
+++ b/client/checkout/blocks/payment-methods-logos/style.scss
@@ -29,19 +29,18 @@
background-color: #fff;
border: 1px solid $gray-300;
border-radius: 3px;
- padding: 10px;
+ padding: 8px;
box-sizing: border-box;
box-shadow: 0 0 10px 0 rgba( 0, 0, 0, 0.1 );
display: grid;
- grid-template-columns: repeat( auto-fit, minmax( 38px, 1fr ) );
- gap: 10px;
- justify-items: center;
- align-items: center;
+ gap: 8px;
+ justify-content: center;
cursor: pointer;
+ width: fit-content;
> img {
+ width: 38px;
+ height: 24px;
box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.1 );
- max-width: 100%;
- height: auto;
}
}
diff --git a/client/checkout/classic/event-handlers.js b/client/checkout/classic/event-handlers.js
index c866524f974..ac6179b9295 100644
--- a/client/checkout/classic/event-handlers.js
+++ b/client/checkout/classic/event-handlers.js
@@ -271,7 +271,19 @@ jQuery( function ( $ ) {
];
function getMaxElements() {
- return window.innerWidth <= 330 ? 2 : 4;
+ const paymentMethodElement = document.querySelector(
+ '.payment_method_woocommerce_payments'
+ );
+ if ( ! paymentMethodElement ) {
+ return 4; // Default fallback
+ }
+
+ const elementWidth = paymentMethodElement.offsetWidth;
+ if ( elementWidth <= 300 ) {
+ return 1;
+ } else if ( elementWidth <= 330 ) {
+ return 2;
+ }
}
function shouldHavePopover() {
@@ -296,6 +308,16 @@ jQuery( function ( $ ) {
popover.appendChild( img );
} );
+ // Calculate number of items per row (max 5)
+ const itemsPerRow = Math.min( remainingMethods.length, 5 );
+
+ // Set grid-template-columns based on number of items
+ popover.style.gridTemplateColumns = `repeat(${ itemsPerRow }, 38px)`;
+
+ // Calculate width: (items * width) + (gaps * gap-size) + (padding * 2)
+ const width = itemsPerRow * 38 + ( itemsPerRow - 1 ) * 8 + 16;
+ popover.style.width = `${ width }px`;
+
return popover;
}
@@ -304,31 +326,16 @@ jQuery( function ( $ ) {
if ( ! label ) return;
const labelRect = label.getBoundingClientRect();
- const anchorRect = anchor.getBoundingClientRect();
-
- if ( ! popover.dataset.labelOffset ) {
- popover.style.visibility = 'hidden';
- popover.style.display = 'block';
- const popoverRect = popover.getBoundingClientRect();
- popover.style.display = '';
- popover.style.visibility = '';
-
- const offset = 7;
- const initialTop = labelRect.top - popoverRect.height - offset;
-
- // Store the offset from the label
- popover.dataset.labelOffset = (
- labelRect.top - initialTop
- ).toString();
- }
-
- const labelOffset = parseFloat( popover.dataset.labelOffset );
+ const labelStyle = window.getComputedStyle( label );
+ const labelPaddingRight = parseInt( labelStyle.paddingRight, 10 );
popover.style.position = 'fixed';
- popover.style.width = `${ anchorRect.width }px`;
- popover.style.left = `${ anchorRect.left }px`; // Use anchor's left position
- popover.style.top = `${ labelRect.top - labelOffset }px`;
+ popover.style.right = `${
+ window.innerWidth - ( labelRect.right - labelPaddingRight )
+ }px`;
+ popover.style.top = `${ labelRect.top - 25 }px`;
popover.style.zIndex = '1000';
+ popover.style.left = 'auto';
}
function updateLogos() {
@@ -352,6 +359,14 @@ jQuery( function ( $ ) {
const countDiv = document.createElement( 'div' );
countDiv.className = 'payment-methods--logos-count';
countDiv.textContent = `+ ${ remainingCount }`;
+
+ // Add click handler directly to the count div
+ countDiv.addEventListener( 'click', ( e ) => {
+ e.stopPropagation();
+ e.preventDefault();
+ togglePopover();
+ } );
+
innerContainer.appendChild( countDiv );
}
@@ -424,13 +439,12 @@ jQuery( function ( $ ) {
setupPopover();
}
- // Click handler
- innerContainer.addEventListener( 'click', togglePopover );
-
- // Keyboard handler
+ // Remove the click handler from innerContainer since we're handling it on the count div
+ // Keep the keyboard handler for accessibility
innerContainer.addEventListener( 'keydown', ( e ) => {
if ( e.key === 'Enter' || e.key === ' ' ) {
e.preventDefault();
+ e.stopPropagation();
togglePopover();
}
} );
diff --git a/client/checkout/classic/style.scss b/client/checkout/classic/style.scss
index 2b937fb2dae..7d515babe6c 100644
--- a/client/checkout/classic/style.scss
+++ b/client/checkout/classic/style.scss
@@ -49,6 +49,24 @@
height: 24px !important;
max-height: 24px !important;
}
+ .logo-popover {
+ background-color: #fff;
+ border: 1px solid $gray-300;
+ border-radius: 3px;
+ padding: 8px;
+ box-sizing: border-box;
+ box-shadow: 0 0 10px 0 rgba( 0, 0, 0, 0.1 );
+ display: grid;
+ gap: 8px;
+ justify-content: center;
+ cursor: pointer;
+
+ > img {
+ box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.1 );
+ width: 38px;
+ height: 24px;
+ }
+ }
}
}
From ca885bb92ccdd36a835d346baa5ad1c690b6550e Mon Sep 17 00:00:00 2001
From: Mike Moore
Date: Fri, 31 Jan 2025 16:34:28 -0500
Subject: [PATCH 09/13] Update e2e tests
---
.../merchant/merchant-payment-gateways-confirmation.spec.ts | 3 +--
tests/e2e-pw/utils/shopper.ts | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/tests/e2e-pw/specs/merchant/merchant-payment-gateways-confirmation.spec.ts b/tests/e2e-pw/specs/merchant/merchant-payment-gateways-confirmation.spec.ts
index 7e089ba858a..fc26cad9ccc 100644
--- a/tests/e2e-pw/specs/merchant/merchant-payment-gateways-confirmation.spec.ts
+++ b/tests/e2e-pw/specs/merchant/merchant-payment-gateways-confirmation.spec.ts
@@ -13,8 +13,7 @@ test.describe( 'payment gateways disable confirmation', () => {
const getToggle = ( page: Page ) =>
page.getByRole( 'link', {
- name:
- '"WooPayments (Credit card / debit card)" payment method is currently',
+ name: '"WooPayments (Cards)" payment method is currently',
} );
const getModalHeading = ( page: Page ) =>
diff --git a/tests/e2e-pw/utils/shopper.ts b/tests/e2e-pw/utils/shopper.ts
index d3105d83a11..a14d40ae293 100644
--- a/tests/e2e-pw/utils/shopper.ts
+++ b/tests/e2e-pw/utils/shopper.ts
@@ -480,7 +480,7 @@ export const addSavedCard = async (
) => {
await page.getByRole( 'link', { name: 'Add payment method' } ).click();
await page.waitForLoadState( 'networkidle' );
- await page.getByText( 'Credit card / debit card' ).click();
+ await page.getByText( 'Cards' ).click();
const frameHandle = page.getByTitle( 'Secure payment input frame' );
const stripeFrame = frameHandle.contentFrame();
From 1b83a56971f9dbc33ea5426f205fdca15ffbe9b9 Mon Sep 17 00:00:00 2001
From: Mike Moore
Date: Fri, 31 Jan 2025 20:30:17 -0500
Subject: [PATCH 10/13] Update e2e tests
---
.../shopper/shopper-subscriptions-purchase-free-trial.spec.ts | 2 +-
.../shopper-subscriptions-purchase-no-signup-fee.spec.ts | 2 +-
tests/e2e-pw/utils/shopper.ts | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-free-trial.spec.ts b/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-free-trial.spec.ts
index 275a99752cc..0350326b7bb 100644
--- a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-free-trial.spec.ts
+++ b/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-free-trial.spec.ts
@@ -151,7 +151,7 @@ describeif( shouldRunSubscriptionsTests )(
test( 'Merchant should be able to create an order with "Setup Intent"', async () => {
await goToOrder( merchantPage, orderId );
await expect(
- merchantPage.getByText( 'Payment via Credit card /' )
+ merchantPage.getByText( 'Payment via Cards /' )
).toHaveText( /\(seti_.*\)/ );
await goToSubscriptions( merchantPage );
diff --git a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts b/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts
index 626c42771d5..9279440b515 100644
--- a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts
+++ b/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts
@@ -71,7 +71,7 @@ describeif( shouldRunSubscriptionsTests )(
.replace( '#', '' );
const transactionPageLink = await merchantPage
- .getByText( 'Payment via Credit card /' )
+ .getByText( 'Payment via Cards /' )
.getByRole( 'link', { name: /pi_.+/ } )
.getAttribute( 'href' );
diff --git a/tests/e2e-pw/utils/shopper.ts b/tests/e2e-pw/utils/shopper.ts
index a14d40ae293..f9c66e49b95 100644
--- a/tests/e2e-pw/utils/shopper.ts
+++ b/tests/e2e-pw/utils/shopper.ts
@@ -295,7 +295,7 @@ export const addToCartFromShopPage = async (
export const selectPaymentMethod = async (
page: Page,
- paymentMethod = 'Credit card'
+ paymentMethod = 'Cards'
) => {
await page.getByText( paymentMethod ).click();
};
From 5f6fdc0368a0c86c35a1eb7cbce579d7182b2709 Mon Sep 17 00:00:00 2001
From: Mike Moore
Date: Mon, 3 Feb 2025 12:39:48 -0500
Subject: [PATCH 11/13] Update subscriptions e2e test
---
.../shopper/shopper-subscriptions-purchase-free-trial.spec.ts | 4 ++--
.../shopper-subscriptions-purchase-no-signup-fee.spec.ts | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-free-trial.spec.ts b/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-free-trial.spec.ts
index 0350326b7bb..1322f476e7a 100644
--- a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-free-trial.spec.ts
+++ b/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-free-trial.spec.ts
@@ -151,8 +151,8 @@ describeif( shouldRunSubscriptionsTests )(
test( 'Merchant should be able to create an order with "Setup Intent"', async () => {
await goToOrder( merchantPage, orderId );
await expect(
- merchantPage.getByText( 'Payment via Cards /' )
- ).toHaveText( /\(seti_.*\)/ );
+ merchantPage.locator( '.woocommerce-order-data__meta' )
+ ).toContainText( 'seti_' );
await goToSubscriptions( merchantPage );
const subscriptionRow = merchantPage.getByRole( 'row', {
diff --git a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts b/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts
index 9279440b515..ad28925e112 100644
--- a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts
+++ b/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts
@@ -71,7 +71,7 @@ describeif( shouldRunSubscriptionsTests )(
.replace( '#', '' );
const transactionPageLink = await merchantPage
- .getByText( 'Payment via Cards /' )
+ .getByText( 'Payment via Cards', { exact: false } )
.getByRole( 'link', { name: /pi_.+/ } )
.getAttribute( 'href' );
@@ -81,7 +81,7 @@ describeif( shouldRunSubscriptionsTests )(
await expect(
merchantPage.getByText(
- 'A payment of $9.99 was successfully charged.'
+ 'A payment of $9.99 USD was successfully charged.'
)
).toBeVisible();
From a691f2ac95a0a4eb18e218dcbdcecc63c477d1f6 Mon Sep 17 00:00:00 2001
From: Mike Moore
Date: Mon, 3 Feb 2025 20:09:48 -0500
Subject: [PATCH 12/13] Only inject payment logos once
---
client/checkout/classic/event-handlers.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/client/checkout/classic/event-handlers.js b/client/checkout/classic/event-handlers.js
index ac6179b9295..7eb87d79481 100644
--- a/client/checkout/classic/event-handlers.js
+++ b/client/checkout/classic/event-handlers.js
@@ -250,6 +250,8 @@ jQuery( function ( $ ) {
);
if ( ! cardLabel ) return;
+ if ( cardLabel.querySelector( '.payment-methods--logos' ) ) return;
+
const target = cardLabel.querySelector( 'img' );
if ( ! target ) return;
From f78457c18c6b0e461882140610c0d437ea6ec0f6 Mon Sep 17 00:00:00 2001
From: Mike Moore
Date: Tue, 4 Feb 2025 11:45:39 -0500
Subject: [PATCH 13/13] Remove currency from E2E test
---
.../shopper-subscriptions-purchase-no-signup-fee.spec.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts b/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts
index ad28925e112..9cace535df1 100644
--- a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts
+++ b/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts
@@ -81,7 +81,7 @@ describeif( shouldRunSubscriptionsTests )(
await expect(
merchantPage.getByText(
- 'A payment of $9.99 USD was successfully charged.'
+ 'A payment of $9.99 was successfully charged.'
)
).toBeVisible();