Commit 456580a9490 for woocommerce

commit 456580a94903d43ba9e7cfebe7579137cec1cf5c
Author: Luigi Teschio <gigitux@gmail.com>
Date:   Thu May 21 13:52:18 2026 +0200

    Fix variation table edit links (#65241)

    * Fix variation table edit links

    * Add changelog entry for variation edit link fix

diff --git a/packages/js/experimental-products-app/changelog/fix-variation-parent-edit-link b/packages/js/experimental-products-app/changelog/fix-variation-parent-edit-link
new file mode 100644
index 00000000000..910b36551bf
--- /dev/null
+++ b/packages/js/experimental-products-app/changelog/fix-variation-parent-edit-link
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix variation table edit links to open the parent product.
diff --git a/packages/js/experimental-products-app/src/dataviews-actions/actions.test.tsx b/packages/js/experimental-products-app/src/dataviews-actions/actions.test.tsx
index e23450ea725..1403ff3656e 100644
--- a/packages/js/experimental-products-app/src/dataviews-actions/actions.test.tsx
+++ b/packages/js/experimental-products-app/src/dataviews-actions/actions.test.tsx
@@ -343,6 +343,37 @@ describe( 'product list actions', () => {
 		} );
 	} );

+	it( 'opens the parent product editor when the Edit action is triggered for a variation', () => {
+		const { result } = renderHook( () => useProductActions() );
+		const editProductAction = result.current.find(
+			( action ) => action.id === 'edit-product'
+		);
+
+		expect( editProductAction ).toBeDefined();
+
+		if ( ! editProductAction ) {
+			throw new Error( 'Edit action not found.' );
+		}
+
+		const originalLocation = window.location;
+		Object.defineProperty( window, 'location', {
+			writable: true,
+			value: { href: '' },
+		} );
+
+		getCallbackAction( editProductAction ).callback( [ blueVariation ], {
+			onActionPerformed,
+		} );
+
+		expect( window.location.href ).toBe( 'post.php?post=78&action=edit' );
+		expect( onActionPerformed ).toHaveBeenCalledWith( [ blueVariation ] );
+
+		Object.defineProperty( window, 'location', {
+			writable: true,
+			value: originalLocation,
+		} );
+	} );
+
 	it( 'duplicates products through the WooCommerce duplicate endpoint', async () => {
 		const duplicatedProduct = {
 			...product,
diff --git a/packages/js/experimental-products-app/src/dataviews-actions/actions.tsx b/packages/js/experimental-products-app/src/dataviews-actions/actions.tsx
index c301ceec29a..db160646783 100644
--- a/packages/js/experimental-products-app/src/dataviews-actions/actions.tsx
+++ b/packages/js/experimental-products-app/src/dataviews-actions/actions.tsx
@@ -24,7 +24,10 @@ import { useMemo } from '@wordpress/element';
  */
 import type { ProductEntityRecord } from '../fields/types';
 import { unlock } from '../lock-unlock';
-import { getProductListNavigationPath } from '../product-list/utils';
+import {
+	getProductEditPostId,
+	getProductListNavigationPath,
+} from '../product-list/utils';

 const { useHistory, useLocation } = unlock( routerPrivateApis );

@@ -189,7 +192,7 @@ export const editAction = (): Action< ProductEntityRecord > => ( {
 		if ( product ) {
 			window.location.href = getAdminLink(
 				addQueryArgs( 'post.php', {
-					post: product.id,
+					post: getProductEditPostId( product ),
 					action: 'edit',
 				} )
 			);
diff --git a/packages/js/experimental-products-app/src/product-list/index.tsx b/packages/js/experimental-products-app/src/product-list/index.tsx
index 1a060c51fb1..40c1df7c8ec 100644
--- a/packages/js/experimental-products-app/src/product-list/index.tsx
+++ b/packages/js/experimental-products-app/src/product-list/index.tsx
@@ -28,6 +28,7 @@ import {
 import { productFields } from './fields';
 import {
 	getItemId,
+	getProductEditPostId,
 	getProductListNavigationPath,
 	getProductListTab,
 	getProductsWithEmbeddedVariations,
@@ -318,7 +319,7 @@ export default function ProductList( {
 						{ ...props }
 						href={ getAdminLink(
 							addQueryArgs( 'post.php', {
-								post: item.id,
+								post: getProductEditPostId( item ),
 								action: 'edit',
 							} )
 						) }
diff --git a/packages/js/experimental-products-app/src/product-list/utils.test.ts b/packages/js/experimental-products-app/src/product-list/utils.test.ts
index c9643ecc516..d0614da8d12 100644
--- a/packages/js/experimental-products-app/src/product-list/utils.test.ts
+++ b/packages/js/experimental-products-app/src/product-list/utils.test.ts
@@ -8,6 +8,7 @@ import type { View } from '@wordpress/dataviews';
  */
 import type { ProductEntityRecord } from '../fields/types';
 import {
+	getProductEditPostId,
 	hasActiveProductListSearchOrFilters,
 	getProductListNavigationPath,
 	getProductsWithEmbeddedVariations,
@@ -67,6 +68,20 @@ describe( 'product list utils', () => {
 		} );
 	} );

+	describe( 'getProductEditPostId', () => {
+		it( 'returns the product ID for parent products', () => {
+			expect( getProductEditPostId( createProduct( 1 ) ) ).toBe( 1 );
+		} );
+
+		it( 'returns the parent product ID for variations', () => {
+			expect( getProductEditPostId( createProduct( 2, 1 ) ) ).toBe( 1 );
+		} );
+
+		it( 'falls back to the variation ID when a parent ID is missing', () => {
+			expect( getProductEditPostId( createProduct( 2, 0 ) ) ).toBe( 2 );
+		} );
+	} );
+
 	describe( 'getProductsWithEmbeddedVariations', () => {
 		it( 'adds embedded variations after their parent product', () => {
 			const variation = createProduct( 2, 1 );
diff --git a/packages/js/experimental-products-app/src/product-list/utils.ts b/packages/js/experimental-products-app/src/product-list/utils.ts
index 189d0e38a8c..eabdcb3aee9 100644
--- a/packages/js/experimental-products-app/src/product-list/utils.ts
+++ b/packages/js/experimental-products-app/src/product-list/utils.ts
@@ -33,6 +33,10 @@ export function getItemId( item: ProductEntityRecord ) {
 	return item.id.toString();
 }

+export function getProductEditPostId( item: ProductEntityRecord ) {
+	return item.parent_id && item.parent_id > 0 ? item.parent_id : item.id;
+}
+
 export function getProductsWithEmbeddedVariations(
 	items: ProductEntityRecord[]
 ): ProductEntityRecord[] {