Commit b01d5af8c74 for woocommerce
commit b01d5af8c7487245612385670ff3de366950fb86
Author: Luigi Teschio <gigitux@gmail.com>
Date: Wed May 13 10:14:59 2026 +0200
Update quick edit field ordering (#64812)
* show right fields bulk editing
* Update quick edit field ordering
* Add changefile(s) from automation for the following project(s): @woocommerce/experimental-products-app
---------
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
diff --git a/packages/js/experimental-products-app/changelog/64812-fix-bulk-editing-field b/packages/js/experimental-products-app/changelog/64812-fix-bulk-editing-field
new file mode 100644
index 00000000000..60cbe154750
--- /dev/null
+++ b/packages/js/experimental-products-app/changelog/64812-fix-bulk-editing-field
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Ensure product quick edit shows the correct fields in the expected order.
\ No newline at end of file
diff --git a/packages/js/experimental-products-app/src/fields/components/dimension.tsx b/packages/js/experimental-products-app/src/fields/components/dimension.tsx
index 4abcb6249db..1a42f1016c1 100644
--- a/packages/js/experimental-products-app/src/fields/components/dimension.tsx
+++ b/packages/js/experimental-products-app/src/fields/components/dimension.tsx
@@ -17,6 +17,7 @@ export const createDimensionField = (
key: DimensionKey
): Partial< Field< ProductEntityRecord > > => {
return {
+ isVisible: ( item ) => ! item.virtual,
Edit: ( { data, onChange, field } ) => {
const {
record: storeProductsSettings,
diff --git a/packages/js/experimental-products-app/src/fields/downloadable/field.tsx b/packages/js/experimental-products-app/src/fields/downloadable/field.tsx
index 3774bad3711..e5f9b2751dd 100644
--- a/packages/js/experimental-products-app/src/fields/downloadable/field.tsx
+++ b/packages/js/experimental-products-app/src/fields/downloadable/field.tsx
@@ -37,7 +37,8 @@ const fieldDefinition = {
export const fieldExtensions: Partial< Field< ProductEntityRecord > > = {
...fieldDefinition,
type: 'boolean',
- isVisible: ( item ) => item.downloadable === true,
+ isVisible: ( item ) =>
+ item.downloadable === true && item.type !== 'variable',
getValue: ( { item } ) => item.downloadable,
Edit: ( { data, onChange } ) => {
const downloads = ( data.downloads ?? [] ) as Array<
diff --git a/packages/js/experimental-products-app/src/fields/registry.tsx b/packages/js/experimental-products-app/src/fields/registry.tsx
index 21134f6d03a..cd3782d0ad6 100644
--- a/packages/js/experimental-products-app/src/fields/registry.tsx
+++ b/packages/js/experimental-products-app/src/fields/registry.tsx
@@ -64,11 +64,11 @@ export const PRODUCT_FIELD_IDS = [
'sku',
'price',
'regular_price',
+ 'on_sale',
'sale_price',
'schedule_sale',
'date_on_sale_from',
'date_on_sale_to',
- 'on_sale',
'price_summary',
'stock',
'stock_quantity',
diff --git a/packages/js/experimental-products-app/src/fields/shipping_class/field.tsx b/packages/js/experimental-products-app/src/fields/shipping_class/field.tsx
index 4e174986834..389700c3470 100644
--- a/packages/js/experimental-products-app/src/fields/shipping_class/field.tsx
+++ b/packages/js/experimental-products-app/src/fields/shipping_class/field.tsx
@@ -34,6 +34,7 @@ export const fieldExtensions: Partial< Field< ProductEntityRecord > > = {
label: __( 'Shipping Class', 'woocommerce' ),
enableSorting: false,
type: 'text',
+ isVisible: ( item ) => ! item.virtual,
getValue: ( { item } ) => item.shipping_class,
Edit: ( { data, onChange, field } ) => {
const { shippingClasses } = useSelect( ( select ) => {
diff --git a/packages/js/experimental-products-app/src/fields/weight/field.tsx b/packages/js/experimental-products-app/src/fields/weight/field.tsx
index ec53e70ff5c..f4473271a15 100644
--- a/packages/js/experimental-products-app/src/fields/weight/field.tsx
+++ b/packages/js/experimental-products-app/src/fields/weight/field.tsx
@@ -23,6 +23,7 @@ const fieldDefinition = {
export const fieldExtensions: Partial< Field< ProductEntityRecord > > = {
...fieldDefinition,
label: __( 'Weight', 'woocommerce' ),
+ isVisible: ( item ) => ! item.virtual,
Edit: ( { data, onChange, field } ) => {
const {
record: storeProductsSettings,
diff --git a/packages/js/experimental-products-app/src/product-edit/utils.test.ts b/packages/js/experimental-products-app/src/product-edit/utils.test.ts
index aeeef708fbb..70ede4ed98c 100644
--- a/packages/js/experimental-products-app/src/product-edit/utils.test.ts
+++ b/packages/js/experimental-products-app/src/product-edit/utils.test.ts
@@ -234,6 +234,20 @@ describe( 'product edit utils', () => {
expect( fieldIds ).not.toContain( fieldId );
} );
};
+ const expectFieldOrder = (
+ fieldIds: string[],
+ orderedFieldIds: string[]
+ ) => {
+ orderedFieldIds.forEach( ( fieldId, index ) => {
+ const previousFieldId = orderedFieldIds[ index - 1 ];
+
+ if ( previousFieldId ) {
+ expect( fieldIds.indexOf( previousFieldId ) ).toBeLessThan(
+ fieldIds.indexOf( fieldId )
+ );
+ }
+ } );
+ };
const parentOwnedFieldIds = [
'name',
'short_description',
@@ -241,6 +255,7 @@ describe( 'product edit utils', () => {
'product_status',
'catalog_visibility',
'categories',
+ 'brands',
'tags',
'type',
'featured',
@@ -258,23 +273,26 @@ describe( 'product edit utils', () => {
'date_on_sale_from',
'date_on_sale_to',
];
- const universalFieldIds = [
- 'images',
- 'sku',
- 'manage_stock',
- 'stock_quantity',
+ const basePriceFieldIds = [ 'regular_price', 'on_sale' ];
+ const managedStockFieldIds = [ 'manage_stock', 'stock_quantity' ];
+ const stockStatusFieldIds = [ 'stock', 'manage_stock' ];
+ const shippingFieldIds = [
'weight',
'length',
'width',
'height',
'shipping_class',
- 'tax_status',
];
- const bulkUniversalFieldIds = universalFieldIds.filter(
+ const sellableInstanceFieldIds = [
+ 'images',
+ 'sku',
+ ...managedStockFieldIds,
+ ];
+ const bulkSellableInstanceFieldIds = sellableInstanceFieldIds.filter(
( fieldId ) => fieldId !== 'sku'
);
- it( 'shows pricing, shipping, and linked product fields for simple physical products', () => {
+ it( 'shows simple product fields in quick edit order', () => {
const fieldIds = getVisibleFieldIds( [
buildProduct( {
type: 'simple',
@@ -286,24 +304,35 @@ describe( 'product edit utils', () => {
} ),
] );
- expect( fieldIds ).toEqual(
- expect.arrayContaining( [
- 'price',
- 'regular_price',
- 'on_sale',
- 'sale_price',
- 'schedule_sale',
- 'date_on_sale_from',
- 'weight',
- 'length',
- 'width',
- 'height',
- 'shipping_class',
- 'upsell_ids',
- 'cross_sell_ids',
- ] )
- );
- expectFieldsHidden( fieldIds, [ 'external_url', 'button_text' ] );
+ expect( fieldIds ).toEqual( [
+ 'name',
+ 'product_status',
+ 'catalog_visibility',
+ 'regular_price',
+ 'on_sale',
+ 'sale_price',
+ 'images',
+ 'sku',
+ 'stock',
+ 'manage_stock',
+ 'categories',
+ 'brands',
+ 'tags',
+ ] );
+ expectFieldsHidden( fieldIds, [
+ 'price',
+ 'schedule_sale',
+ 'date_on_sale_from',
+ 'date_on_sale_to',
+ 'downloadable',
+ 'external_url',
+ 'button_text',
+ ...shippingFieldIds,
+ 'tax_status',
+ 'upsell_ids',
+ 'cross_sell_ids',
+ 'featured',
+ ] );
} );
it( 'does not include excluded fields in product type compatibility', () => {
@@ -318,7 +347,24 @@ describe( 'product edit utils', () => {
);
} );
- it( 'uses the same compatible fields for simple products regardless of virtual status', () => {
+ it( 'orders pricing fields for the quick edit form', () => {
+ const fieldIds = getVisibleFieldIds( [
+ buildProduct( {
+ type: 'simple',
+ on_sale: true,
+ sale_price: '12',
+ date_on_sale_from: '2026-05-06T00:00:00',
+ } ),
+ ] );
+
+ expectFieldOrder( fieldIds, [
+ 'regular_price',
+ 'on_sale',
+ 'sale_price',
+ ] );
+ } );
+
+ it( 'hides shipping fields for virtual simple products', () => {
const fieldIds = getVisibleFieldIds( [
buildProduct( {
type: 'simple',
@@ -329,18 +375,19 @@ describe( 'product edit utils', () => {
expect( fieldIds ).toEqual(
expect.arrayContaining( [
- 'price',
'regular_price',
- 'weight',
- 'length',
- 'width',
- 'height',
- 'shipping_class',
- 'upsell_ids',
- 'cross_sell_ids',
+ 'categories',
+ 'brands',
+ 'tags',
] )
);
- expectFieldsHidden( fieldIds, [ 'external_url', 'button_text' ] );
+ expectFieldsHidden( fieldIds, [
+ ...shippingFieldIds,
+ 'external_url',
+ 'button_text',
+ 'upsell_ids',
+ 'cross_sell_ids',
+ ] );
} );
it( 'shows downloads for simple downloadable products', () => {
@@ -353,15 +400,40 @@ describe( 'product edit utils', () => {
] );
expect( fieldIds ).toContain( 'downloadable' );
- expect( fieldIds ).toEqual(
- expect.arrayContaining( [
- 'weight',
- 'length',
- 'width',
- 'height',
- 'shipping_class',
- ] )
- );
+ expectFieldOrder( fieldIds, [ 'images', 'downloadable', 'sku' ] );
+ expectFieldsHidden( fieldIds, shippingFieldIds );
+ } );
+
+ it( 'shows grouped product fields in quick edit order', () => {
+ const fieldIds = getVisibleFieldIds( [
+ buildProduct( {
+ type: 'grouped',
+ } ),
+ ] );
+
+ expect( fieldIds ).toEqual( [
+ 'name',
+ 'product_status',
+ 'catalog_visibility',
+ 'upsell_ids',
+ 'images',
+ 'sku',
+ 'categories',
+ 'brands',
+ 'tags',
+ 'featured',
+ ] );
+ expectFieldsHidden( fieldIds, [
+ ...priceFieldIds,
+ 'downloadable',
+ 'cross_sell_ids',
+ 'external_url',
+ 'button_text',
+ ...shippingFieldIds,
+ ...stockStatusFieldIds,
+ 'stock_quantity',
+ 'tax_status',
+ ] );
} );
it( 'shows external fields for external products', () => {
@@ -371,23 +443,33 @@ describe( 'product edit utils', () => {
} ),
] );
- expect( fieldIds ).toEqual(
- expect.arrayContaining( [
- 'price',
- 'regular_price',
- 'external_url',
- 'button_text',
- 'upsell_ids',
- ] )
- );
+ expect( fieldIds ).toEqual( [
+ 'name',
+ 'product_status',
+ 'catalog_visibility',
+ 'regular_price',
+ 'on_sale',
+ 'images',
+ 'external_url',
+ 'button_text',
+ 'sku',
+ 'categories',
+ 'brands',
+ 'tags',
+ 'featured',
+ ] );
expectFieldsHidden( fieldIds, [
+ 'price',
+ 'sale_price',
+ 'schedule_sale',
+ 'date_on_sale_from',
+ 'date_on_sale_to',
'cross_sell_ids',
'downloadable',
- 'weight',
- 'length',
- 'width',
- 'height',
- 'shipping_class',
+ 'upsell_ids',
+ ...shippingFieldIds,
+ ...stockStatusFieldIds,
+ 'stock_quantity',
] );
} );
@@ -395,7 +477,6 @@ describe( 'product edit utils', () => {
const fieldIds = getVisibleFieldIds( [
buildProduct( {
type: 'variable',
- manage_stock: true,
} ),
] );
@@ -411,11 +492,16 @@ describe( 'product edit utils', () => {
] );
expect( fieldIds ).toEqual(
expect.arrayContaining( [
+ 'sku',
'upsell_ids',
'cross_sell_ids',
- ...universalFieldIds,
+ 'stock',
+ 'manage_stock',
+ ...shippingFieldIds,
+ 'tax_status',
] )
);
+ expectFieldsHidden( fieldIds, [ 'stock_quantity' ] );
} );
it( 'shows parent-owned and universal fields for simple and variable products', () => {
@@ -443,33 +529,46 @@ describe( 'product edit utils', () => {
'catalog_visibility',
'categories',
'tags',
- 'featured',
- 'upsell_ids',
- 'cross_sell_ids',
- ...bulkUniversalFieldIds,
+ ...bulkSellableInstanceFieldIds,
] )
);
+ expectFieldsHidden( fieldIds, [
+ 'featured',
+ 'upsell_ids',
+ 'cross_sell_ids',
+ ...shippingFieldIds,
+ 'tax_status',
+ ] );
} );
- it( 'shows price but not SKU when bulk editing simple products', () => {
+ it( 'shows sale fields but not SKU when bulk editing simple products', () => {
const fieldIds = getVisibleFieldIds( [
buildProduct( {
id: 1,
type: 'simple',
regular_price: '12',
price: '12',
+ on_sale: false,
} ),
buildProduct( {
id: 2,
type: 'simple',
regular_price: '15',
price: '15',
+ on_sale: true,
+ sale_price: '12',
} ),
] );
expect( fieldIds ).toEqual(
- expect.arrayContaining( [ 'price', 'regular_price' ] )
+ expect.arrayContaining( basePriceFieldIds )
);
+ expectFieldsHidden( fieldIds, [
+ 'sale_price',
+ 'schedule_sale',
+ 'date_on_sale_from',
+ 'date_on_sale_to',
+ ] );
expectFieldsHidden( fieldIds, [ 'sku' ] );
} );
@@ -488,16 +587,77 @@ describe( 'product edit utils', () => {
expect( fieldIds ).toEqual(
expect.arrayContaining( [
- ...priceFieldIds,
- ...universalFieldIds,
+ 'regular_price',
+ 'on_sale',
+ 'sale_price',
+ 'images',
+ 'sku',
+ 'manage_stock',
+ 'stock_quantity',
] )
);
expectFieldsHidden( fieldIds, [
...parentOwnedFieldIds,
+ 'stock',
'downloadable',
+ 'price',
+ 'schedule_sale',
+ 'date_on_sale_from',
+ 'date_on_sale_to',
+ ...shippingFieldIds,
+ 'tax_status',
] );
} );
+ it( 'computes variation fields from parent IDs even when type differs', () => {
+ const fieldIds = getVisibleFieldIds( [
+ buildProduct( {
+ id: 34,
+ parent_id: 12,
+ type: 'simple',
+ on_sale: true,
+ sale_price: '12',
+ date_on_sale_from: '2026-05-06T00:00:00',
+ } ),
+ ] );
+
+ expect( fieldIds ).toEqual(
+ expect.arrayContaining( [
+ 'images',
+ 'sku',
+ 'regular_price',
+ 'on_sale',
+ 'sale_price',
+ 'stock',
+ 'manage_stock',
+ ] )
+ );
+ expectFieldsHidden( fieldIds, parentOwnedFieldIds );
+ expectFieldsHidden( fieldIds, [
+ 'price',
+ 'schedule_sale',
+ 'date_on_sale_from',
+ 'date_on_sale_to',
+ ...shippingFieldIds,
+ 'tax_status',
+ ] );
+ } );
+
+ it( 'shows downloads and hides shipping for virtual downloadable variations', () => {
+ const fieldIds = getVisibleFieldIds( [
+ buildProduct( {
+ id: 34,
+ parent_id: 12,
+ type: 'variation',
+ virtual: true,
+ downloadable: true,
+ } ),
+ ] );
+
+ expect( fieldIds ).toContain( 'downloadable' );
+ expectFieldsHidden( fieldIds, shippingFieldIds );
+ } );
+
it( 'shows shared sellable instance fields for simple products and variations', () => {
const fieldIds = getVisibleFieldIds( [
buildProduct( {
@@ -521,17 +681,89 @@ describe( 'product edit utils', () => {
expect( fieldIds ).toEqual(
expect.arrayContaining( [
- ...priceFieldIds,
- ...bulkUniversalFieldIds,
+ 'regular_price',
+ 'on_sale',
+ 'sale_price',
+ ...bulkSellableInstanceFieldIds,
] )
);
expectFieldsHidden( fieldIds, [
...parentOwnedFieldIds,
'downloadable',
'sku',
+ 'price',
+ 'schedule_sale',
+ 'date_on_sale_from',
+ 'date_on_sale_to',
] );
} );
+ it( 'hides shipping fields when a bulk variation selection includes virtual items', () => {
+ const fieldIds = getVisibleFieldIds( [
+ buildProduct( {
+ id: 1,
+ type: 'simple',
+ virtual: false,
+ manage_stock: true,
+ } ),
+ buildProduct( {
+ id: 34,
+ parent_id: 12,
+ type: 'variation',
+ virtual: true,
+ manage_stock: true,
+ } ),
+ ] );
+
+ expect( fieldIds ).toEqual(
+ expect.arrayContaining( [
+ ...basePriceFieldIds,
+ 'images',
+ ...managedStockFieldIds,
+ ] )
+ );
+ expectFieldsHidden( fieldIds, [
+ ...shippingFieldIds,
+ 'tax_status',
+ 'sku',
+ ] );
+ } );
+
+ it( 'shows downloadable fields when every bulk sellable item is downloadable', () => {
+ const fieldIds = getVisibleFieldIds( [
+ buildProduct( {
+ id: 1,
+ type: 'simple',
+ downloadable: true,
+ } ),
+ buildProduct( {
+ id: 34,
+ parent_id: 12,
+ type: 'variation',
+ downloadable: true,
+ } ),
+ ] );
+
+ expect( fieldIds ).toContain( 'downloadable' );
+ } );
+
+ it( 'hides downloadable fields unless every bulk item supports downloads', () => {
+ const fieldIds = getVisibleFieldIds( [
+ buildProduct( {
+ id: 1,
+ type: 'simple',
+ downloadable: true,
+ } ),
+ buildProduct( {
+ id: 2,
+ type: 'simple',
+ downloadable: false,
+ } ),
+ ] );
+
+ expectFieldsHidden( fieldIds, [ 'downloadable' ] );
+ } );
+
it( 'shows only universal fields for variable products and variations', () => {
const fieldIds = getVisibleFieldIds( [
buildProduct( {
@@ -551,13 +783,15 @@ describe( 'product edit utils', () => {
] );
expect( fieldIds ).toEqual(
- expect.arrayContaining( bulkUniversalFieldIds )
+ expect.arrayContaining( [ ...managedStockFieldIds ] )
);
expectFieldsHidden( fieldIds, [
...parentOwnedFieldIds,
...priceFieldIds,
'downloadable',
'sku',
+ ...shippingFieldIds,
+ 'tax_status',
] );
} );
@@ -588,13 +822,15 @@ describe( 'product edit utils', () => {
] );
expect( fieldIds ).toEqual(
- expect.arrayContaining( bulkUniversalFieldIds )
+ expect.arrayContaining( [ 'images', ...managedStockFieldIds ] )
);
expectFieldsHidden( fieldIds, [
...parentOwnedFieldIds,
...priceFieldIds,
'downloadable',
'sku',
+ ...shippingFieldIds,
+ 'tax_status',
] );
} );
diff --git a/packages/js/experimental-products-app/src/product-edit/utils.ts b/packages/js/experimental-products-app/src/product-edit/utils.ts
index badf7158313..683e5807bb2 100644
--- a/packages/js/experimental-products-app/src/product-edit/utils.ts
+++ b/packages/js/experimental-products-app/src/product-edit/utils.ts
@@ -50,6 +50,7 @@ const PRODUCT_EDIT_FIELD_IDS = [
'manage_stock',
'inventory_summary',
'categories',
+ 'brands',
'tags',
'organization_summary',
'type',
@@ -72,66 +73,31 @@ const PRODUCT_EDIT_FIELD_IDS = [
'linked_products_count',
] as const;
-const COMMON_PRODUCT_EDIT_FIELD_IDS = [
+const SIMPLE_PRODUCT_EDIT_FIELD_IDS = [
'name',
- 'short_description',
- 'description',
- 'images',
'product_status',
- 'sku',
- 'categories',
- 'tags',
- 'featured',
'catalog_visibility',
- 'upsell_ids',
-] satisfies ProductEditFieldId[];
-
-const SIMPLE_PRODUCT_EDIT_FIELD_IDS = [
- ...COMMON_PRODUCT_EDIT_FIELD_IDS,
- 'price',
'regular_price',
'on_sale',
'sale_price',
- 'schedule_sale',
- 'date_on_sale_from',
- 'date_on_sale_to',
- 'stock',
- 'stock_quantity',
- 'manage_stock',
+ 'images',
'downloadable',
- 'weight',
- 'length',
- 'width',
- 'height',
- 'shipping_class',
- 'tax_status',
- 'cross_sell_ids',
-] satisfies ProductEditFieldId[];
-
-const VARIABLE_PRODUCT_EDIT_FIELD_IDS = [
- ...COMMON_PRODUCT_EDIT_FIELD_IDS,
+ 'sku',
'stock',
- 'stock_quantity',
'manage_stock',
- 'weight',
- 'length',
- 'width',
- 'height',
- 'shipping_class',
- 'tax_status',
- 'cross_sell_ids',
+ 'stock_quantity',
+ 'categories',
+ 'brands',
+ 'tags',
] satisfies ProductEditFieldId[];
-const VARIATION_PRODUCT_EDIT_FIELD_IDS = [
+const VARIABLE_PRODUCT_EDIT_FIELD_IDS = [
+ 'name',
+ 'short_description',
+ 'description',
'images',
+ 'product_status',
'sku',
- 'price',
- 'regular_price',
- 'on_sale',
- 'sale_price',
- 'schedule_sale',
- 'date_on_sale_from',
- 'date_on_sale_to',
'stock',
'stock_quantity',
'manage_stock',
@@ -141,33 +107,53 @@ const VARIATION_PRODUCT_EDIT_FIELD_IDS = [
'height',
'shipping_class',
'tax_status',
+ 'categories',
+ 'tags',
+ 'featured',
+ 'catalog_visibility',
+ 'upsell_ids',
+ 'cross_sell_ids',
] satisfies ProductEditFieldId[];
const EXTERNAL_PRODUCT_EDIT_FIELD_IDS = [
- ...COMMON_PRODUCT_EDIT_FIELD_IDS,
- 'price',
+ 'name',
+ 'product_status',
+ 'catalog_visibility',
'regular_price',
'on_sale',
'sale_price',
- 'schedule_sale',
- 'date_on_sale_from',
- 'date_on_sale_to',
+ 'images',
'external_url',
'button_text',
- 'tax_status',
+ 'sku',
+ 'categories',
+ 'brands',
+ 'tags',
+ 'featured',
] satisfies ProductEditFieldId[];
const GROUPED_PRODUCT_EDIT_FIELD_IDS = [
- ...COMMON_PRODUCT_EDIT_FIELD_IDS,
-] as const;
+ 'name',
+ 'product_status',
+ 'catalog_visibility',
+ 'upsell_ids',
+ 'images',
+ 'sku',
+ 'categories',
+ 'brands',
+ 'tags',
+ 'featured',
+] satisfies ProductEditFieldId[];
const PRODUCT_TYPE_COMPATIBLE_FIELD_IDS = {
simple: SIMPLE_PRODUCT_EDIT_FIELD_IDS,
variable: VARIABLE_PRODUCT_EDIT_FIELD_IDS,
- variation: VARIATION_PRODUCT_EDIT_FIELD_IDS,
grouped: GROUPED_PRODUCT_EDIT_FIELD_IDS,
external: EXTERNAL_PRODUCT_EDIT_FIELD_IDS,
-} satisfies Record< string, readonly ProductEditFieldId[] >;
+} satisfies Record<
+ 'simple' | 'variable' | 'grouped' | 'external',
+ readonly ProductEditFieldId[]
+>;
const PARENT_OWNED_PRODUCT_EDIT_FIELD_ID_SET = new Set< ProductEditFieldId >( [
'name',
@@ -176,6 +162,7 @@ const PARENT_OWNED_PRODUCT_EDIT_FIELD_ID_SET = new Set< ProductEditFieldId >( [
'product_status',
'catalog_visibility',
'categories',
+ 'brands',
'tags',
'type',
'featured',
@@ -222,28 +209,6 @@ function getMixedValueFallback( sample: unknown ) {
return undefined;
}
-function getFieldValue( field: ProductField, item: ProductEntityRecord ) {
- if ( typeof field.getValue === 'function' ) {
- return field.getValue( {
- item,
- } );
- }
-
- return item[ field.id as keyof ProductEntityRecord ];
-}
-
-function getProductTypeCompatibleFieldIds( product: ProductEntityRecord ) {
- const productType = product.type;
-
- if ( productType && productType in PRODUCT_TYPE_COMPATIBLE_FIELD_IDS ) {
- return PRODUCT_TYPE_COMPATIBLE_FIELD_IDS[
- productType as keyof typeof PRODUCT_TYPE_COMPATIBLE_FIELD_IDS
- ];
- }
-
- return COMMON_PRODUCT_EDIT_FIELD_IDS;
-}
-
function isVariableProductParent( product: ProductEntityRecord ) {
return product.type === 'variable' && ! product.parent_id;
}
@@ -254,6 +219,19 @@ export function isProductVariation(
return product.type === 'variation' || Boolean( product.parent_id );
}
+function getProductTypeCompatibleFieldIds(
+ product: ProductEntityRecord
+): readonly ProductEditFieldId[] {
+ const productType =
+ product.type === 'variable' ||
+ product.type === 'grouped' ||
+ product.type === 'external'
+ ? product.type
+ : 'simple';
+
+ return PRODUCT_TYPE_COMPATIBLE_FIELD_IDS[ productType ];
+}
+
function isFieldVisibleForProductRelationships(
fieldId: string,
products: ProductEntityRecord[]
@@ -343,27 +321,20 @@ function getCommonProductTypeCompatibleFieldIds(
products: ProductEntityRecord[]
) {
if ( products.length === 0 ) {
- return new Set< string >();
+ return [];
}
const [ firstProduct, ...remainingProducts ] = products;
- const commonFieldIds = new Set(
- getProductTypeCompatibleFieldIds( firstProduct )
+ const remainingCompatibleFieldIdSets = remainingProducts.map(
+ ( product ) => new Set( getProductTypeCompatibleFieldIds( product ) )
);
- remainingProducts.forEach( ( product ) => {
- const compatibleFieldIds = new Set(
- getProductTypeCompatibleFieldIds( product )
- );
-
- commonFieldIds.forEach( ( fieldId ) => {
- if ( ! compatibleFieldIds.has( fieldId ) ) {
- commonFieldIds.delete( fieldId );
- }
- } );
- } );
-
- return commonFieldIds;
+ return getProductTypeCompatibleFieldIds( firstProduct ).filter(
+ ( fieldId ) =>
+ remainingCompatibleFieldIdSets.every( ( compatibleFieldIds ) =>
+ compatibleFieldIds.has( fieldId )
+ )
+ );
}
export function getProductEditFields( fields: ProductField[] ): ProductField[] {
@@ -407,31 +378,6 @@ export function buildMergedProductEditData(
return mergedData as ProductEntityRecord;
}
-export function getMixedProductEditFieldIds(
- fields: ProductField[],
- products: ProductEntityRecord[]
-) {
- if ( products.length <= 1 ) {
- return [];
- }
-
- return fields.reduce< string[] >( ( mixedFields, field ) => {
- const values = products.map( ( product ) =>
- getFieldValue( field, product )
- );
- const isMixed = values.some(
- ( value ) =>
- normalizeValue( value ) !== normalizeValue( values[ 0 ] )
- );
-
- if ( isMixed ) {
- mixedFields.push( field.id );
- }
-
- return mixedFields;
- }, [] );
-}
-
export function getVisibleProductEditFields(
fields: ProductField[],
products: ProductEntityRecord[]
@@ -439,39 +385,49 @@ export function getVisibleProductEditFields(
const compatibleFieldIds =
getCommonProductTypeCompatibleFieldIds( products );
const isBulkEdit = products.length > 1;
+ const fieldsById = new Map(
+ fields.map( ( field ) => [ field.id, field ] )
+ );
- return fields.reduce< ProductField[] >( ( visibleFields, field ) => {
- if ( ! compatibleFieldIds.has( field.id ) ) {
- return visibleFields;
- }
+ return compatibleFieldIds.reduce< ProductField[] >(
+ ( visibleFields, fieldId ) => {
+ const field = fieldsById.get( fieldId );
- if (
- isBulkEdit &&
- BULK_UNSUPPORTED_PRODUCT_EDIT_FIELD_ID_SET.has(
- field.id as ProductEditFieldId
- )
- ) {
- return visibleFields;
- }
+ if ( ! field ) {
+ return visibleFields;
+ }
- if ( ! isFieldVisibleForProductRelationships( field.id, products ) ) {
- return visibleFields;
- }
+ if (
+ isBulkEdit &&
+ BULK_UNSUPPORTED_PRODUCT_EDIT_FIELD_ID_SET.has(
+ field.id as ProductEditFieldId
+ )
+ ) {
+ return visibleFields;
+ }
- const { isVisible } = field;
+ if (
+ ! isFieldVisibleForProductRelationships( field.id, products )
+ ) {
+ return visibleFields;
+ }
- if ( typeof isVisible !== 'function' ) {
- visibleFields.push( field );
- return visibleFields;
- }
+ const { isVisible } = field;
- if ( products.every( ( product ) => isVisible( product ) ) ) {
- visibleFields.push( {
- ...field,
- isVisible: undefined,
- } );
- }
+ if ( typeof isVisible !== 'function' ) {
+ visibleFields.push( field );
+ return visibleFields;
+ }
+
+ if ( products.every( ( product ) => isVisible( product ) ) ) {
+ visibleFields.push( {
+ ...field,
+ isVisible: undefined,
+ } );
+ }
- return visibleFields;
- }, [] );
+ return visibleFields;
+ },
+ []
+ );
}