Commit 3752a5fac78 for woocommerce

commit 3752a5fac78706ece062baa0e7ce52b794c6c6c9
Author: Karol Manijak <20098064+kmanijak@users.noreply.github.com>
Date:   Tue Apr 14 09:28:22 2026 +0200

    Fix Product Results Count not updating when filtering products (#63969)

    * Fix Product Results Count being overwritten by legacy JS when iAPI updates it

    The refresh_sorted_by_live_region() function was unconditionally snapshotting
    the result count innerHTML on page load and restoring it after 2 seconds,
    overwriting any updates made by the Interactivity API during that window.

    Guard the function to only run when both a query string is present (page loaded
    from filtering) and a legacy filter block (.wc-blocks-filter-wrapper) exists on
    the page. Also add data-wp-interactive to ProductResultsCount block output.

    Fixes #63948

    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

    * Add changefile(s) from automation for the following project(s): woocommerce

    * Use role="status" for result count, upgrade to role="alert" only for legacy filters

    The result count element had role="alert" by default, which caused
    Playwright strict mode violations when tests looked for getByRole('alert')
    and found both the cart notice and the result count. Changed the template
    to use role="status" (a polite live region) by default, and only upgrade
    to role="alert" in the JS when legacy filter blocks trigger a refresh.

    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

    * Invert filter detection: skip for iAPI filters instead of requiring legacy ones

    Instead of checking for legacy filter blocks (.wc-blocks-filter-wrapper),
    detect Interactivity API filters and only skip for those. This ensures
    legacy widgets and classic filters continue to get the accessibility
    announcement.

    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

    ---------

    Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
    Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/63969-fix-product-results-count-refresh b/plugins/woocommerce/changelog/63969-fix-product-results-count-refresh
new file mode 100644
index 00000000000..0204f26647a
--- /dev/null
+++ b/plugins/woocommerce/changelog/63969-fix-product-results-count-refresh
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix Product Results Count block not updating when filtering products with the Interactivity API.
\ No newline at end of file
diff --git a/plugins/woocommerce/client/legacy/js/frontend/woocommerce.js b/plugins/woocommerce/client/legacy/js/frontend/woocommerce.js
index 0cc915268b9..1ee26bc2efc 100644
--- a/plugins/woocommerce/client/legacy/js/frontend/woocommerce.js
+++ b/plugins/woocommerce/client/legacy/js/frontend/woocommerce.js
@@ -28,7 +28,7 @@ jQuery( function ( $ ) {
 		 * we need to add the keydown event listener to it.
 		 */
 		function store_notice_keydown_handler( event ) {
-			if ( ['Enter', ' '].includes( event.key ) ) {
+			if ( [ 'Enter', ' ' ].includes( event.key ) ) {
 				event.preventDefault();
 				$( '.woocommerce-store-notice__dismiss-link' ).click();
 			}
@@ -184,11 +184,18 @@ jQuery( function ( $ ) {

 	// If the "Enable AJAX add to cart buttons on archives" setting is disabled
 	// the add-to-cart.js file won't be loaded, so we need to add the event listener here.
-	if ( typeof wc_add_to_cart_params === 'undefined') {
-		$( document.body ).on( 'keydown', '.remove_from_cart_button', on_keydown_remove_from_cart );
+	if ( typeof wc_add_to_cart_params === 'undefined' ) {
+		$( document.body ).on(
+			'keydown',
+			'.remove_from_cart_button',
+			on_keydown_remove_from_cart
+		);
 	}

-	$( document.body ).on( 'item_removed_from_classic_cart updated_wc_div', focus_populate_live_region );
+	$( document.body ).on(
+		'item_removed_from_classic_cart updated_wc_div',
+		focus_populate_live_region
+	);
 } );

 /**
@@ -240,23 +247,36 @@ function focus_populate_live_region() {

 /**
  * Refresh the sorted by live region.
+ *
+ * Skips when the Interactivity API product filters are present on the page,
+ * as those manage the result count updates themselves.
  */
 function refresh_sorted_by_live_region() {
 	var sorted_by_live_region = document.querySelector(
 		'.woocommerce-result-count'
 	);
+	var hasInteractivityFilters = document.querySelector(
+		'[data-wp-interactive="woocommerce/product-filters"]'
+	);

-	if ( sorted_by_live_region ) {
-		var text = sorted_by_live_region.innerHTML;
-		sorted_by_live_region.setAttribute('aria-hidden', 'true');
-
-		var sorted_by_live_region_id = setTimeout( function () {
-			sorted_by_live_region.setAttribute('aria-hidden', 'false');
-			sorted_by_live_region.innerHTML = '';
-			sorted_by_live_region.innerHTML = text;
-			clearTimeout( sorted_by_live_region_id );
-		}, 2000 );
+	if (
+		! sorted_by_live_region ||
+		! window.location.search ||
+		hasInteractivityFilters
+	) {
+		return;
 	}
+
+	var text = sorted_by_live_region.innerHTML;
+	sorted_by_live_region.setAttribute( 'role', 'alert' );
+	sorted_by_live_region.setAttribute( 'aria-hidden', 'true' );
+
+	var sorted_by_live_region_id = setTimeout( function () {
+		sorted_by_live_region.setAttribute( 'aria-hidden', 'false' );
+		sorted_by_live_region.innerHTML = '';
+		sorted_by_live_region.innerHTML = text;
+		clearTimeout( sorted_by_live_region_id );
+	}, 2000 );
 }

 function on_document_ready() {
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductResultsCount.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductResultsCount.php
index c952361075c..a1252b07270 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductResultsCount.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductResultsCount.php
@@ -57,6 +57,7 @@ class ProductResultsCount extends AbstractBlock {
 		);
 		$p->set_attribute( 'class', implode( ' ', $classes ) );
 		$p->set_attribute( 'style', $parsed_style_attributes['styles'] );
+		$p->set_attribute( 'data-wp-interactive', $this->get_full_block_name() );
 		$p->set_attribute(
 			'data-wp-router-region',
 			'wc-product-results-count-' . ( isset( $block->context['queryId'] ) ? $block->context['queryId'] : 0 )
diff --git a/plugins/woocommerce/templates/loop/result-count.php b/plugins/woocommerce/templates/loop/result-count.php
index 612b95f881b..14573284572 100644
--- a/plugins/woocommerce/templates/loop/result-count.php
+++ b/plugins/woocommerce/templates/loop/result-count.php
@@ -14,14 +14,14 @@
  *
  * @see         https://woocommerce.com/document/template-structure/
  * @package     WooCommerce\Templates
- * @version     9.9.0
+ * @version     10.8.0
  */

 if ( ! defined( 'ABSPATH' ) ) {
 	exit;
 }
 ?>
-<p class="woocommerce-result-count" role="alert" aria-relevant="all" <?php echo ( empty( $orderedby ) || 1 === intval( $total ) ) ? '' : 'data-is-sorted-by="true"'; ?>>
+<p class="woocommerce-result-count" role="status" aria-relevant="all" <?php echo ( empty( $orderedby ) || 1 === intval( $total ) ) ? '' : 'data-is-sorted-by="true"'; ?>>
 	<?php
 	// phpcs:disable WordPress.Security
 	if ( 1 === intval( $total ) ) {