Commit e7b9082fdd0 for woocommerce

commit e7b9082fdd0f77fad08647abf5d8504b2fb31bb5
Author: Amit Raj <77401999+amitraj2203@users.noreply.github.com>
Date:   Sat May 2 01:15:02 2026 +0530

    Migrate Catalog Sorting Block to use iAPI (#62854)

    * Add interactivity support and frontend store logic in catalog sorting block

    * Refactor catalog sorting block to utilize Interactivity API

    * Add changelog file

    * Fix lint error

    * Use `WP HTML Tag Processor` to inject directives

    * Add iAPI related tests for CatalogSorting block

    * Add conditional validation for iAPI

    * Refactor CatalogSorting block to use current window URL for sorting updates

    * Lock the store

    * Use withSyncEvent for preventSubmit and handleSortChange

    * remove client-side navigation checks

    * Remove unused BlocksSharedState import and related code from CatalogSorting class

    ---------

    Co-authored-by: Brandon Kraft <public@brandonkraft.com>

diff --git a/plugins/woocommerce/changelog/60314-migrate-catalog-sorting-block-iapi b/plugins/woocommerce/changelog/60314-migrate-catalog-sorting-block-iapi
new file mode 100644
index 00000000000..44696b8bab1
--- /dev/null
+++ b/plugins/woocommerce/changelog/60314-migrate-catalog-sorting-block-iapi
@@ -0,0 +1,4 @@
+Significance: minor
+Type: enhancement
+
+Migrate the Catalog Sorting block to use Interactivity API
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/catalog-sorting/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/catalog-sorting/block.json
index 93219fc6f51..3085af2d194 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/catalog-sorting/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/catalog-sorting/block.json
@@ -5,9 +5,7 @@
 	"category": "woocommerce",
 	"keywords": [ "WooCommerce" ],
 	"supports": {
-		"interactivity": {
-			"clientNavigation": true
-		},
+		"interactivity": true,
 		"color": {
 			"text": true,
 			"background": false
@@ -16,6 +14,7 @@
 			"fontSize": true
 		}
 	},
+	"viewScriptModule": "woocommerce/catalog-sorting",
 	"attributes": {
 		"fontSize": {
 			"type": "string",
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/catalog-sorting/frontend.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/catalog-sorting/frontend.ts
new file mode 100644
index 00000000000..e77ff5f1b57
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/catalog-sorting/frontend.ts
@@ -0,0 +1,44 @@
+/**
+ * External dependencies
+ */
+import { store, withSyncEvent } from '@wordpress/interactivity';
+
+const BLOCK_NAME = 'woocommerce/catalog-sorting';
+
+const catalogSortingStore = {
+	actions: {
+		/**
+		 * Prevent default form submission.
+		 */
+		preventSubmit: withSyncEvent( ( event: Event ) => {
+			event.preventDefault();
+		} ),
+
+		/**
+		 * Handle sort order change.
+		 */
+		handleSortChange: withSyncEvent( function* ( event: Event ): Generator {
+			// Stop propagation to prevent jQuery handler from seeing the event.
+			event.stopPropagation();
+
+			const target = event.target as HTMLSelectElement;
+			const newOrderBy = target.value;
+
+			// Build URL with updated orderby parameter.
+			const url = new URL( window.location.href );
+
+			url.searchParams.set( 'orderby', newOrderBy );
+			url.searchParams.set( 'paged', '1' );
+
+			// Client-side navigation.
+			const routerModule: typeof import('@wordpress/interactivity-router') =
+				yield import( '@wordpress/interactivity-router' );
+
+			yield routerModule.actions.navigate( url.href );
+		} ),
+	},
+};
+
+store( BLOCK_NAME, catalogSortingStore, {
+	lock: true,
+} );
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/CatalogSorting.php b/plugins/woocommerce/src/Blocks/BlockTypes/CatalogSorting.php
index 71796c57e4e..be39d3c118a 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/CatalogSorting.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/CatalogSorting.php
@@ -35,6 +35,22 @@ class CatalogSorting extends AbstractBlock {
 			return;
 		}

+		// Use WP_HTML_Tag_Processor to inject Interactivity API directives.
+		$processor = new \WP_HTML_Tag_Processor( $catalog_sorting );
+
+		// Find and modify the form element.
+		if ( $processor->next_tag( array( 'tag_name' => 'form' ) ) ) {
+			$processor->set_attribute( 'data-wp-interactive', 'woocommerce/catalog-sorting' );
+			$processor->set_attribute( 'data-wp-on--submit', 'actions.preventSubmit' );
+		}
+
+		// Find and modify the select element.
+		if ( $processor->next_tag( array( 'tag_name' => 'select' ) ) ) {
+			$processor->set_attribute( 'data-wp-on--change', 'actions.handleSortChange' );
+		}
+
+		$catalog_sorting = $processor->get_updated_html();
+
 		$classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes, array(), array( 'extra_classes' ) );
 		$wrapper_attributes = get_block_wrapper_attributes(
 			array(
@@ -57,13 +73,4 @@ class CatalogSorting extends AbstractBlock {
 			$catalog_sorting
 		);
 	}
-
-	/**
-	 * Get the frontend script handle for this block type.
-	 *
-	 * @param string $key Data to get, or default to everything.
-	 */
-	protected function get_block_type_script( $key = null ) {
-		return null;
-	}
 }
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/CatalogSorting.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/CatalogSorting.php
index e1b3703333d..2c9ae0015cb 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/CatalogSorting.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/CatalogSorting.php
@@ -9,9 +9,12 @@ namespace Automattic\WooCommerce\Tests\Blocks\BlockTypes;
  */
 class CatalogSorting extends \WP_UnitTestCase {
 	/**
-	 * Tests that the Catalog Sorting block has the correct font size based on the default style attribute.
+	 * Set up test fixtures.
 	 */
-	public function test_catalog_sorting_has_small_font_size() {
+	public function setUp(): void {
+		parent::setUp();
+
+		// Create a test product and set up loop.
 		$temp_product = \WC_Helper_Product::create_simple_product();
 		$temp_product->set_name( 'Test Product' );
 		$temp_product->save();
@@ -21,8 +24,46 @@ class CatalogSorting extends \WP_UnitTestCase {
 		wc_set_loop_prop( 'total', 1 );
 		wc_set_loop_prop( 'per_page', 1 );
 		wc_set_loop_prop( 'current_page', 1 );
+	}

+	/**
+	 * Tests that the Catalog Sorting block has the correct font size based on the default style attribute.
+	 */
+	public function test_catalog_sorting_has_small_font_size() {
 		$markup = do_blocks( '<!-- wp:woocommerce/catalog-sorting /-->' );
 		$this->assertStringContainsString( 'has-small-font-size', $markup, 'The Catalog Sorting block has the correct font size.' );
 	}
+
+	/**
+	 * Tests that Interactivity API directive is added to the form element.
+	 */
+	public function test_form_has_interactive_directive() {
+		$markup = do_blocks( '<!-- wp:woocommerce/catalog-sorting /-->' );
+		$this->assertStringContainsString( 'data-wp-interactive="woocommerce/catalog-sorting"', $markup, 'Form should have data-wp-interactive directive.' );
+	}
+
+	/**
+	 * Tests that form submit prevention directive is added to the form element.
+	 */
+	public function test_form_has_submit_prevention_directive() {
+		$markup = do_blocks( '<!-- wp:woocommerce/catalog-sorting /-->' );
+		$this->assertStringContainsString( 'data-wp-on--submit="actions.preventSubmit"', $markup, 'Form should have submit prevention directive.' );
+	}
+
+	/**
+	 * Tests that change handler directive is added to the select element.
+	 */
+	public function test_select_has_change_handler_directive() {
+		$markup = do_blocks( '<!-- wp:woocommerce/catalog-sorting /-->' );
+		$this->assertStringContainsString( 'data-wp-on--change="actions.handleSortChange"', $markup, 'Select should have change handler directive.' );
+	}
+
+	/**
+	 * Tests that the block renders without errors when no products exist.
+	 */
+	public function test_renders_empty_when_no_pagination() {
+		wc_set_loop_prop( 'is_paginated', false );
+		$markup = do_blocks( '<!-- wp:woocommerce/catalog-sorting /-->' );
+		$this->assertEmpty( trim( $markup ), 'Block should not render when pagination is disabled.' );
+	}
 }