Commit bc742e9e4fe for woocommerce
commit bc742e9e4feefb63d77c0ac24e1316a97d6c0e27
Author: Ahmed <ahmed.el.azzabi@automattic.com>
Date: Mon Jul 6 15:49:11 2026 +0100
Replace payment method notice with WordPress UI (#66016)
* refactor: Replace payment method notice with WordPress UI
* chore: Add changelog for payment method notice
diff --git a/plugins/woocommerce/changelog/wooprd-3212-wp-ui-payment-method-notice b/plugins/woocommerce/changelog/wooprd-3212-wp-ui-payment-method-notice
new file mode 100644
index 00000000000..01e9604f779
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooprd-3212-wp-ui-payment-method-notice
@@ -0,0 +1,4 @@
+Significance: patch
+Type: update
+
+Use the standard WordPress UI Notice for payment method eligibility notices in WooPayments onboarding.
diff --git a/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/list-item.tsx b/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/list-item.tsx
index 9ab9f42b549..3c13e8c1a27 100644
--- a/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/list-item.tsx
+++ b/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/list-item.tsx
@@ -3,10 +3,9 @@
*/
import { decodeEntities } from '@wordpress/html-entities';
import { type RecommendedPaymentMethod } from '@woocommerce/data';
-import { ExternalLink, Icon, ToggleControl } from '@wordpress/components';
-import { info as infoIcon } from '@wordpress/icons';
-import { useEffect, useRef } from '@wordpress/element';
-import { speak } from '@wordpress/a11y';
+import { ToggleControl } from '@wordpress/components';
+import { useRef } from '@wordpress/element';
+import { Notice } from '@wordpress/ui';
/**
* Internal dependencies
@@ -82,14 +81,6 @@ export const PaymentMethodListItem = ( {
const shouldRender = isExpanded || baseVisibility;
- // Announce notice to screen readers when the method is toggled on.
- const isEnabled = paymentMethodsState[ method.id ] ?? false;
- useEffect( () => {
- if ( isEnabled && method.notice?.message ) {
- speak( method.notice.message, 'polite' );
- }
- }, [ isEnabled, method.notice?.message ] );
-
if ( ! shouldRender ) {
return null;
}
@@ -209,26 +200,31 @@ export const PaymentMethodListItem = ( {
</div>
{ method.notice?.message &&
( paymentMethodsState[ method.id ] ?? false ) && (
- <div
+ <Notice.Root
className="woocommerce-list__item-notice-info"
data-testid="payment-method-notice-info"
+ intent="info"
+ spokenMessage={ decodeEntities(
+ method.notice.message
+ ) }
>
- <Icon icon={ infoIcon } size={ 24 } />
- <p>
- { method.notice.message }
- { method.notice.link_url &&
- method.notice.link_text && (
- <>
- { ' ' }
- <ExternalLink
- href={ method.notice.link_url }
- >
- { method.notice.link_text }
- </ExternalLink>
- </>
- ) }
- </p>
- </div>
+ <Notice.Description>
+ { decodeEntities( method.notice.message ) }
+ </Notice.Description>
+ { method.notice.link_url && method.notice.link_text && (
+ <Notice.Actions>
+ <Notice.ActionLink
+ href={ method.notice.link_url }
+ openInNewTab
+ rel="noopener noreferrer"
+ >
+ { decodeEntities(
+ method.notice.link_text
+ ) }
+ </Notice.ActionLink>
+ </Notice.Actions>
+ ) }
+ </Notice.Root>
) }
</div>
);
diff --git a/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/style.scss b/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/style.scss
index 6734675162b..d00d42617a6 100644
--- a/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/style.scss
+++ b/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/style.scss
@@ -260,26 +260,5 @@
.woocommerce-list__item-notice-info {
flex-basis: 100%;
- display: flex;
- gap: 4px;
- align-items: flex-start;
margin: 20px 0 0;
- padding: 12px;
- background-color: #f5f9fd;
- border: 1px solid #9fbcdd;
- border-radius: 8px;
-
- svg {
- flex-shrink: 0;
- fill: #006cd8;
- }
-
- p {
- margin: 0;
- padding: 2px 0;
- font-size: 13px;
- font-weight: 400;
- line-height: 20px;
- color: #001758;
- }
}
diff --git a/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/test/list-item.test.tsx b/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/test/list-item.test.tsx
index 626b5ac8848..36ce33469d8 100644
--- a/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/test/list-item.test.tsx
+++ b/plugins/woocommerce/client/admin/client/settings-payments/onboarding/providers/woopayments/steps/payment-methods-selection/test/list-item.test.tsx
@@ -3,10 +3,65 @@
*/
import { render, screen } from '@testing-library/react';
import { type RecommendedPaymentMethod } from '@woocommerce/data';
+import { type ReactNode } from 'react';
-const mockSpeak = jest.fn();
-jest.mock( '@wordpress/a11y', () => ( {
- speak: ( ...args: unknown[] ) => mockSpeak( ...args ),
+type MockComponentProps = {
+ children?: ReactNode;
+ className?: string;
+ href?: string;
+ intent?: string;
+ openInNewTab?: boolean;
+ rel?: string;
+ spokenMessage?: string;
+ 'data-testid'?: string;
+};
+
+jest.mock( '@wordpress/components', () => ( {
+ ToggleControl: ( { checked }: { checked?: boolean } ) => (
+ <input type="checkbox" checked={ checked } readOnly />
+ ),
+} ) );
+
+jest.mock( '@wordpress/ui', () => ( {
+ Notice: {
+ Root: ( {
+ children,
+ className,
+ intent,
+ spokenMessage,
+ 'data-testid': testId,
+ }: MockComponentProps ) => (
+ <div
+ className={ className }
+ data-intent={ intent }
+ data-spoken-message={ spokenMessage }
+ data-testid={ testId }
+ >
+ { children }
+ </div>
+ ),
+ Description: ( { children }: MockComponentProps ) => (
+ <span>{ children }</span>
+ ),
+ Actions: ( { children }: MockComponentProps ) => (
+ <div>{ children }</div>
+ ),
+ ActionLink: ( {
+ children,
+ href,
+ openInNewTab,
+ rel,
+ }: MockComponentProps ) => (
+ // eslint-disable-next-line react/jsx-no-target-blank -- The mock forwards rel so tests can assert the component contract.
+ <a
+ href={ href }
+ rel={ rel }
+ target={ openInNewTab ? '_blank' : undefined }
+ >
+ { children }
+ </a>
+ ),
+ },
} ) );
/**
@@ -93,14 +148,14 @@ describe( 'PaymentMethodListItem', () => {
} );
} );
- describe( 'Warning notice', () => {
- it( 'renders a warning notice when method is enabled and notice.message is set', () => {
+ describe( 'Payment method notice', () => {
+ it( 'renders a notice when method is enabled and notice.message is set', () => {
const method = createMethod( {
id: 'p24',
notice: {
badge: 'Verification required',
- message: 'Strict requirements apply.',
- link_text: 'Review requirements',
+ message: 'Strict requirements & eligibility apply.',
+ link_text: 'Review requirements & terms',
link_url: 'https://example.com/docs',
},
} );
@@ -113,15 +168,30 @@ describe( 'PaymentMethodListItem', () => {
/>
);
- expect(
- screen.getByText( 'Strict requirements apply.' )
- ).toBeInTheDocument();
- expect(
- screen.getByRole( 'link', { name: /review requirements/i } )
- ).toHaveAttribute( 'href', 'https://example.com/docs' );
+ const notice = screen.getByTestId( 'payment-method-notice-info' );
+ expect( notice ).toHaveAttribute( 'data-intent', 'info' );
+ expect( notice ).toHaveAttribute(
+ 'data-spoken-message',
+ 'Strict requirements & eligibility apply.'
+ );
+ expect( notice ).toHaveTextContent(
+ 'Strict requirements & eligibility apply.'
+ );
+ const requirementsLink = screen.getByRole( 'link', {
+ name: /review requirements & terms/i,
+ } );
+ expect( requirementsLink ).toHaveAttribute(
+ 'href',
+ 'https://example.com/docs'
+ );
+ expect( requirementsLink ).toHaveAttribute( 'target', '_blank' );
+ expect( requirementsLink ).toHaveAttribute(
+ 'rel',
+ 'noopener noreferrer'
+ );
} );
- it( 'does not render a warning notice when method is disabled', () => {
+ it( 'does not render a notice when method is disabled', () => {
const method = createMethod( {
id: 'p24',
notice: {
@@ -145,7 +215,7 @@ describe( 'PaymentMethodListItem', () => {
).not.toBeInTheDocument();
} );
- it( 'does not render a warning notice when notice.message is empty', () => {
+ it( 'does not render a notice when notice.message is empty', () => {
const method = createMethod( {
notice: {
badge: 'Verification required',
diff --git a/plugins/woocommerce/client/admin/package.json b/plugins/woocommerce/client/admin/package.json
index 97fd3936d9d..a2539e4f775 100644
--- a/plugins/woocommerce/client/admin/package.json
+++ b/plugins/woocommerce/client/admin/package.json
@@ -100,6 +100,7 @@
"@wordpress/plugins": "catalog:wp-min",
"@wordpress/primitives": "catalog:wp-min",
"@wordpress/router": "0.7.0",
+ "@wordpress/ui": "catalog:wp-bundled",
"@wordpress/url": "catalog:wp-min",
"@wordpress/viewport": "catalog:wp-min",
"@wordpress/warning": "catalog:wp-min",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4c8e3a5df95..85adf07e2c4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -21,6 +21,9 @@ catalogs:
'@wordpress/interface':
specifier: 9.18.5
version: 9.18.5
+ '@wordpress/ui':
+ specifier: 0.15.1
+ version: 0.15.1
wp-min:
'@types/wordpress__block-editor':
specifier: 15.0.6
@@ -2966,6 +2969,9 @@ importers:
'@wordpress/router':
specifier: 0.7.0
version: 0.7.0(react@18.3.1)
+ '@wordpress/ui':
+ specifier: catalog:wp-bundled
+ version: 0.15.1(@date-fns/tz@1.4.1)(date-fns@3.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(stylelint@14.16.1)
'@wordpress/url':
specifier: catalog:wp-min
version: 4.33.1
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 90bbccedd83..edc1961bb87 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -63,6 +63,7 @@ catalogs:
'@wordpress/fields': 0.25.11
'@wordpress/icons': 11.0.1
'@wordpress/interface': 9.18.5
+ '@wordpress/ui': 0.15.1
'@wordpress/undo-manager': 1.33.1
'@wordpress/views': 1.0.8
'@wordpress/base-styles': 6.9.1