Commit 043817bfd02 for woocommerce
commit 043817bfd0222d8158cf24eafa86630e9a75408c
Author: Pavel Dohnal <pavel.dohnal@automattic.com>
Date: Tue Mar 24 15:25:38 2026 +0100
Email Editor: Fix double margin-top in flex layout and list block rendering (#63790)
* Email Editor: Fix double margin-top in flex layout and list block rendering
Both Flex_Layout_Renderer and List_Block applied margin-top inside
render_content(), duplicating the margin already applied by
add_spacer() in Abstract_Block_Renderer::render(). This resulted
in 2x the intended gap above buttons and list blocks in rendered emails.
* Add changefile(s) from automation for the following project(s): packages/php/email-editor
* Email Editor: Fix double margin-top in rendered emails
Flex_Layout_Renderer and List_Block apply margin-top on inner elements
where Gmail preserves it. The parent add_spacer() was applying the same
margin-top on the outer wrapper, which Gmail strips — but other clients
rendered both, doubling the gap.
Override render() in Buttons and List_Block to unset margin-top from
email_attrs before passing to add_spacer(), keeping the inner margin
as the single effective source.
* Restore missing docblock on render_content()
* Fix lint: capitalize doc comment long description
* Remove duplicate docblock and add integration tests
Remove stacked docblock on render_content() in Buttons. Add integration
tests for Buttons and List_Block verifying that margin-top appears on
the inner element but not on the outer email-block-layout wrapper.
---------
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
diff --git a/packages/php/email-editor/changelog/63790-wooprd-3077-fix-double-top-margin-in-flex-layout-rendering b/packages/php/email-editor/changelog/63790-wooprd-3077-fix-double-top-margin-in-flex-layout-rendering
new file mode 100644
index 00000000000..f5e7ce30776
--- /dev/null
+++ b/packages/php/email-editor/changelog/63790-wooprd-3077-fix-double-top-margin-in-flex-layout-rendering
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix double margin-top applied to buttons and list blocks in rendered emails.
\ No newline at end of file
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-buttons.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-buttons.php
index 3d65ce68724..49c644b62e2 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-buttons.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-buttons.php
@@ -33,6 +33,25 @@ class Buttons extends Abstract_Block_Renderer {
$this->flex_layout_renderer = $flex_layout_renderer;
}
+ /**
+ * Render the block.
+ *
+ * Flex_Layout_Renderer applies margin-top on its inner div/td where Gmail
+ * preserves it. Strip margin-top from email_attrs so add_spacer() doesn't
+ * apply it again on the outer wrapper (which Gmail ignores).
+ *
+ * @param string $block_content The block content.
+ * @param array $parsed_block The parsed block.
+ * @param Rendering_Context $rendering_context The rendering context.
+ * @return string
+ */
+ public function render( string $block_content, array $parsed_block, Rendering_Context $rendering_context ): string {
+ $content = $this->render_content( $block_content, $parsed_block, $rendering_context );
+ $email_attrs = $parsed_block['email_attrs'] ?? array();
+ unset( $email_attrs['margin-top'] );
+ return $this->add_spacer( $content, $email_attrs );
+ }
+
/**
* Renders the block content.
*
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php
index 1f97fdd4696..ae414ce2cab 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-list-block.php
@@ -16,6 +16,25 @@ use Automattic\WooCommerce\EmailEditor\Integrations\Utils\Styles_Helper;
* We have to avoid using keyword `List`
*/
class List_Block extends Abstract_Block_Renderer {
+ /**
+ * Render the block.
+ *
+ * The render_content() method applies margin-top on its inner wrapper div
+ * where Gmail preserves it. Strip margin-top from email_attrs so
+ * add_spacer() doesn't apply it again on the outer wrapper.
+ *
+ * @param string $block_content The block content.
+ * @param array $parsed_block The parsed block.
+ * @param Rendering_Context $rendering_context The rendering context.
+ * @return string
+ */
+ public function render( string $block_content, array $parsed_block, Rendering_Context $rendering_context ): string {
+ $content = $this->render_content( $block_content, $parsed_block, $rendering_context );
+ $email_attrs = $parsed_block['email_attrs'] ?? array();
+ unset( $email_attrs['margin-top'] );
+ return $this->add_spacer( $content, $email_attrs );
+ }
+
/**
* Renders the block content
*
diff --git a/packages/php/email-editor/tests/integration/Engine/Renderer/ContentRenderer/Layout/Flex_Layout_Renderer_Test.php b/packages/php/email-editor/tests/integration/Engine/Renderer/ContentRenderer/Layout/Flex_Layout_Renderer_Test.php
index bc8653d7cc6..01857f96882 100644
--- a/packages/php/email-editor/tests/integration/Engine/Renderer/ContentRenderer/Layout/Flex_Layout_Renderer_Test.php
+++ b/packages/php/email-editor/tests/integration/Engine/Renderer/ContentRenderer/Layout/Flex_Layout_Renderer_Test.php
@@ -96,6 +96,25 @@ class Flex_Layout_Renderer_Test extends \Email_Editor_Integration_Test_Case {
$this->assertStringContainsString( 'align="center"', $output );
}
+ /**
+ * Test it applies margin-top from email_attrs on the inner div for Gmail compatibility.
+ */
+ public function testItAppliesMarginTopFromEmailAttrs(): void {
+ $parsed_block = array(
+ 'innerBlocks' => array(
+ array(
+ 'blockName' => 'dummy/block',
+ 'innerHTML' => 'Dummy 1',
+ ),
+ ),
+ 'email_attrs' => array(
+ 'margin-top' => '16px',
+ ),
+ );
+ $output = $this->renderer->render_inner_blocks_in_layout( $parsed_block, $this->rendering_context );
+ $this->assertStringContainsString( 'margin-top: 16px', $output );
+ }
+
/**
* Test it escapes attributes.
*/
diff --git a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Buttons_Test.php b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Buttons_Test.php
new file mode 100644
index 00000000000..1bfd5b16e61
--- /dev/null
+++ b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Buttons_Test.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * This file is part of the WooCommerce Email Editor package
+ *
+ * @package Automattic\WooCommerce\EmailEditor
+ */
+
+declare(strict_types = 1);
+namespace Automattic\WooCommerce\EmailEditor\Integrations\Core\Renderer\Blocks;
+
+use Automattic\WooCommerce\EmailEditor\Engine\Renderer\ContentRenderer\Dummy_Block_Renderer;
+use Automattic\WooCommerce\EmailEditor\Engine\Renderer\ContentRenderer\Layout\Flex_Layout_Renderer;
+use Automattic\WooCommerce\EmailEditor\Engine\Renderer\ContentRenderer\Rendering_Context;
+use Automattic\WooCommerce\EmailEditor\Engine\Theme_Controller;
+
+require_once __DIR__ . '/../../../../Engine/Renderer/ContentRenderer/Dummy_Block_Renderer.php';
+
+/**
+ * Integration test for Buttons class
+ */
+class Buttons_Test extends \Email_Editor_Integration_Test_Case {
+ /**
+ * Buttons renderer instance.
+ *
+ * @var Buttons
+ */
+ private $buttons_renderer;
+
+ /**
+ * Rendering context instance.
+ *
+ * @var Rendering_Context
+ */
+ private $rendering_context;
+
+ /**
+ * Set up before each test.
+ */
+ public function setUp(): void {
+ parent::setUp();
+ $theme_controller = $this->di_container->get( Theme_Controller::class );
+ $this->rendering_context = new Rendering_Context( $theme_controller->get_theme() );
+ $this->buttons_renderer = new Buttons( new Flex_Layout_Renderer() );
+ register_block_type( 'dummy/block', array() );
+ add_filter( 'render_block', array( $this, 'renderDummyBlock' ), 10, 2 );
+ }
+
+ /**
+ * Test it does not double margin-top between flex renderer and add_spacer().
+ */
+ public function testItDoesNotDoubleMarginTop(): void {
+ $parsed_block = array(
+ 'blockName' => 'core/buttons',
+ 'attrs' => array(),
+ 'innerBlocks' => array(
+ array(
+ 'blockName' => 'dummy/block',
+ 'innerHTML' => 'Click me',
+ ),
+ ),
+ 'email_attrs' => array(
+ 'margin-top' => '20px',
+ ),
+ );
+ $rendered = $this->buttons_renderer->render( '', $parsed_block, $this->rendering_context );
+ // The inner flex div has margin-top (for Gmail).
+ $this->assertStringContainsString( 'margin-top: 20px', $rendered );
+ // The outer email-block-layout wrapper should not have margin-top.
+ $this->assertStringNotContainsString( 'email-block-layout" style="margin-top', $rendered );
+ }
+
+ /**
+ * Render a dummy block.
+ *
+ * @param string $block_content Block content.
+ * @param array $parsed_block Parsed block data.
+ * @return string
+ */
+ public function renderDummyBlock( $block_content, $parsed_block ): string {
+ $dummy_renderer = new Dummy_Block_Renderer();
+ return $dummy_renderer->render( $block_content, $parsed_block, $this->rendering_context );
+ }
+
+ /**
+ * Clean up after each test.
+ */
+ public function tearDown(): void {
+ parent::tearDown();
+ unregister_block_type( 'dummy/block' );
+ remove_filter( 'render_block', array( $this, 'renderDummyBlock' ), 10 );
+ }
+}
diff --git a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/List_Block_Test.php b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/List_Block_Test.php
index 188686c79c4..86cea83b0b6 100644
--- a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/List_Block_Test.php
+++ b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/List_Block_Test.php
@@ -100,6 +100,20 @@ class List_Block_Test extends \Email_Editor_Integration_Test_Case {
$this->assertStringContainsString( 'font-size:20px;', $rendered );
}
+ /**
+ * Test it does not double margin-top between render_content() and add_spacer().
+ */
+ public function testItDoesNotDoubleMarginTop(): void {
+ $parsed_list = $this->parsed_list;
+ $parsed_list['email_attrs'] = array(
+ 'margin-top' => '20px',
+ );
+ $rendered = $this->list_renderer->render( '<ul><li>Item 1</li></ul>', $parsed_list, $this->rendering_context );
+ $this->assertStringContainsString( 'margin-top:20px', $rendered );
+ // The inner wrapper div has margin-top, but the outer email-block-layout should not.
+ $this->assertStringNotContainsString( 'email-block-layout" style="margin-top', $rendered );
+ }
+
/**
* Test it preserves custom set colors
*/