Commit e61f44b8c68 for woocommerce

commit e61f44b8c684d6d1b21b4b09246eea460a4153d4
Author: Rishabh Gupta <109821717+R1shabh-Gupta@users.noreply.github.com>
Date:   Tue Nov 10 23:19:07 2026 +0530

    Read product names as plain text in the checkout order summary for screen readers (#70734)

    * Read product names as plain text in the checkout order summary for screen readers

    * Add changelog entry

    * Decode entities before stripping HTML in the order summary screen reader text

    * Decode the order summary screen reader product name only once

diff --git a/plugins/woocommerce/changelog/70623-fix-order-summary-screen-reader-product-name b/plugins/woocommerce/changelog/70623-fix-order-summary-screen-reader-product-name
new file mode 100644
index 00000000000..e96ab2da127
--- /dev/null
+++ b/plugins/woocommerce/changelog/70623-fix-order-summary-screen-reader-product-name
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Screen readers now read product names with symbols like "&" correctly in the Checkout block order summary.
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/order-summary/order-summary-item.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/order-summary/order-summary-item.tsx
index 9b2d22f9033..3addaa5c515 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/order-summary/order-summary-item.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/order-summary/order-summary-item.tsx
@@ -3,6 +3,8 @@
  */
 import clsx from 'clsx';
 import { sprintf, _n } from '@wordpress/i18n';
+// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
+import { __unstableStripHTML as stripHTML } from '@wordpress/dom';
 import { Label } from '@woocommerce/blocks-components';
 import ProductPrice from '@woocommerce/base-components/product-price';
 import ProductName from '@woocommerce/base-components/product-name';
@@ -171,7 +173,8 @@ const OrderSummaryItem = ( {
 				case 'quantity':
 					return String( quantity );
 				case 'productName':
-					return name;
+					// `name` is HTML; stripHTML removes tags and decodes entities once, matching the visible name.
+					return stripHTML( name );
 				case 'price':
 					return formatPrice( subtotalPrice, totalsCurrency );
 			}
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/order-summary/test/index.js b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/order-summary/test/index.js
index c413b9ca03f..cfc99788eb9 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/order-summary/test/index.js
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/order-summary/test/index.js
@@ -78,4 +78,40 @@ describe( 'Order Summary', () => {
 			screen.getByText( textContentMatcher( '$0.00' ), visibleOnly )
 		).toBeInTheDocument();
 	} );
+
+	it( 'renders the product name as plain text in the screen reader price', () => {
+		render(
+			<OrderSummary
+				cartItems={ [
+					{
+						...previewCart.items[ 0 ],
+						name: 'Salt &#038; Grandma&#8217;s <em>Jam</em>',
+					},
+				] }
+			/>
+		);
+
+		expect(
+			screen.getByText(
+				'Total price for 2 Salt & Grandma’s Jam items: $20.00'
+			)
+		).toBeInTheDocument();
+	} );
+
+	it( 'keeps encoded angle brackets in the screen reader product name', () => {
+		render(
+			<OrderSummary
+				cartItems={ [
+					{
+						...previewCart.items[ 0 ],
+						name: 'Shirt &lt;XL&gt;',
+					},
+				] }
+			/>
+		);
+
+		expect(
+			screen.getByText( 'Total price for 2 Shirt <XL> items: $20.00' )
+		).toBeInTheDocument();
+	} );
 } );