Commit 5df7da7bf37 for woocommerce

commit 5df7da7bf37cd505b41f63ed85ebc062f08d975a
Author: Ali Raza <ali@alirazasolutions.com>
Date:   Thu Oct 22 09:04:12 2026 +0500

    Expose empty state on Product Filters wrapper (#69858)

    * Expose Product Filters empty state

    * refactor: collect filter empty states as boolean lists

    Amp-Thread-ID: https://ampcode.com/threads/T-01a152f8-07da-73cc-8ed8-a97556a3a680

    ---------

    Co-authored-by: Tung Du <dinhtungdu@gmail.com>
    Co-authored-by: Amp <amp@ampcode.com>

diff --git a/plugins/woocommerce/changelog/product-filters-empty-state b/plugins/woocommerce/changelog/product-filters-empty-state
new file mode 100644
index 00000000000..5e160abeed2
--- /dev/null
+++ b/plugins/woocommerce/changelog/product-filters-empty-state
@@ -0,0 +1,4 @@
+Significance: patch
+Type: enhancement
+
+Expose an empty-state class when all Product Filters have no options.
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/block.json
index c2d55ca310f..ecf844fe1ee 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/block.json
@@ -33,6 +33,6 @@
 			}
 		}
 	},
-	"usesContext": [ "activeFilters" ],
+	"usesContext": [ "activeFilters", "productFiltersKey" ],
 	"viewScriptModule": "woocommerce/product-filter-active"
 }
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/block.json
index d319e4e041a..f47cfdf2176 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/block.json
@@ -53,7 +53,7 @@
 			}
 		}
 	},
-	"usesContext": [ "query", "filterParams" ],
+	"usesContext": [ "query", "filterParams", "productFiltersKey" ],
 	"attributes": {
 		"attributeId": {
 			"type": "number",
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/block.json
index 90018500cc6..1852f94d674 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/block.json
@@ -12,6 +12,6 @@
 		"interactivity": true,
 		"html": false
 	},
-	"usesContext": [ "query", "filterParams" ],
+	"usesContext": [ "query", "filterParams", "productFiltersKey" ],
 	"viewScriptModule": "woocommerce/product-filter-price"
 }
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/block.json
index a7732b69be1..303c990803d 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/block.json
@@ -12,7 +12,7 @@
 		}
 	},
 	"ancestor": [ "woocommerce/product-filters" ],
-	"usesContext": [ "query", "filterParams" ],
+	"usesContext": [ "query", "filterParams", "productFiltersKey" ],
 	"attributes": {
 		"className": {
 			"type": "string",
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/status-filter/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/status-filter/block.json
index bbd621d79a5..975c7ec1daa 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/status-filter/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/status-filter/block.json
@@ -71,7 +71,7 @@
 			"default": true
 		}
 	},
-	"usesContext": [ "query", "filterParams" ],
+	"usesContext": [ "query", "filterParams", "productFiltersKey" ],
 	"example": {
 		"attributes": {
 			"isPreview": true
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/taxonomy-filter/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/taxonomy-filter/block.json
index 773a611913f..c9fb404d605 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/taxonomy-filter/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/taxonomy-filter/block.json
@@ -53,7 +53,7 @@
 			}
 		}
 	},
-	"usesContext": [ "query", "filterParams" ],
+	"usesContext": [ "query", "filterParams", "productFiltersKey" ],
 	"attributes": {
 		"taxonomy": {
 			"type": "string",
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index dbd35c83077..3351be5a359 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -50361,12 +50361,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductCollection/NoResults.php

-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\NoResults\:\:render\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductCollection/NoResults.php
-
 		-
 			message: '#^Call to function is_array\(\) with array\<array\<int\>\> will always evaluate to true\.$#'
 			identifier: function.alreadyNarrowedType
@@ -50493,12 +50487,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductCollection/Renderer.php

-		-
-			message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 6
-			path: src/Blocks/BlockTypes/ProductCollection/Utils.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\Utils\:\:prepare_and_execute_query\(\) has no return type specified\.$#'
 			identifier: missingType.return
@@ -50511,12 +50499,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductCollection/Utils.php

-		-
-			message: '#^Parameter \#1 \$block of function build_query_vars_from_query_block expects WP_Block, Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\WP_Block given\.$#'
-			identifier: argument.type
-			count: 2
-			path: src/Blocks/BlockTypes/ProductCollection/Utils.php
-
 		-
 			message: '#^Parameter \#1 \$object_or_string of function is_a expects object, WP_Post\|WP_Post_Type\|WP_Term\|WP_User\|null given\.$#'
 			identifier: argument.type
@@ -50529,18 +50511,6 @@ parameters:
 			count: 2
 			path: src/Blocks/BlockTypes/ProductCollection/Utils.php

-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\Utils\:\:get_query_vars\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductCollection/Utils.php
-
-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\Utils\:\:prepare_and_execute_query\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductCollection/Utils.php
-
 		-
 			message: '#^Property WooCommerce\:\:\$cart \(WC_Cart\) in isset\(\) is not nullable\.$#'
 			identifier: isset.property
@@ -50601,30 +50571,12 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductDetails.php

-		-
-			message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterActive.php
-
-		-
-			message: '#^Access to property \$parsed_block on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterActive.php
-
 		-
 			message: '#^Parameter \#1 \$extra_attributes of function get_block_wrapper_attributes expects array\<string, string\>, array\<string, string\|false\> given\.$#'
 			identifier: argument.type
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterActive.php

-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterActive\:\:render\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterActive.php
-
 		-
 			message: '#^Access to an undefined property object\:\:\$attribute_id\.$#'
 			identifier: property.notFound
@@ -50637,18 +50589,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterAttribute.php

-		-
-			message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 2
-			path: src/Blocks/BlockTypes/ProductFilterAttribute.php
-
-		-
-			message: '#^Access to property \$parsed_block on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterAttribute.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterAttribute\:\:delete_default_attribute_id_transient\(\) has no return type specified\.$#'
 			identifier: missingType.return
@@ -50685,12 +50625,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterAttribute.php

-		-
-			message: '#^Parameter \#1 \$block of static method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\Utils\:\:get_query_vars\(\) expects Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\WP_Block, Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterAttribute.php
-
 		-
 			message: '#^Parameter \#1 \$extra_attributes of function get_block_wrapper_attributes expects array\<string, string\>, array\<string, bool\|string\> given\.$#'
 			identifier: argument.type
@@ -50703,18 +50637,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterAttribute.php

-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterAttribute\:\:get_attribute_counts\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterAttribute.php
-
-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterAttribute\:\:render\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterAttribute.php
-
 		-
 			message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
 			identifier: class.notFound
@@ -50763,18 +50685,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterClearButton.php

-		-
-			message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 2
-			path: src/Blocks/BlockTypes/ProductFilterPrice.php
-
-		-
-			message: '#^Access to property \$parsed_block on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 2
-			path: src/Blocks/BlockTypes/ProductFilterPrice.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterPrice\:\:get_filtered_price\(\) has no return type specified\.$#'
 			identifier: missingType.return
@@ -50787,12 +50697,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterPrice.php

-		-
-			message: '#^Parameter \#1 \$block of static method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\Utils\:\:get_query_vars\(\) expects Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\WP_Block, Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterPrice.php
-
 		-
 			message: '#^Parameter \#1 \$extra_attributes of function get_block_wrapper_attributes expects array\<string, string\>, array\<string, bool\|string\> given\.$#'
 			identifier: argument.type
@@ -50805,18 +50709,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterPrice.php

-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterPrice\:\:get_filtered_price\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterPrice.php
-
-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterPrice\:\:render\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterPrice.php
-
 		-
 			message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
 			identifier: class.notFound
@@ -50841,18 +50733,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterPriceSlider.php

-		-
-			message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 2
-			path: src/Blocks/BlockTypes/ProductFilterRating.php
-
-		-
-			message: '#^Access to property \$parsed_block on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterRating.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterRating\:\:get_rating_counts\(\) has no return type specified\.$#'
 			identifier: missingType.return
@@ -50865,12 +50745,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterRating.php

-		-
-			message: '#^Parameter \#1 \$block of static method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\Utils\:\:get_query_vars\(\) expects Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\WP_Block, Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterRating.php
-
 		-
 			message: '#^Parameter \#1 \$extra_attributes of function get_block_wrapper_attributes expects array\<string, string\>, array\<string, bool\|string\> given\.$#'
 			identifier: argument.type
@@ -50883,18 +50757,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterRating.php

-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterRating\:\:get_rating_counts\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterRating.php
-
-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterRating\:\:render\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterRating.php
-
 		-
 			message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
 			identifier: class.notFound
@@ -50925,18 +50787,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterRemovableChips.php

-		-
-			message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 2
-			path: src/Blocks/BlockTypes/ProductFilterStatus.php
-
-		-
-			message: '#^Access to property \$parsed_block on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterStatus.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterStatus\:\:enqueue_data\(\) has no return type specified\.$#'
 			identifier: missingType.return
@@ -50955,42 +50805,12 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterStatus.php

-		-
-			message: '#^Parameter \#1 \$block of static method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\Utils\:\:get_query_vars\(\) expects Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\WP_Block, Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterStatus.php
-
 		-
 			message: '#^Parameter \#1 \$extra_attributes of function get_block_wrapper_attributes expects array\<string, string\>, array\<string, bool\|string\> given\.$#'
 			identifier: argument.type
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterStatus.php

-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterStatus\:\:get_stock_status_counts\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterStatus.php
-
-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterStatus\:\:render\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterStatus.php
-
-		-
-			message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 2
-			path: src/Blocks/BlockTypes/ProductFilterTaxonomy.php
-
-		-
-			message: '#^Access to property \$parsed_block on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterTaxonomy.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterTaxonomy\:\:enqueue_data\(\) has no return type specified\.$#'
 			identifier: missingType.return
@@ -51021,30 +50841,12 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterTaxonomy.php

-		-
-			message: '#^Parameter \#1 \$block of static method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\Utils\:\:get_query_vars\(\) expects Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\WP_Block, Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterTaxonomy.php
-
 		-
 			message: '#^Parameter \#1 \$extra_attributes of function get_block_wrapper_attributes expects array\<string, string\>, array\<string, bool\|string\> given\.$#'
 			identifier: argument.type
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilterTaxonomy.php

-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterTaxonomy\:\:get_taxonomy_term_counts\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterTaxonomy.php
-
-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilterTaxonomy\:\:render\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilterTaxonomy.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductFilters\:\:enqueue_data\(\) has no return type specified\.$#'
 			identifier: missingType.return
@@ -51639,12 +51441,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductTemplate.php

-		-
-			message: '#^Parameter \#1 \$block of static method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\Utils\:\:prepare_and_execute_query\(\) expects Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductCollection\\WP_Block, WP_Block given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/ProductTemplate.php
-
 		-
 			message: '#^Parameter \#1 \$inner_blocks of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductTemplate\:\:block_core_post_template_uses_featured_image\(\) expects Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block_List, WP_Block_List given\.$#'
 			identifier: argument.type
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/NoResults.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/NoResults.php
index c1b2e0927b7..0b0b7d76bd7 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/NoResults.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/NoResults.php
@@ -5,6 +5,7 @@ namespace Automattic\WooCommerce\Blocks\BlockTypes\ProductCollection;

 use Automattic\WooCommerce\Blocks\BlockTypes\ProductCollection\Utils as ProductCollectionUtils;
 use Automattic\WooCommerce\Blocks\BlockTypes\AbstractBlock;
+use WP_Block;

 /**
  * NoResults class.
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/Utils.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/Utils.php
index f49ec22683c..c954a9f30a2 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/Utils.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/Utils.php
@@ -3,6 +3,7 @@ declare(strict_types=1);

 namespace Automattic\WooCommerce\Blocks\BlockTypes\ProductCollection;

+use WP_Block;
 use WP_Query;
 use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;
 use Automattic\WooCommerce\Utilities\ArrayUtil;
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterActive.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterActive.php
index de18788a701..96e0ec2d8b8 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterActive.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterActive.php
@@ -4,6 +4,8 @@ declare( strict_types = 1 );

 namespace Automattic\WooCommerce\Blocks\BlockTypes;

+use WP_Block;
+
 /**
  * Product Filter: Active Block.
  */
@@ -66,6 +68,14 @@ final class ProductFilterActive extends AbstractBlock {
 			'storeNamespace' => 'woocommerce/product-filters',
 		);

+		$is_empty            = empty( $removable_items );
+		$product_filters_key = $block->context['productFiltersKey'] ?? '';
+		if ( is_string( $product_filters_key ) && '' !== $product_filters_key ) {
+			$state = wp_interactivity_state( 'woocommerce/product-filters' );
+			$state['filterEmptyStates'][ $product_filters_key ][] = $is_empty;
+			wp_interactivity_state( 'woocommerce/product-filters', $state );
+		}
+
 		$wrapper_attributes = array(
 			'data-wp-interactive'  => 'woocommerce/product-filters',
 			'data-wp-key'          => wp_unique_prefixed_id( $this->get_full_block_name() ),
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterAttribute.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterAttribute.php
index 24f68a915dc..068c018c6d5 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterAttribute.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterAttribute.php
@@ -7,6 +7,7 @@ use Automattic\WooCommerce\Blocks\BlockTypes\ProductCollection\Utils as ProductC
 use Automattic\WooCommerce\Internal\ProductAttributes\VisualAttributeTermMeta;
 use Automattic\WooCommerce\Internal\ProductFilters\FilterDataProvider;
 use Automattic\WooCommerce\Internal\ProductFilters\QueryClauses;
+use WP_Block;

 /**
  * Product Filter: Attribute Block.
@@ -250,7 +251,15 @@ final class ProductFilterAttribute extends AbstractBlock {
 			),
 		);

-		if ( empty( $filter_context['items'] ) ) {
+		$is_empty            = empty( $filter_context['items'] );
+		$product_filters_key = $block->context['productFiltersKey'] ?? '';
+		if ( is_string( $product_filters_key ) && '' !== $product_filters_key ) {
+			$state = wp_interactivity_state( 'woocommerce/product-filters' );
+			$state['filterEmptyStates'][ $product_filters_key ][] = $is_empty;
+			wp_interactivity_state( 'woocommerce/product-filters', $state );
+		}
+
+		if ( $is_empty ) {
 			$wrapper_attributes['hidden'] = true;
 			$wrapper_attributes['class']  = 'wc-block-product-filter--hidden';
 		}
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterPrice.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterPrice.php
index 7b31811cef7..df01e0220df 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterPrice.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterPrice.php
@@ -7,6 +7,7 @@ namespace Automattic\WooCommerce\Blocks\BlockTypes;
 use Automattic\WooCommerce\Blocks\BlockTypes\ProductCollection\Utils as ProductCollectionUtils;
 use Automattic\WooCommerce\Internal\ProductFilters\FilterDataProvider;
 use Automattic\WooCommerce\Internal\ProductFilters\QueryClauses;
+use WP_Block;

 /**
  * Product Filter: Price Block.
@@ -155,7 +156,15 @@ final class ProductFilterPrice extends AbstractBlock {
 			)
 		);

-		if ( $min_range === $max_range || ! $max_range ) {
+		$is_empty            = $min_range === $max_range || ! $max_range;
+		$product_filters_key = $block->context['productFiltersKey'] ?? '';
+		if ( is_string( $product_filters_key ) && '' !== $product_filters_key ) {
+			$state = wp_interactivity_state( 'woocommerce/product-filters' );
+			$state['filterEmptyStates'][ $product_filters_key ][] = $is_empty;
+			wp_interactivity_state( 'woocommerce/product-filters', $state );
+		}
+
+		if ( $is_empty ) {
 			$wrapper_attributes['hidden'] = true;
 			$wrapper_attributes['class']  = 'wc-block-product-filter--hidden';
 			return sprintf(
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterRating.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterRating.php
index aeebec806dc..2971b16fb91 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterRating.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterRating.php
@@ -7,6 +7,7 @@ namespace Automattic\WooCommerce\Blocks\BlockTypes;
 use Automattic\WooCommerce\Blocks\BlockTypes\ProductCollection\Utils as ProductCollectionUtils;
 use Automattic\WooCommerce\Internal\ProductFilters\FilterDataProvider;
 use Automattic\WooCommerce\Internal\ProductFilters\QueryClauses;
+use WP_Block;

 /**
  * Product Filter: Rating Block
@@ -151,7 +152,15 @@ final class ProductFilterRating extends AbstractBlock {
 			),
 		);

-		if ( empty( $filter_options ) ) {
+		$is_empty            = empty( $filter_options );
+		$product_filters_key = $block->context['productFiltersKey'] ?? '';
+		if ( is_string( $product_filters_key ) && '' !== $product_filters_key ) {
+			$state = wp_interactivity_state( 'woocommerce/product-filters' );
+			$state['filterEmptyStates'][ $product_filters_key ][] = $is_empty;
+			wp_interactivity_state( 'woocommerce/product-filters', $state );
+		}
+
+		if ( $is_empty ) {
 			$wrapper_attributes['hidden'] = true;
 			$wrapper_attributes['class']  = 'wc-block-product-filter--hidden';
 		}
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterStatus.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterStatus.php
index 5c9861b44fa..57bcb0d4b2d 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterStatus.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterStatus.php
@@ -5,6 +5,7 @@ namespace Automattic\WooCommerce\Blocks\BlockTypes;
 use Automattic\WooCommerce\Blocks\BlockTypes\ProductCollection\Utils as ProductCollectionUtils;
 use Automattic\WooCommerce\Internal\ProductFilters\FilterDataProvider;
 use Automattic\WooCommerce\Internal\ProductFilters\QueryClauses;
+use WP_Block;

 /**
  * Product Filter: Status Block.
@@ -151,7 +152,15 @@ final class ProductFilterStatus extends AbstractBlock {
 			),
 		);

-		if ( empty( $filter_options ) ) {
+		$is_empty            = empty( $filter_options );
+		$product_filters_key = $block->context['productFiltersKey'] ?? '';
+		if ( is_string( $product_filters_key ) && '' !== $product_filters_key ) {
+			$state = wp_interactivity_state( 'woocommerce/product-filters' );
+			$state['filterEmptyStates'][ $product_filters_key ][] = $is_empty;
+			wp_interactivity_state( 'woocommerce/product-filters', $state );
+		}
+
+		if ( $is_empty ) {
 			$wrapper_attributes['hidden'] = true;
 			$wrapper_attributes['class']  = 'wc-block-product-filter--hidden';
 		}
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterTaxonomy.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterTaxonomy.php
index 9db3982fdd0..df9e6d4dcde 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterTaxonomy.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterTaxonomy.php
@@ -7,6 +7,7 @@ use Automattic\WooCommerce\Blocks\BlockTypes\ProductCollection\Utils as ProductC
 use Automattic\WooCommerce\Internal\ProductFilters\FilterDataProvider;
 use Automattic\WooCommerce\Internal\ProductFilters\QueryClauses;
 use Automattic\WooCommerce\Internal\ProductFilters\TaxonomyHierarchyData;
+use WP_Block;

 /**
  * Product Filter: Taxonomy Block.
@@ -292,7 +293,15 @@ final class ProductFilterTaxonomy extends AbstractBlock {
 			),
 		);

-		if ( empty( $filter_context['items'] ) ) {
+		$is_empty            = empty( $filter_context['items'] );
+		$product_filters_key = $block->context['productFiltersKey'] ?? '';
+		if ( is_string( $product_filters_key ) && '' !== $product_filters_key ) {
+			$state = wp_interactivity_state( 'woocommerce/product-filters' );
+			$state['filterEmptyStates'][ $product_filters_key ][] = $is_empty;
+			wp_interactivity_state( 'woocommerce/product-filters', $state );
+		}
+
+		if ( $is_empty ) {
 			$wrapper_attributes['hidden'] = true;
 			$wrapper_attributes['class']  = 'wc-block-product-filter--hidden';
 		}
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilters.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilters.php
index c654546ea19..cdaf3e120b0 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilters.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilters.php
@@ -195,11 +195,14 @@ CSS;
 			}
 		);

+		$navigation_id         = $this->generate_navigation_id( $block );
+		$product_filters_key   = wp_unique_prefixed_id( $navigation_id . '-' );
 		$block_context         = array_merge(
 			$block->context,
 			array(
-				'filterParams'  => $filter_params,
-				'activeFilters' => $active_filters,
+				'filterParams'      => $filter_params,
+				'activeFilters'     => $active_filters,
+				'productFiltersKey' => $product_filters_key,
 			),
 		);
 		$inner_blocks          = array_reduce(
@@ -221,7 +224,13 @@ CSS;
 		$overlay_mode     = $this->get_overlay_mode( $attributes );
 		$has_overlay      = 'off' !== $overlay_mode;
 		$overlay_position = isset( $attributes['overlayPosition'] ) && 'right' === $attributes['overlayPosition'] ? 'right' : 'left';
-		$wrapper_classes  = array( 'wc-block-product-filters' );
+
+		$state               = wp_interactivity_state( 'woocommerce/product-filters' );
+		$filter_empty_states = $state['filterEmptyStates'][ $product_filters_key ] ?? array();
+		$wrapper_classes     = array( 'wc-block-product-filters' );
+		if ( ! empty( $filter_empty_states ) && ! in_array( false, $filter_empty_states, true ) ) {
+			$wrapper_classes[] = 'wc-block-product-filters--empty';
+		}
 		if ( ! $has_overlay ) {
 			$wrapper_classes[] = 'is-filter-drawer-disabled';
 		}
@@ -234,7 +243,7 @@ CSS;

 		$wrapper_attributes = array(
 			'class'                          => implode( ' ', $wrapper_classes ),
-			'data-wp-router-region'          => $this->generate_navigation_id( $block ),
+			'data-wp-router-region'          => $navigation_id,
 			'data-wp-interactive'            => $this->get_full_block_name(),
 			'data-wp-init---colors'          => 'callbacks.initColors',
 			'data-wp-watch---active-filters' => 'callbacks.syncActiveFiltersWithServer',
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductFilters.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductFilters.php
index 8bef33692aa..80973557b17 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductFilters.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductFilters.php
@@ -15,6 +15,11 @@ use Automattic\WooCommerce\Tests\Blocks\Mocks\AssetDataRegistryMock;
  */
 class ProductFilters extends \WP_UnitTestCase {

+	/**
+	 * Test-only filter block name.
+	 */
+	private const TEST_FILTER_BLOCK = 'woocommerce/test-product-filter-empty-state';
+
 	/**
 	 * Instance of the Product Filters block under test.
 	 *
@@ -36,6 +41,13 @@ class ProductFilters extends \WP_UnitTestCase {
 	 */
 	private $canonical_method;

+	/**
+	 * Reflection method used to invoke the protected render method.
+	 *
+	 * @var \ReflectionMethod
+	 */
+	private $render_method;
+
 	/**
 	 * Set up the test subject and dependencies.
 	 *
@@ -58,6 +70,33 @@ class ProductFilters extends \WP_UnitTestCase {

 		$this->canonical_method = new \ReflectionMethod( ProductFiltersBlock::class, 'get_canonical_url_no_pagination' );
 		$this->canonical_method->setAccessible( true );
+
+		$this->render_method = new \ReflectionMethod( ProductFiltersBlock::class, 'render' );
+		$this->render_method->setAccessible( true );
+
+		register_block_type(
+			self::TEST_FILTER_BLOCK,
+			array(
+				'api_version'     => 3,
+				'uses_context'    => array( 'productFiltersKey' ),
+				'attributes'      => array(
+					'empty' => array(
+						'type'    => 'boolean',
+						'default' => true,
+					),
+				),
+				'render_callback' => static function ( $attributes, $content, $block ) {
+					$product_filters_key = $block->context['productFiltersKey'] ?? '';
+					if ( is_string( $product_filters_key ) && '' !== $product_filters_key ) {
+						$state = wp_interactivity_state( 'woocommerce/product-filters' );
+						$state['filterEmptyStates'][ $product_filters_key ][] = (bool) $attributes['empty'];
+						wp_interactivity_state( 'woocommerce/product-filters', $state );
+					}
+
+					return '<div class="test-product-filter"></div>';
+				},
+			)
+		);
 	}

 	/**
@@ -71,6 +110,11 @@ class ProductFilters extends \WP_UnitTestCase {
 		}

 		$this->registered_filters = [];
+
+		if ( \WP_Block_Type_Registry::get_instance()->is_registered( self::TEST_FILTER_BLOCK ) ) {
+			unregister_block_type( self::TEST_FILTER_BLOCK );
+		}
+
 		parent::tearDown();
 	}

@@ -103,6 +147,98 @@ class ProductFilters extends \WP_UnitTestCase {
 		return (string) $this->canonical_method->invoke( $this->product_filters, $filter_params );
 	}

+	/**
+	 * Render Product Filters with test filter states.
+	 *
+	 * @param bool[] $empty_states Empty-state value for each test filter.
+	 * @return string Rendered Product Filters markup.
+	 */
+	private function render_with_filter_states( array $empty_states ): string {
+		$inner_blocks = array_map(
+			static function ( $is_empty ) {
+				return array(
+					'blockName'    => self::TEST_FILTER_BLOCK,
+					'attrs'        => array( 'empty' => $is_empty ),
+					'innerBlocks'  => array(),
+					'innerHTML'    => '',
+					'innerContent' => array(),
+				);
+			},
+			$empty_states
+		);
+
+		$parsed_block = array(
+			'blockName'    => 'woocommerce/product-filters',
+			'attrs'        => array(),
+			'innerBlocks'  => $inner_blocks,
+			'innerHTML'    => '',
+			'innerContent' => array(),
+		);
+		$block        = new \WP_Block( $parsed_block, array() );
+
+		return (string) $this->render_method->invoke( $this->product_filters, array(), '', $block );
+	}
+
+	/**
+	 * @testdox All participating filters being empty marks the wrapper as empty.
+	 *
+	 * @return void
+	 */
+	public function test_wrapper_has_empty_class_when_all_filters_are_empty(): void {
+		$output = $this->render_with_filter_states( array( true, true ) );
+
+		$this->assertStringContainsString( 'wc-block-product-filters--empty', $output );
+	}
+
+	/**
+	 * @testdox One visible filter keeps the wrapper out of the empty state in either order.
+	 *
+	 * @param bool[] $empty_states Empty-state value for each test filter.
+	 * @return void
+	 *
+	 * @testWith [[true, false]]
+	 *           [[false, true]]
+	 */
+	public function test_wrapper_has_no_empty_class_when_one_filter_has_options( array $empty_states ): void {
+		$output = $this->render_with_filter_states( $empty_states );
+
+		$this->assertStringNotContainsString( 'wc-block-product-filters--empty', $output );
+	}
+
+	/**
+	 * @testdox Empty state is scoped independently for identical and different Product Filters wrappers.
+	 *
+	 * @return void
+	 */
+	public function test_empty_state_is_scoped_per_wrapper(): void {
+		$previous_states        = wp_interactivity_state( 'woocommerce/product-filters' )['filterEmptyStates'] ?? array();
+		$empty_output           = $this->render_with_filter_states( array( true ) );
+		$duplicate_empty_output = $this->render_with_filter_states( array( true ) );
+		$visible_output         = $this->render_with_filter_states( array( false ) );
+
+		$this->assertStringContainsString( 'wc-block-product-filters--empty', $empty_output );
+		$this->assertStringContainsString( 'wc-block-product-filters--empty', $duplicate_empty_output );
+		$this->assertStringNotContainsString( 'wc-block-product-filters--empty', $visible_output );
+
+		$current_states = wp_interactivity_state( 'woocommerce/product-filters' )['filterEmptyStates'];
+		$wrapper_states = array_diff_key( $current_states, $previous_states );
+		$this->assertCount( 3, $wrapper_states );
+		foreach ( $wrapper_states as $empty_states ) {
+			$this->assertCount( 1, $empty_states );
+		}
+	}
+
+	/**
+	 * @testdox A wrapper with no participating filters is not marked empty.
+	 *
+	 * @return void
+	 */
+	public function test_wrapper_without_participating_filters_is_not_empty(): void {
+		$output = $this->render_with_filter_states( array() );
+
+		$this->assertStringNotContainsString( 'wc-block-product-filters--empty', $output );
+	}
+
 	/**
 	 * Ensures get_pagenum_link filters receive the expected argument types.
 	 *