Commit 74237435479 for woocommerce

commit 742374354799e45207938b5e0459201a4ec0c0ea
Author: verofasulo <98944206+verofasulo@users.noreply.github.com>
Date:   Wed May 20 14:51:17 2026 +0200

    Tailor variation row actions in the experimental products app (#65199)

    Tailor variation row actions: hide duplicate, swap trash for permanent delete

diff --git a/packages/js/experimental-products-app/changelog/update-products-app-variation-row-actions b/packages/js/experimental-products-app/changelog/update-products-app-variation-row-actions
new file mode 100644
index 00000000000..a3730a2fc00
--- /dev/null
+++ b/packages/js/experimental-products-app/changelog/update-products-app-variation-row-actions
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Tailor the row actions on variation rows in the experimental products app product list: keep Quick edit and Edit, hide Duplicate, and replace Move to trash with Permanently delete (using the existing delete-confirmation modal).
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 13dcf5daa8a..e23450ea725 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
@@ -505,10 +505,15 @@ describe( 'product list actions', () => {
 		): ActionModal< ProductEntityRecord > =>
 			action as ActionModal< ProductEntityRecord >;

-		it( 'is eligible only for trashed products', () => {
+		it( 'is eligible for trashed products and for any variation', () => {
 			const action = permanentlyDeleteAction();
+			const variation = {
+				...product,
+				type: 'variation',
+			} as ProductEntityRecord;

 			expect( action.isEligible?.( trashedProduct ) ).toBe( true );
+			expect( action.isEligible?.( variation ) ).toBe( true );
 			expect( action.isEligible?.( product ) ).toBe( false );
 		} );

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 94a7153ddbc..dd7a24cb5ea 100644
--- a/packages/js/experimental-products-app/src/dataviews-actions/actions.tsx
+++ b/packages/js/experimental-products-app/src/dataviews-actions/actions.tsx
@@ -333,7 +333,10 @@ export const duplicateProductAction = (): Action< ProductEntityRecord > => ( {
 	supportsBulk: true,
 	isEligible( item ) {
 		return (
-			!! item && item.status !== 'trash' && item.status !== 'auto-draft'
+			!! item &&
+			item.status !== 'trash' &&
+			item.status !== 'auto-draft' &&
+			item.type !== 'variation'
 		);
 	},
 	async callback( items, { onActionPerformed } ) {
@@ -360,7 +363,10 @@ export const moveToTrashAction = (): Action< ProductEntityRecord > => ( {
 	supportsBulk: true,
 	icon: trash,
 	isEligible( product ) {
-		return product.status !== 'trash';
+		// Variations skip the trash and go straight to permanent delete
+		// (see `permanentlyDeleteAction`), since the variations REST endpoint
+		// doesn't support a soft-trash state.
+		return product.status !== 'trash' && product.type !== 'variation';
 	},
 	async callback( items, { onActionPerformed } ) {
 		const { deleteEntityRecord } = dispatch( coreStore );
@@ -483,7 +489,9 @@ export const permanentlyDeleteAction = (): Action< ProductEntityRecord > => ( {
 	supportsBulk: true,
 	icon: trash,
 	isEligible( product ) {
-		return product.status === 'trash';
+		// Variations are deleted directly (no trash step), so show this
+		// action for them regardless of status.
+		return product.status === 'trash' || product.type === 'variation';
 	},
 	modalHeader: ( items ) =>
 		items.length === 1