Commit 1c67ffce1f3 for woocommerce

commit 1c67ffce1f35f79091c74bd9dd67862796b7a689
Author: Karol Manijak <20098064+kmanijak@users.noreply.github.com>
Date:   Tue Jun 30 15:06:28 2026 +0200

    Fix Product Collection filter resets (#66022)

    * Fix Product Collection filter resets

    * Add changelog entry for Product Collection reset fix

    * Guard Product Collection tax query updates

diff --git a/plugins/woocommerce/changelog/fix-59238-product-collection-reset-all b/plugins/woocommerce/changelog/fix-59238-product-collection-reset-all
new file mode 100644
index 00000000000..7170b34d783
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-59238-product-collection-reset-all
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent Product Collection filter resets from clearing lifted collection controls.
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/inspector-controls/index.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/inspector-controls/index.tsx
index 1d8376539af..d33f4b3208b 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/inspector-controls/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/inspector-controls/index.tsx
@@ -249,7 +249,7 @@ const ProductCollectionInspectorControls = (
 			{ showCustomQueryControls ? (
 				<ToolsPanel
 					label={ __( 'Filters', 'woocommerce' ) }
-					resetAll={ ( resetAllFilters: ( () => void )[] ) => {
+					resetAll={ ( resetAllFilters = [] ) => {
 						resetAllFilters.forEach( ( resetFilter ) => {
 							resetFilter();
 						} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/inspector-controls/taxonomy-controls/use-taxonomy-controls.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/inspector-controls/taxonomy-controls/use-taxonomy-controls.tsx
index f5454f61abe..ea9207427c2 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/inspector-controls/taxonomy-controls/use-taxonomy-controls.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/inspector-controls/taxonomy-controls/use-taxonomy-controls.tsx
@@ -67,7 +67,6 @@ function useTaxonomyControls( {
 	const createHandleChange = ( slug: string ) => ( newTermIds: number[] ) => {
 		setQueryAttribute( {
 			taxQuery: {
-				...taxQuery,
 				[ slug ]: newTermIds,
 			},
 		} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/taxonomy-picker.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/taxonomy-picker.tsx
index b88ac9c7dc8..581f5d408b3 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/taxonomy-picker.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/edit/taxonomy-picker.tsx
@@ -106,7 +106,6 @@ const TaxonomyPicker = ( props: TaxonomyPickerProps ) => {
 	const handleTermChange = ( termIds: number[] ) => {
 		setQueryAttribute( props, {
 			taxQuery: {
-				...attributes.query?.taxQuery,
 				[ taxonomySlug ]: termIds,
 			},
 		} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/test/query-attributes.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/test/query-attributes.ts
new file mode 100644
index 00000000000..da0cd4190fa
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/test/query-attributes.ts
@@ -0,0 +1,102 @@
+/**
+ * Internal dependencies
+ */
+import { DEFAULT_QUERY } from '../constants';
+import { ProductCollectionQuery } from '../types';
+import { getUpdatedQuery } from '../utils';
+
+const getQuery = (
+	query: Partial< ProductCollectionQuery > = {}
+): ProductCollectionQuery => ( {
+	...DEFAULT_QUERY,
+	...query,
+} );
+
+describe( 'getUpdatedQuery', () => {
+	it( 'updates query attributes', () => {
+		expect(
+			getUpdatedQuery(
+				getQuery( {
+					search: 'shirt',
+					woocommerceOnSale: true,
+				} ),
+				{
+					woocommerceOnSale: false,
+				}
+			)
+		).toEqual(
+			getQuery( {
+				search: 'shirt',
+				woocommerceOnSale: false,
+			} )
+		);
+	} );
+
+	it( 'merges taxQuery updates into the current taxQuery', () => {
+		expect(
+			getUpdatedQuery(
+				getQuery( {
+					taxQuery: {
+						product_cat: [ 1 ],
+						product_tag: [ 2 ],
+					},
+				} ),
+				{
+					taxQuery: {
+						product_tag: [],
+					},
+				}
+			).taxQuery
+		).toEqual( {
+			product_cat: [ 1 ],
+			product_tag: [],
+		} );
+	} );
+
+	it( 'preserves taxQuery when an update does not include taxonomy changes', () => {
+		expect(
+			getUpdatedQuery(
+				getQuery( {
+					taxQuery: {
+						product_cat: [ 1 ],
+						product_tag: [ 2 ],
+					},
+				} ),
+				{
+					woocommerceOnSale: true,
+				}
+			).taxQuery
+		).toEqual( {
+			product_cat: [ 1 ],
+			product_tag: [ 2 ],
+		} );
+	} );
+
+	it( 'preserves taxQuery when a taxonomy update is invalid', () => {
+		const query = getQuery( {
+			taxQuery: {
+				product_cat: [ 1 ],
+				product_tag: [ 2 ],
+			},
+		} );
+
+		expect(
+			getUpdatedQuery( query, {
+				taxQuery: undefined,
+			} ).taxQuery
+		).toEqual( {
+			product_cat: [ 1 ],
+			product_tag: [ 2 ],
+		} );
+
+		expect(
+			getUpdatedQuery( query, {
+				taxQuery:
+					null as unknown as ProductCollectionQuery[ 'taxQuery' ],
+			} ).taxQuery
+		).toEqual( {
+			product_cat: [ 1 ],
+			product_tag: [ 2 ],
+		} );
+	} );
+} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/utils.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/utils.tsx
index 661f3df7cf9..62daf105787 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/utils.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/utils.tsx
@@ -53,17 +53,38 @@ import {
  *
  * Shorthand for setting new nested query parameters.
  */
+export const getUpdatedQuery = (
+	query: ProductCollectionQuery,
+	queryParams: Partial< ProductCollectionQuery >
+): ProductCollectionQuery => {
+	const { taxQuery, ...queryParamsWithoutTaxQuery } = queryParams;
+	const hasTaxQueryUpdates =
+		taxQuery && typeof taxQuery === 'object' && ! Array.isArray( taxQuery );
+
+	return {
+		...query,
+		...queryParamsWithoutTaxQuery,
+		...( hasTaxQueryUpdates && {
+			taxQuery: {
+				...query.taxQuery,
+				...taxQuery,
+			},
+		} ),
+	};
+};
+
 export function setQueryAttribute(
 	block: BlockEditProps< ProductCollectionAttributes >,
 	queryParams: Partial< ProductCollectionQuery >
 ) {
-	const { query } = block.attributes;
+	const currentBlock = select( blockEditorStore ).getBlock( block.clientId );
+	const currentAttributes = currentBlock?.attributes as
+		| ProductCollectionAttributes
+		| undefined;
+	const query = currentAttributes?.query || block.attributes.query;

 	block.setAttributes( {
-		query: {
-			...query,
-			...queryParams,
-		},
+		query: getUpdatedQuery( query, queryParams ),
 	} );
 }