Commit 055cb5d244 for woocommerce

commit 055cb5d244043744a190f7b2e9bd41c6c6883c68
Author: Albert Juhé Lluveras <contact@albertjuhe.com>
Date:   Thu Sep 18 15:48:39 2025 +0200

    Add to Cart + Options: skip adding default values to variations data object (#60966)

    * Add to Cart + Options: skip adding default values to variations data object

    * Add changelog file

    * CodeRabbit suggestions

diff --git a/plugins/woocommerce/changelog/fix-add-to-cart-with-options-non-default-variation-data b/plugins/woocommerce/changelog/fix-add-to-cart-with-options-non-default-variation-data
new file mode 100644
index 0000000000..b4a3c8d583
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-add-to-cart-with-options-non-default-variation-data
@@ -0,0 +1,4 @@
+Significance: patch
+Type: update
+
+Add to Cart + Options: skip adding default values to variations data object
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts
index f1ea3afad1..1db4c8cc10 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts
@@ -47,6 +47,22 @@ export type ClientCartItem = Omit< OptimisticCartItem, 'variation' > & {
 	variation?: SelectedAttributes[];
 };

+export type VariationData = {
+	attributes: Record< string, string >;
+	is_in_stock?: boolean;
+	price_html?: string;
+	image_id?: number;
+	availability?: string;
+	variation_description?: string;
+	sku?: string;
+	weight?: string;
+	dimensions?: string;
+	min?: number;
+	max?: number;
+	step?: number;
+	sold_individually?: boolean;
+};
+
 export type ProductData = {
 	type: string;
 	price_html?: string;
@@ -58,23 +74,7 @@ export type ProductData = {
 	min?: number;
 	max?: number;
 	step?: number;
-	variations?: {
-		[ variationId: number ]: {
-			attributes: Record< string, string >;
-			is_in_stock: boolean;
-			type: string;
-			price_html?: string;
-			image_id?: number;
-			availability?: string;
-			sku?: string;
-			weight?: string;
-			dimensions?: string;
-			min?: number;
-			max?: number;
-			step?: number;
-			sold_individually?: boolean;
-		};
-	};
+	variations?: Record< number, VariationData >;
 };

 type CartUpdateOptions = { showCartUpdatesNotices?: boolean };
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts
index b55f50741d..2579ba09ac 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts
@@ -6,6 +6,7 @@ import type {
 	Store as WooCommerce,
 	SelectedAttributes,
 	ProductData,
+	VariationData,
 	WooCommerceConfig,
 } from '@woocommerce/stores/woocommerce/cart';
 import '@woocommerce/stores/woocommerce/product-data';
@@ -55,10 +56,11 @@ export const getProductData = (
 	selectedAttributes: SelectedAttributes[]
 ) => {
 	let productId = id;
-	let productData: ProductData | undefined;
+	let productData: ProductData | VariationData | undefined;

 	const { products } = getConfig( 'woocommerce' ) as WooCommerceConfig;

+	let type: ProductData[ 'type' ] | 'variation' | null = null;
 	if ( selectedAttributes && selectedAttributes.length > 0 ) {
 		if ( ! products || ! products[ id ] ) {
 			return null;
@@ -70,13 +72,14 @@ export const getProductData = (
 		);
 		if ( matchedVariation?.variation_id ) {
 			productId = matchedVariation.variation_id;
-			productData =
-				products?.[ id ]?.variations?.[
-					matchedVariation?.variation_id
-				];
+			productData = products?.[ id ]?.variations?.[
+				matchedVariation?.variation_id
+			] as VariationData;
+			type = 'variation';
 		}
 	} else {
-		productData = products?.[ productId ];
+		productData = products?.[ productId ] as ProductData;
+		type = productData?.type;
 	}

 	if ( typeof productData !== 'object' || productData === null ) {
@@ -96,6 +99,7 @@ export const getProductData = (
 		min,
 		max,
 		step,
+		type,
 	};
 };

diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
index acb12f5192..94d8d3e6eb 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
@@ -258,7 +258,7 @@ class AddToCartWithOptions extends AbstractBlock {
 			);

 			if ( $product->is_type( ProductType::VARIABLE ) ) {
-				$variation_data                = array();
+				$variations_data               = array();
 				$context['selectedAttributes'] = array();
 				$available_variations          = $product->get_available_variations( 'objects' );
 				foreach ( $available_variations as $variation ) {
@@ -267,11 +267,14 @@ class AddToCartWithOptions extends AbstractBlock {
 					// input for all variations, so we want quantities to be in sync.
 					$context['quantity'][ $variation->get_id() ] = $default_quantity;

-					$variation_data[ $variation->get_id() ] = array(
-						'is_in_stock' => $variation->is_in_stock(),
-						'attributes'  => $variation->get_variation_attributes(),
-						'type'        => $variation->get_type(),
+					$variation_data = array(
+						'attributes' => $variation->get_variation_attributes(),
 					);
+					if ( $variation->is_in_stock() ) {
+						$variation_data['is_in_stock'] = true;
+					}
+
+					$variations_data[ $variation->get_id() ] = $variation_data;
 				}

 				wp_interactivity_config(
@@ -279,7 +282,7 @@ class AddToCartWithOptions extends AbstractBlock {
 					array(
 						'products' => array(
 							$product->get_id() => array(
-								'variations' => $variation_data,
+								'variations' => $variations_data,
 							),
 						),
 					)
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/QuantitySelector.php b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/QuantitySelector.php
index d1d3745c6e..cbdf811561 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/QuantitySelector.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/QuantitySelector.php
@@ -129,13 +129,23 @@ class QuantitySelector extends AbstractBlock {
 			$variations_data           = $product->get_available_variations( 'objects' );
 			$formatted_variations_data = array();
 			foreach ( $variations_data as $variation ) {
-				$variation_quantity_constraints                    = AddToCartWithOptionsUtils::get_product_quantity_constraints( $variation );
-				$formatted_variations_data[ $variation->get_id() ] = array(
-					'min'               => $variation_quantity_constraints['min'],
-					'max'               => $variation_quantity_constraints['max'],
-					'step'              => $variation_quantity_constraints['step'],
-					'sold_individually' => (bool) $variation->is_sold_individually(),
-				);
+				$variation_quantity_constraints = AddToCartWithOptionsUtils::get_product_quantity_constraints( $variation );
+				$variation_data                 = array();
+
+				// Only add variation data if it's different than the defaults.
+				if ( 1 !== $variation_quantity_constraints['min'] ) {
+					$variation_data['min'] = $variation_quantity_constraints['min'];
+				}
+				if ( null !== $variation_quantity_constraints['max'] ) {
+					$variation_data['max'] = $variation_quantity_constraints['max'];
+				}
+				if ( 1 !== $variation_quantity_constraints['step'] ) {
+					$variation_data['step'] = $variation_quantity_constraints['step'];
+				}
+				if ( $variation->is_sold_individually() ) {
+					$variation_data['sold_individually'] = true;
+				}
+				$formatted_variations_data[ $variation->get_id() ] = $variation_data;
 			}

 			wp_interactivity_config(
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationDescription.php b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationDescription.php
index 2d48f29251..e30ea5ea80 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationDescription.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationDescription.php
@@ -39,9 +39,12 @@ class VariationDescription extends AbstractBlock {
 		$variations                = $product->get_available_variations( 'objects' );
 		$formatted_variations_data = array();
 		foreach ( $variations as $variation ) {
-			$formatted_variations_data[ $variation->get_id() ] = array(
-				'variation_description' => wp_kses_post( wc_format_content( $variation->get_description() ) ),
-			);
+			$variation_description = $variation->get_description();
+			if ( is_string( $variation_description ) && ! empty( $variation_description ) ) {
+				$formatted_variations_data[ $variation->get_id() ] = array(
+					'variation_description' => wp_kses_post( wc_format_content( $variation_description ) ),
+				);
+			}
 		}

 		wp_interactivity_config(
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductGallery.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductGallery.php
index 2fd1c165f2..a6d74f2f4f 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductGallery.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductGallery.php
@@ -167,10 +167,15 @@ class ProductGallery extends AbstractBlock {
 					) {
 						continue;
 					}
-					$has_variation_images                                    = $has_variation_images || $variation['image_id'] !== $product->get_image_id();
-					$formatted_variations_data[ $variation['variation_id'] ] = array(
-						'image_id' => (int) $variation['image_id'],
-					);
+
+					$variation_image_id = (int) $variation['image_id'];
+					if ( $variation_image_id && $variation_image_id !== (int) $product->get_image_id() ) {
+						$has_variation_images = true;
+
+						$formatted_variations_data[ $variation['variation_id'] ] = array(
+							'image_id' => $variation_image_id,
+						);
+					}
 				}

 				if ( $has_variation_images ) {
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductStockIndicator.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductStockIndicator.php
index ed20dc4f86..ff2ff9fcec 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductStockIndicator.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductStockIndicator.php
@@ -130,10 +130,12 @@ class ProductStockIndicator extends AbstractBlock {
 			$variations                = $product_to_render->get_available_variations( 'objects' );
 			$formatted_variations_data = array();
 			foreach ( $variations as $variation ) {
-				$variation_availability                            = $variation->get_availability();
-				$formatted_variations_data[ $variation->get_id() ] = array(
-					'availability' => $variation_availability['availability'],
-				);
+				$variation_availability = $variation->get_availability();
+				if ( is_string( $variation_availability['availability'] ) && ! empty( $variation_availability['availability'] ) ) {
+					$formatted_variations_data[ $variation->get_id() ] = array(
+						'availability' => $variation_availability['availability'],
+					);
+				}
 			}

 			wp_interactivity_config(