Commit f5828efba37 for woocommerce

commit f5828efba3715f8aff3b6503a91dcb77918fd420
Author: Tung Du <dinhtungdu@gmail.com>
Date:   Wed Oct 14 11:36:52 2026 +0700

    Fix Product Button margins adding to parent block spacing (#69583)

    * Fix Product Button margins adding to parent block spacing

    * Add changelog entries for Product Button spacing

diff --git a/plugins/woocommerce/changelog/fix-67314-product-button-margin b/plugins/woocommerce/changelog/fix-67314-product-button-margin
new file mode 100644
index 00000000000..cac9c60cc2a
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-67314-product-button-margin
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Respect Product Button margins without adding the parent block spacing.
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/block.json
index 3cf188450ac..5854d599e67 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/block.json
@@ -55,12 +55,16 @@
 		"spacing": {
 			"margin": true,
 			"padding": true,
-			"__experimentalSkipSerialization": true
+			"__experimentalSkipSerialization": [ "padding" ]
 		},
 		"email": true
 	},
 	"selectors": {
-		"root": ".wp-block-woocommerce-product-button .wc-block-components-product-button__button"
+		"root": ".wp-block-woocommerce-product-button .wc-block-components-product-button__button",
+		"spacing": {
+			"margin": ".wp-block-woocommerce-product-button",
+			"padding": ".wp-block-woocommerce-product-button .wc-block-components-product-button__button"
+		}
 	},
 	"ancestor": [
 		"woocommerce/all-products",
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/block.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/block.tsx
index be3fb7d3804..bd323c9eb70 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/block.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/block.tsx
@@ -286,6 +286,21 @@ const AddToCartButtonPlaceholder = ( {
 export const Block = ( props: BlockAttributes ): JSX.Element => {
 	const { className, textAlign, blockClientId, collection } = props;
 	const styleProps = useStyleProps( props );
+	const {
+		margin,
+		marginTop,
+		marginRight,
+		marginBottom,
+		marginLeft,
+		...buttonStyle
+	} = styleProps.style;
+	const blockMarginStyle = {
+		margin,
+		marginTop,
+		marginRight,
+		marginBottom,
+		marginLeft,
+	};
 	const { parentClassName } = useInnerBlockLayoutContext();
 	const { product, isLoading } = useProductDataContext( {
 		product: props.product,
@@ -307,11 +322,12 @@ export const Block = ( props: BlockAttributes ): JSX.Element => {
 					[ `align-${ textAlign }` ]: textAlign,
 				}
 			) }
+			style={ props.isAdmin ? undefined : blockMarginStyle }
 		>
 			{ isLoading ? (
 				<LoadingAddToCartButton
 					className={ styleProps.className }
-					style={ styleProps.style }
+					style={ buttonStyle }
 				/>
 			) : (
 				<>
@@ -330,7 +346,7 @@ export const Block = ( props: BlockAttributes ): JSX.Element => {
 						( product && product?.id ? (
 							<AddToCartButton
 								product={ product }
-								style={ styleProps.style }
+								style={ buttonStyle }
 								className={ styleProps.className }
 								isAdmin={ props.isAdmin }
 								isDescendantOfAddToCartWithOptions={
@@ -343,7 +359,7 @@ export const Block = ( props: BlockAttributes ): JSX.Element => {
 							/>
 						) : (
 							<AddToCartButtonPlaceholder
-								style={ styleProps.style }
+								style={ buttonStyle }
 								className={ styleProps.className }
 								isLoading={ isLoading ?? false }
 								blockClientId={ blockClientId }
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/test/block.test.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/test/block.test.tsx
index b5cc2f01cf8..62d94907a7a 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/test/block.test.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/button/test/block.test.tsx
@@ -6,11 +6,12 @@ import {
 	useStoreAddToCart,
 	useStoreEvents,
 } from '@woocommerce/base-context/hooks';
+import { useStyleProps } from '@woocommerce/base-hooks';

 /**
  * Internal dependencies
  */
-import { AddToCartButton } from '../block';
+import { AddToCartButton, Block } from '../block';
 import type { AddToCartButtonAttributes } from '../types';

 jest.mock( '@woocommerce/base-context/hooks', () => ( {
@@ -18,7 +19,18 @@ jest.mock( '@woocommerce/base-context/hooks', () => ( {
 	useStoreEvents: jest.fn(),
 } ) );

-jest.mock( '@woocommerce/base-hooks', () => ( {} ) );
+jest.mock( '@woocommerce/base-hooks', () => ( {
+	useStyleProps: jest.fn(),
+} ) );
+
+jest.mock( '@woocommerce/shared-context', () => ( {
+	useInnerBlockLayoutContext: () => ( {} ),
+	useProductDataContext: () => ( {} ),
+} ) );
+
+jest.mock( '../../../../shared/stores/product-type-template-state', () => ( {
+	useProductTypeSelector: () => ( {} ),
+} ) );

 jest.mock( '@woocommerce/block-settings', () => ( {
 	CART_URL: '/cart/',
@@ -85,3 +97,24 @@ describe( 'AddToCartButton', () => {
 		expect( link ).toHaveAttribute( 'rel', 'nofollow' );
 	} );
 } );
+
+describe( 'Product Button spacing', () => {
+	it.each( [ true, false ] )(
+		'keeps padding on the button and leaves editor margin to block supports (isAdmin=%s)',
+		( isAdmin ) => {
+			( useStyleProps as jest.Mock ).mockReturnValue( {
+				className: '',
+				style: { marginTop: '12px', paddingTop: '10px' },
+			} );
+
+			const { container } = render( <Block isAdmin={ isAdmin } /> );
+			const wrapper = container.firstElementChild as HTMLElement;
+			const button = screen.getByRole( 'button' );
+
+			expect( wrapper.style.marginTop ).toBe( isAdmin ? '' : '12px' );
+			expect( wrapper.style.paddingTop ).toBe( '' );
+			expect( button.style.marginTop ).toBe( '' );
+			expect( button ).toHaveStyle( { paddingTop: '10px' } );
+		}
+	);
+} );
diff --git a/plugins/woocommerce/client/blocks/changelog/fix-67314-product-button-margin b/plugins/woocommerce/client/blocks/changelog/fix-67314-product-button-margin
new file mode 100644
index 00000000000..cac9c60cc2a
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/changelog/fix-67314-product-button-margin
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Respect Product Button margins without adding the parent block spacing.
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductButton.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductButton.php
index 2858eaf7218..b3aeaa06aae 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductButton.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductButton.php
@@ -99,7 +99,7 @@ class ProductButton extends AbstractBlock {
 		$ajax_add_to_cart_enabled = get_option( 'woocommerce_enable_ajax_add_to_cart' ) === 'yes';
 		$is_ajax_button           = ( ( $ajax_add_to_cart_enabled && $product->supports( 'ajax_add_to_cart' ) ) || $is_descendant_of_add_to_cart_form ) && $is_product_purchasable && ! $cart_redirect_after_add;
 		$html_element             = $is_ajax_button || ( $is_descendant_of_add_to_cart_form && ! $product->is_type( ProductType::EXTERNAL ) ) ? 'button' : 'a';
-		$styles_and_classes       = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes, array(), array( 'extra_classes' ) );
+		$styles_and_classes       = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes, array(), array( 'extra_classes', 'margin' ) );
 		$classname                = StyleAttributesUtils::get_classes_by_attributes( $attributes, array( 'extra_classes' ) );
 		$custom_width_classes     = isset( $attributes['width'] ) ? 'has-custom-width wp-block-button__width-' . $attributes['width'] : '';
 		$custom_align_classes     = isset( $attributes['textAlign'] ) ? 'align-' . $attributes['textAlign'] : '';