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