Commit 5e8e78c9c6 for woocommerce
commit 5e8e78c9c6ede78719d884cc84cdcc70d8d4294e
Author: Allison Levine <1689238+allilevine@users.noreply.github.com>
Date: Tue Jan 20 15:06:51 2026 -0500
Email editor: Add support for horizontal blockGap settings on columns (#62838)
* Email editor: Add support for blockgap settings on columns.
* Add changefile(s) from automation for the following project(s): packages/php/email-editor
* Get the padding value via the WP Styles engine.
* Add default blockGap to match post editor.
* Update test.
---------
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
diff --git a/packages/php/email-editor/changelog/62838-add-column-blockgap-support b/packages/php/email-editor/changelog/62838-add-column-blockgap-support
new file mode 100644
index 0000000000..c6857dfb67
--- /dev/null
+++ b/packages/php/email-editor/changelog/62838-add-column-blockgap-support
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Email editor: Add support for horizontal blockGap settings on columns.
\ No newline at end of file
diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-spacing-preprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-spacing-preprocessor.php
index 0f5b06fa9e..f56d909c43 100644
--- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-spacing-preprocessor.php
+++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-spacing-preprocessor.php
@@ -49,10 +49,50 @@ class Spacing_Preprocessor implements Preprocessor {
$block['email_attrs']['margin-top'] = $gap;
}
+ // Handle horizontal gap for columns: apply padding-left to column children (except the first).
+ if ( 'core/columns' === $parent_block_name && 0 !== $key && null !== $parent_block ) {
+ $columns_gap = $this->get_columns_block_gap( $parent_block, $gap );
+ if ( $columns_gap ) {
+ $block['email_attrs']['padding-left'] = $columns_gap;
+ }
+ }
+
$block['innerBlocks'] = $this->add_block_gaps( $block['innerBlocks'] ?? array(), $gap, $block );
$parsed_blocks[ $key ] = $block;
}
return $parsed_blocks;
}
+
+ /**
+ * Extracts the horizontal blockGap from a columns block.
+ *
+ * @param array $columns_block The columns block.
+ * @param string $default_gap Default gap value to use if blockGap is not set on the columns block.
+ * @return string|null The horizontal gap value (e.g., "30px" or "var:preset|spacing|30") or null if not set.
+ */
+ private function get_columns_block_gap( array $columns_block, string $default_gap = '' ): ?string {
+ $block_gap = $columns_block['attrs']['style']['spacing']['blockGap'] ?? null;
+
+ // Columns block uses object format: { "top": "...", "left": "..." }.
+ // If blockGap.left is explicitly set, use it.
+ if ( is_array( $block_gap ) && isset( $block_gap['left'] ) && is_string( $block_gap['left'] ) ) {
+ $gap_value = $block_gap['left'];
+
+ // Validate against potentially malicious values.
+ if ( preg_match( '/[<>"\']/', $gap_value ) ) {
+ return null;
+ }
+
+ // Return the value as-is. WP's styles engine will handle transformation of preset variables.
+ return $gap_value;
+ }
+
+ // If blockGap.left is not set, use the default gap value if provided.
+ if ( $default_gap ) {
+ return $default_gap;
+ }
+
+ return null;
+ }
}
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-column.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-column.php
index d572884235..6a71b96e29 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-column.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-column.php
@@ -109,6 +109,13 @@ class Column extends Abstract_Block_Renderer {
$inner_table = Table_Wrapper_Helper::render_table_wrapper( '{column_content}', $inner_table_attrs, $inner_cell_attrs );
+ // Apply padding-left from email_attrs (set by Spacing_Preprocessor for columns blockGap).
+ $padding_left = $parsed_block['email_attrs']['padding-left'] ?? null;
+ if ( $padding_left ) {
+ $gap_padding_styles = wp_style_engine_get_styles( array( 'spacing' => array( 'padding' => array( 'left' => $padding_left ) ) ) );
+ $wrapper_styles = Styles_Helper::extend_block_styles( $wrapper_styles, $gap_padding_styles['declarations'] ?? array() );
+ }
+
// Create the outer td element (since this is meant to be used within a columns structure).
$wrapper_cell_attrs = array(
'class' => $wrapper_classname,
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-columns.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-columns.php
index 1f8b1ed000..95e4cdc860 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-columns.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-columns.php
@@ -18,8 +18,8 @@ use Automattic\WooCommerce\EmailEditor\Integrations\Utils\Styles_Helper;
*/
class Columns extends Abstract_Block_Renderer {
/**
- * Override this method to disable spacing (block gap) for columns.
- * Spacing is applied on wrapping columns block. Columns are rendered side by side so no spacer is needed.
+ * Renders the block content.
+ * BlockGap spacing is handled by Spacing_Preprocessor which sets padding-left on column children.
*
* @param string $block_content Block content.
* @param array $parsed_block Parsed block.
diff --git a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Column_Test.php b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Column_Test.php
index 2974a17c00..4ce9cfe063 100644
--- a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Column_Test.php
+++ b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Column_Test.php
@@ -181,4 +181,28 @@ class Column_Test extends \Email_Editor_Integration_Test_Case {
$this->checkValidHTML( $rendered );
$this->assertStringContainsString( 'wp-block-column editor-class-1 another-class', $rendered );
}
+
+ /**
+ * Test it applies padding-left from email_attrs (set by Spacing_Preprocessor for columns blockGap)
+ */
+ public function testItAppliesPaddingLeftFromEmailAttrs(): void {
+ $parsed_column = $this->parsed_column;
+ $parsed_column['email_attrs']['padding-left'] = '30px';
+ $rendered = $this->column_renderer->render( '<p>Column content</p>', $parsed_column, $this->rendering_context );
+ $this->checkValidHTML( $rendered );
+ $this->assertStringContainsString( 'padding-left:30px', $rendered );
+ }
+
+ /**
+ * Test it applies padding-left with preset variable from email_attrs (set by Spacing_Preprocessor for columns blockGap)
+ * Verifies that wp_style_engine_get_styles transforms var:preset|spacing|30 to CSS variable format
+ */
+ public function testItAppliesPaddingLeftWithPresetVariableFromEmailAttrs(): void {
+ $parsed_column = $this->parsed_column;
+ $parsed_column['email_attrs']['padding-left'] = 'var:preset|spacing|30';
+ $rendered = $this->column_renderer->render( '<p>Column content</p>', $parsed_column, $this->rendering_context );
+ $this->checkValidHTML( $rendered );
+ // wp_style_engine_get_styles transforms var:preset|spacing|30 to var(--wp--preset--spacing--30).
+ $this->assertStringContainsString( 'var(--wp--preset--spacing--30)', $rendered );
+ }
}
diff --git a/packages/php/email-editor/tests/unit/Engine/Renderer/ContentRenderer/Preprocessors/Spacing_Preprocessor_Test.php b/packages/php/email-editor/tests/unit/Engine/Renderer/ContentRenderer/Preprocessors/Spacing_Preprocessor_Test.php
index 6fa1eb8325..066bbe44ad 100644
--- a/packages/php/email-editor/tests/unit/Engine/Renderer/ContentRenderer/Preprocessors/Spacing_Preprocessor_Test.php
+++ b/packages/php/email-editor/tests/unit/Engine/Renderer/ContentRenderer/Preprocessors/Spacing_Preprocessor_Test.php
@@ -112,4 +112,173 @@ class Spacing_Preprocessor_Test extends \Email_Editor_Unit_Test {
$this->assertArrayHasKey( 'margin-top', $nested_column_second_item['email_attrs'] );
$this->assertEquals( '10px', $nested_column_second_item['email_attrs']['margin-top'] );
}
+
+ /**
+ * Test it adds padding-left to column blocks when parent columns has blockGap.left
+ */
+ public function testItAddsPaddingLeftToColumnsWithBlockGap(): void {
+ $blocks = array(
+ array(
+ 'blockName' => 'core/columns',
+ 'attrs' => array(
+ 'style' => array(
+ 'spacing' => array(
+ 'blockGap' => array(
+ 'top' => '20px',
+ 'left' => '30px',
+ ),
+ ),
+ ),
+ ),
+ 'innerBlocks' => array(
+ array(
+ 'blockName' => 'core/column',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ array(
+ 'blockName' => 'core/column',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ array(
+ 'blockName' => 'core/column',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ ),
+ ),
+ );
+
+ $result = $this->preprocessor->preprocess( $blocks, $this->layout, $this->styles );
+ $columns_block = $result[0];
+ $first_column = $columns_block['innerBlocks'][0];
+ $second_column = $columns_block['innerBlocks'][1];
+ $third_column = $columns_block['innerBlocks'][2];
+
+ // First column should not have padding-left.
+ $this->assertArrayNotHasKey( 'padding-left', $first_column['email_attrs'] );
+
+ // Second and third columns should have padding-left.
+ $this->assertArrayHasKey( 'padding-left', $second_column['email_attrs'] );
+ $this->assertEquals( '30px', $second_column['email_attrs']['padding-left'] );
+ $this->assertArrayHasKey( 'padding-left', $third_column['email_attrs'] );
+ $this->assertEquals( '30px', $third_column['email_attrs']['padding-left'] );
+ }
+
+ /**
+ * Test it passes preset variables through for columns blockGap (WP styles engine will handle transformation)
+ */
+ public function testItPassesPresetVariablesThroughForColumnsBlockGap(): void {
+ $blocks = array(
+ array(
+ 'blockName' => 'core/columns',
+ 'attrs' => array(
+ 'style' => array(
+ 'spacing' => array(
+ 'blockGap' => array(
+ 'left' => 'var:preset|spacing|40',
+ ),
+ ),
+ ),
+ ),
+ 'innerBlocks' => array(
+ array(
+ 'blockName' => 'core/column',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ array(
+ 'blockName' => 'core/column',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ ),
+ ),
+ );
+
+ $result = $this->preprocessor->preprocess( $blocks, $this->layout, $this->styles );
+ $second_column = $result[0]['innerBlocks'][1];
+
+ // Should pass through "var:preset|spacing|40" as-is. WP's styles engine will handle transformation.
+ $this->assertEquals( 'var:preset|spacing|40', $second_column['email_attrs']['padding-left'] );
+ }
+
+ /**
+ * Test it adds default padding-left when columns has no blockGap.left
+ */
+ public function testItAddsDefaultPaddingLeftWithoutBlockGapLeft(): void {
+ $blocks = array(
+ array(
+ 'blockName' => 'core/columns',
+ 'attrs' => array(
+ 'style' => array(
+ 'spacing' => array(
+ 'blockGap' => array(
+ 'top' => '20px',
+ // No 'left' key.
+ ),
+ ),
+ ),
+ ),
+ 'innerBlocks' => array(
+ array(
+ 'blockName' => 'core/column',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ array(
+ 'blockName' => 'core/column',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ ),
+ ),
+ );
+
+ $result = $this->preprocessor->preprocess( $blocks, $this->layout, $this->styles );
+ $second_column = $result[0]['innerBlocks'][1];
+
+ // Should have padding-left with default gap value since blockGap.left is not set.
+ $this->assertArrayHasKey( 'padding-left', $second_column['email_attrs'] );
+ $this->assertEquals( '10px', $second_column['email_attrs']['padding-left'] );
+ }
+
+ /**
+ * Test it rejects malicious values in blockGap
+ */
+ public function testItRejectsMaliciousBlockGapValues(): void {
+ $blocks = array(
+ array(
+ 'blockName' => 'core/columns',
+ 'attrs' => array(
+ 'style' => array(
+ 'spacing' => array(
+ 'blockGap' => array(
+ 'left' => '30px"><script>alert("xss")</script>',
+ ),
+ ),
+ ),
+ ),
+ 'innerBlocks' => array(
+ array(
+ 'blockName' => 'core/column',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ array(
+ 'blockName' => 'core/column',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ ),
+ ),
+ );
+
+ $result = $this->preprocessor->preprocess( $blocks, $this->layout, $this->styles );
+ $second_column = $result[0]['innerBlocks'][1];
+
+ // Should not have padding-left due to malicious value.
+ $this->assertArrayNotHasKey( 'padding-left', $second_column['email_attrs'] );
+ }
}