Commit a51692f6a9b for woocommerce
commit a51692f6a9b576da84739e594ee84bdde1522e0c
Author: Allison Levine <1689238+allilevine@users.noreply.github.com>
Date: Wed Mar 18 09:33:22 2026 -0400
Email Editor: Apply root horizontal padding uniformly to all blocks (#63682)
* Apply root horizontal padding uniformly in Content_Renderer
Blocks using render_email_callback without Abstract_Block_Renderer
(e.g., wpcom template blocks, Jetpack blocks, third-party blocks)
were missing horizontal root padding because it was only applied
inside Abstract_Block_Renderer::add_spacer().
This moves root padding application from add_spacer() to
Content_Renderer::render_block(), which runs for all blocks
regardless of their rendering path. Root padding email_attrs
are renamed from padding-left/right to root-padding-left/right
to avoid collisions with column gap padding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix PHPCS: remove blank line before closing brace
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add filterable post-content block names for padding delegation
Integrations using custom post-content-like blocks (e.g.,
wpcom/email-post-content) can now hook into the
woocommerce_email_editor_post_content_block_names filter to
register their block name and receive the same root padding
delegation behaviour as core/post-content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Assert both root-padding keys in negative test cases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Only skip root padding for explicit zero horizontal padding
Non-zero padding (e.g. 20px) is internal content spacing and should
not opt a block out of root padding. Only explicit zero padding
(0, 0px, 0em, etc.) signals edge-to-edge intent.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Separate zero-padding skip from explicit-padding delegation stop
Only explicit zero padding (0px) opts a block out of root padding
(edge-to-edge intent). Any explicit padding on a container stops
delegation to children. Root-level containers with explicit non-zero
padding now receive root padding themselves instead of going
edge-to-edge.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Stop subtracting root padding from block widths in Blocks_Width_Preprocessor
Root padding is now distributed to individual blocks by Spacing_Preprocessor,
not applied on the outer container. The width preprocessor was still subtracting
it from layout width, making blocks ~10px narrower per side than available space.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix PHPCS: inline comments must end with full stops
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Subtract root padding per-block using email_attrs in width preprocessor
Run Spacing_Preprocessor before Blocks_Width_Preprocessor so that
root-padding-left/right values are available in email_attrs when
calculating block widths. This prevents double-subtraction: the width
preprocessor no longer blindly subtracts root padding from all blocks
via styles, but only from blocks that actually received root padding
from the Spacing_Preprocessor.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Resolve preset variable references in block padding for width calculations
Block attributes can store padding as preset references like
var:preset|spacing|20 which parse_number_from_string_with_pixels
cannot handle (returns 0). Pass the CSS variables map from the
theme controller through styles so the width preprocessor can
resolve these references to actual pixel values before calculating
block and column widths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Pass effective post-content width to second preprocessing pass
User blocks inside post-content are preprocessed in a separate pass
that previously started from the raw contentSize, ignoring template
group padding. Store the post-content block's calculated width from
the first pass (template blocks) and use it as contentSize in the
second pass. Also remove root padding from the second pass since it
was already applied on the template group.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Revert post-content width passthrough to preserve full-width block support
Reverts the post_content_width logic from abd7433a29 which constrained
all second-pass blocks to the post-content effective width and removed
root padding distribution. This prevented full-width blocks from breaking
out of the template group. Keeps the variables_map passing for preset
resolution.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Skip root padding in second preprocessing pass to prevent double application
Template blocks are preprocessed first, and root padding is applied to
the template's root group. When user blocks inside post-content are
preprocessed in a separate second pass, root padding was being applied
again, causing blocks to be narrower than expected (e.g. 420px instead
of 520px). Track whether root padding was already applied and remove it
from styles in subsequent passes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add preprocessor ordering comment, cache post-content block names, fix PHPStan types
Add comment in Process_Manager explaining that Spacing must run before
Width due to email_attrs dependency. Cache get_post_content_block_names()
result to avoid repeated apply_filters calls in recursive block
traversal. Make padding left/right optional in PHPStan type annotations
since they may be unset in the second preprocessing pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix PHPCS: align parameter type spacing in PHPDoc annotations
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Skip root padding in second pass only when applied to a wrapper above post-content
The boolean root_padding_applied flag incorrectly stripped root padding
from the second preprocessing pass for ALL templates. MailPoet templates
delegate root padding (root group has no own padding), so user blocks
need root padding applied directly. Fix by comparing post-content width
to contentSize: if post-content is narrower, root padding was absorbed
by a container above it (skip in second pass). If equal, the template
delegates and user blocks should receive root padding.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address code review: cache filter, float tolerance, docs, two-pass tests
1. Cache apply_filters result in find_post_content_width to avoid
repeated calls during recursive block traversal.
2. Add epsilon tolerance (0.01) for floating-point width comparison
to prevent imprecision from round() and division.
3. Document the three-way padding split (zero/non-zero/none) in
add_block_gaps docblock.
4. Add integration tests verifying two-pass coordination: root padding
is skipped when absorbed by a wrapper (WooCommerce pattern) and
kept when delegated (MailPoet pattern).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jan Lysý <jan.lysy@automattic.com>
diff --git a/packages/php/email-editor/changelog/fix-email-editor-uniform-root-padding b/packages/php/email-editor/changelog/fix-email-editor-uniform-root-padding
new file mode 100644
index 00000000000..e65435224d5
--- /dev/null
+++ b/packages/php/email-editor/changelog/fix-email-editor-uniform-root-padding
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Apply root horizontal padding uniformly to all blocks in the email rendering pipeline, fixing missing padding for blocks using render_email_callback without Abstract_Block_Renderer.
diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-blocks-width-preprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-blocks-width-preprocessor.php
index c3e5d3d2599..6db7325d72e 100644
--- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-blocks-width-preprocessor.php
+++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-blocks-width-preprocessor.php
@@ -16,22 +16,59 @@ class Blocks_Width_Preprocessor implements Preprocessor {
/**
* Method to preprocess the content before rendering
*
- * @param array $parsed_blocks Parsed blocks of the email.
- * @param array{contentSize: string} $layout Layout of the email.
- * @param array{spacing: array{padding: array{bottom: string, left: string, right: string, top: string}, blockGap: string}} $styles Styles of the email.
+ * @param array $parsed_blocks Parsed blocks of the email.
+ * @param array{contentSize: string} $layout Layout of the email.
+ * @param array{spacing: array{padding: array{bottom: string, left?: string, right?: string, top: string}, blockGap: string}} $styles Styles of the email.
* @return array
*/
public function preprocess( array $parsed_blocks, array $layout, array $styles ): array {
+ // Root padding is distributed to individual blocks by Spacing_Preprocessor
+ // (which runs before this preprocessor). Zero it out here so we don't
+ // double-subtract: each block's width is reduced only if the block
+ // actually received root-padding-left/right in its email_attrs.
+ $variables_map = $styles['__variables_map'] ?? array();
+ $styles['spacing']['padding']['left'] = '0px';
+ $styles['spacing']['padding']['right'] = '0px';
+
+ return $this->calculate_widths( $parsed_blocks, $layout, $styles, $variables_map );
+ }
+
+ /**
+ * Recursively calculate block widths based on layout and parent padding.
+ *
+ * At the top level, root padding is zeroed out by preprocess() since it's
+ * distributed to individual blocks. Each block that received root padding
+ * from the Spacing_Preprocessor has its width reduced accordingly. For
+ * nested blocks, the parent block's own padding is subtracted as expected.
+ *
+ * @param array $parsed_blocks Parsed blocks.
+ * @param array $layout Layout settings.
+ * @param array $styles Styles with padding from parent context.
+ * @param array $variables_map CSS variable names to resolved pixel values.
+ * @return array
+ */
+ private function calculate_widths( array $parsed_blocks, array $layout, array $styles, array $variables_map = array() ): array {
foreach ( $parsed_blocks as $key => $block ) {
- // Layout width is recalculated for each block because full-width blocks don't exclude padding.
$layout_width = $this->parse_number_from_string_with_pixels( $layout['contentSize'] );
$alignment = $block['attrs']['align'] ?? null;
- // Subtract padding from the block width if it's not full-width.
+ // Subtract parent padding from block width if not full-width.
if ( 'full' !== $alignment ) {
$layout_width -= $this->parse_number_from_string_with_pixels( $styles['spacing']['padding']['left'] ?? '0px' );
$layout_width -= $this->parse_number_from_string_with_pixels( $styles['spacing']['padding']['right'] ?? '0px' );
}
+ // Subtract root padding for blocks that will receive it as CSS padding
+ // from Content_Renderer. This ensures block widths fit inside the
+ // root padding wrapper without overflow.
+ if ( 'full' !== $alignment ) {
+ $layout_width -= $this->parse_number_from_string_with_pixels( $block['email_attrs']['root-padding-left'] ?? '0px' );
+ $layout_width -= $this->parse_number_from_string_with_pixels( $block['email_attrs']['root-padding-right'] ?? '0px' );
+ }
+
+ // Resolve block padding — may be preset references like var:preset|spacing|20.
+ $block_padding_left = $this->resolve_preset_value( $block['attrs']['style']['spacing']['padding']['left'] ?? '0px', $variables_map );
+ $block_padding_right = $this->resolve_preset_value( $block['attrs']['style']['spacing']['padding']['right'] ?? '0px', $variables_map );
+
$width_input = $block['attrs']['width'] ?? '100%';
// Currently we support only % and px units in case only the number is provided we assume it's %
// because editor saves percent values as a number.
@@ -42,23 +79,23 @@ class Blocks_Width_Preprocessor implements Preprocessor {
if ( 'core/columns' === $block['blockName'] ) {
// Calculate width of the columns based on the layout width and padding.
$columns_width = $layout_width;
- $columns_width -= $this->parse_number_from_string_with_pixels( $block['attrs']['style']['spacing']['padding']['left'] ?? '0px' );
- $columns_width -= $this->parse_number_from_string_with_pixels( $block['attrs']['style']['spacing']['padding']['right'] ?? '0px' );
+ $columns_width -= $this->parse_number_from_string_with_pixels( $block_padding_left );
+ $columns_width -= $this->parse_number_from_string_with_pixels( $block_padding_right );
$border_width = $block['attrs']['style']['border']['width'] ?? '0px';
$columns_width -= $this->parse_number_from_string_with_pixels( $block['attrs']['style']['border']['left']['width'] ?? $border_width );
$columns_width -= $this->parse_number_from_string_with_pixels( $block['attrs']['style']['border']['right']['width'] ?? $border_width );
- $block['innerBlocks'] = $this->add_missing_column_widths( $block['innerBlocks'], $columns_width );
+ $block['innerBlocks'] = $this->add_missing_column_widths( $block['innerBlocks'], $columns_width, $variables_map );
}
- // Copy layout styles and update width and padding.
+ // Copy layout styles and update width and padding with resolved values.
$modified_layout = $layout;
$modified_layout['contentSize'] = "{$width}px";
$modified_styles = $styles;
- $modified_styles['spacing']['padding']['left'] = $block['attrs']['style']['spacing']['padding']['left'] ?? '0px';
- $modified_styles['spacing']['padding']['right'] = $block['attrs']['style']['spacing']['padding']['right'] ?? '0px';
+ $modified_styles['spacing']['padding']['left'] = $block_padding_left;
+ $modified_styles['spacing']['padding']['right'] = $block_padding_right;
$block['email_attrs']['width'] = "{$width}px";
- $block['innerBlocks'] = $this->preprocess( $block['innerBlocks'], $modified_layout, $modified_styles );
+ $block['innerBlocks'] = $this->calculate_widths( $block['innerBlocks'], $modified_layout, $modified_styles, $variables_map );
$parsed_blocks[ $key ] = $block;
}
return $parsed_blocks;
@@ -94,14 +131,36 @@ class Blocks_Width_Preprocessor implements Preprocessor {
return (float) str_replace( 'px', '', $value );
}
+ /**
+ * Resolve a CSS value that may contain a preset variable reference.
+ *
+ * Block attributes store padding as preset references like
+ * "var:preset|spacing|20" which resolve to actual pixel values
+ * (e.g. "8px"). This method converts the reference to its resolved
+ * value using the variables map passed through styles.
+ *
+ * @param string $value The CSS value, possibly a preset reference.
+ * @param array $variables_map Map of CSS variable names to resolved values.
+ * @return string The resolved value (e.g. "8px") or the original value.
+ */
+ private function resolve_preset_value( string $value, array $variables_map ): string {
+ if ( strpos( $value, 'var:preset|' ) !== 0 ) {
+ return $value;
+ }
+
+ $css_var_name = '--wp--' . str_replace( '|', '--', str_replace( 'var:', '', $value ) );
+ return $variables_map[ $css_var_name ] ?? $value;
+ }
+
/**
* Add missing column widths
*
* @param array $columns Columns.
* @param float $columns_width Columns width.
+ * @param array $variables_map CSS variable names to resolved pixel values.
* @return array
*/
- private function add_missing_column_widths( array $columns, float $columns_width ): array {
+ private function add_missing_column_widths( array $columns, float $columns_width, array $variables_map = array() ): array {
$columns_count_with_defined_width = 0;
$defined_column_width = 0;
$columns_count = count( $columns );
@@ -111,8 +170,8 @@ class Blocks_Width_Preprocessor implements Preprocessor {
$defined_column_width += $this->convert_width_to_pixels( $column['attrs']['width'], $columns_width );
} else {
// When width is not set we need to add padding to the defined column width for better ratio accuracy.
- $defined_column_width += $this->parse_number_from_string_with_pixels( $column['attrs']['style']['spacing']['padding']['left'] ?? '0px' );
- $defined_column_width += $this->parse_number_from_string_with_pixels( $column['attrs']['style']['spacing']['padding']['right'] ?? '0px' );
+ $defined_column_width += $this->parse_number_from_string_with_pixels( $this->resolve_preset_value( $column['attrs']['style']['spacing']['padding']['left'] ?? '0px', $variables_map ) );
+ $defined_column_width += $this->parse_number_from_string_with_pixels( $this->resolve_preset_value( $column['attrs']['style']['spacing']['padding']['right'] ?? '0px', $variables_map ) );
$border_width = $column['attrs']['style']['border']['width'] ?? '0px';
$defined_column_width += $this->parse_number_from_string_with_pixels( $column['attrs']['style']['border']['left']['width'] ?? $border_width );
$defined_column_width += $this->parse_number_from_string_with_pixels( $column['attrs']['style']['border']['right']['width'] ?? $border_width );
@@ -125,8 +184,8 @@ class Blocks_Width_Preprocessor implements Preprocessor {
if ( ! isset( $column['attrs']['width'] ) || empty( $column['attrs']['width'] ) ) {
// Add padding to the specific column width because it's not included in the default width.
$column_width = $default_columns_width;
- $column_width += $this->parse_number_from_string_with_pixels( $column['attrs']['style']['spacing']['padding']['left'] ?? '0px' );
- $column_width += $this->parse_number_from_string_with_pixels( $column['attrs']['style']['spacing']['padding']['right'] ?? '0px' );
+ $column_width += $this->parse_number_from_string_with_pixels( $this->resolve_preset_value( $column['attrs']['style']['spacing']['padding']['left'] ?? '0px', $variables_map ) );
+ $column_width += $this->parse_number_from_string_with_pixels( $this->resolve_preset_value( $column['attrs']['style']['spacing']['padding']['right'] ?? '0px', $variables_map ) );
$border_width = $column['attrs']['style']['border']['width'] ?? '0px';
$column_width += $this->parse_number_from_string_with_pixels( $column['attrs']['style']['border']['left']['width'] ?? $border_width );
$column_width += $this->parse_number_from_string_with_pixels( $column['attrs']['style']['border']['right']['width'] ?? $border_width );
diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-cleanup-preprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-cleanup-preprocessor.php
index ec60829c408..922be940f22 100644
--- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-cleanup-preprocessor.php
+++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-cleanup-preprocessor.php
@@ -15,9 +15,9 @@ class Cleanup_Preprocessor implements Preprocessor {
/**
* Method to preprocess the content before rendering
*
- * @param array $parsed_blocks Parsed blocks of the email.
- * @param array{contentSize: string} $layout Layout of the email.
- * @param array{spacing: array{padding: array{bottom: string, left: string, right: string, top: string}, blockGap: string}} $styles Styles of the email.
+ * @param array $parsed_blocks Parsed blocks of the email.
+ * @param array{contentSize: string} $layout Layout of the email.
+ * @param array{spacing: array{padding: array{bottom: string, left?: string, right?: string, top: string}, blockGap: string}} $styles Styles of the email.
* @return array
*/
public function preprocess( array $parsed_blocks, array $layout, array $styles ): array {
diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-quote-preprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-quote-preprocessor.php
index e385a64f20b..415f6cdbfe0 100644
--- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-quote-preprocessor.php
+++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-quote-preprocessor.php
@@ -15,9 +15,9 @@ class Quote_Preprocessor implements Preprocessor {
/**
* Method to preprocess the content before rendering
*
- * @param array $parsed_blocks Parsed blocks of the email.
- * @param array{contentSize: string} $layout Layout of the email.
- * @param array{spacing: array{padding: array{bottom: string, left: string, right: string, top: string}, blockGap: string}} $styles Styles of the email.
+ * @param array $parsed_blocks Parsed blocks of the email.
+ * @param array{contentSize: string} $layout Layout of the email.
+ * @param array{spacing: array{padding: array{bottom: string, left?: string, right?: string, top: string}, blockGap: string}} $styles Styles of the email.
* @return array
*/
public function preprocess( array $parsed_blocks, array $layout, array $styles ): array {
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 4ffa638fda3..14ee33b0810 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
@@ -13,6 +13,13 @@ namespace Automattic\WooCommerce\EmailEditor\Engine\Renderer\ContentRenderer\Pre
* In the early development phase, we are setting only margin-top for blocks that are not first or last in the columns block.
*/
class Spacing_Preprocessor implements Preprocessor {
+ /**
+ * Cached post-content block names to avoid repeated apply_filters calls.
+ *
+ * @var string[]|null
+ */
+ private ?array $post_content_block_names = null;
+
/**
* Preprocesses the parsed blocks.
*
@@ -22,8 +29,7 @@ class Spacing_Preprocessor implements Preprocessor {
* @return array
*/
public function preprocess( array $parsed_blocks, array $layout, array $styles ): array {
- $root_padding = $this->get_root_padding( $styles );
-
+ $root_padding = $this->get_root_padding( $styles );
$parsed_blocks = $this->add_block_gaps( $parsed_blocks, $styles['spacing']['blockGap'] ?? '', null, $root_padding );
return $parsed_blocks;
}
@@ -43,6 +49,13 @@ class Spacing_Preprocessor implements Preprocessor {
* padding to their children instead of taking it themselves. This enables
* alignfull blocks to skip root padding and span the full email width.
*
+ * Blocks fall into three categories for root padding:
+ * - Zero padding (has_zero_padding): skip root padding entirely — edge-to-edge intent.
+ * - Non-zero explicit padding (has_own_padding, !has_zero_padding): receive root padding
+ * on top of their own padding. Their own padding is internal content spacing; root
+ * padding ensures inset from the email edge. These blocks also stop delegation.
+ * - No explicit padding: receive root padding if delegated, or delegate if a container.
+ *
* @param array $parsed_blocks Parsed blocks.
* @param string $gap Gap.
* @param array|null $parent_block Parent block.
@@ -87,13 +100,15 @@ class Spacing_Preprocessor implements Preprocessor {
$is_root_level = null === $parent_block;
$is_container = in_array( $block_name, self::CONTAINER_BLOCKS, true );
$alignment = $block['attrs']['align'] ?? null;
+ $has_zero_padding = $this->has_zero_horizontal_padding( $block );
$has_own_padding = $this->has_explicit_horizontal_padding( $block );
$wraps_post_content = $apply_root_padding && $is_container && ! $has_own_padding && $this->contains_post_content( $block );
- $should_apply = $apply_root_padding || ( $is_root_level && ! $is_container );
+ $should_apply = $apply_root_padding || ( $is_root_level && ! $is_container ) || ( $is_root_level && $is_container && $has_own_padding );
- if ( $should_apply && ! $has_own_padding && 'full' !== $alignment && 'core/post-content' !== $block_name && ! $wraps_post_content && ! empty( $root_padding ) ) {
- $block['email_attrs']['padding-left'] = $root_padding['left'];
- $block['email_attrs']['padding-right'] = $root_padding['right'];
+ $post_content_block_names = $this->get_post_content_block_names();
+ if ( $should_apply && ! $has_zero_padding && 'full' !== $alignment && ! in_array( $block_name, $post_content_block_names, true ) && ! $wraps_post_content && ! empty( $root_padding ) ) {
+ $block['email_attrs']['root-padding-left'] = $root_padding['left'];
+ $block['email_attrs']['root-padding-right'] = $root_padding['right'];
}
// Determine whether children should receive root padding delegation.
@@ -106,7 +121,7 @@ class Spacing_Preprocessor implements Preprocessor {
$children_apply = false;
if ( $is_root_level && $is_container && ! $has_own_padding ) {
$children_apply = true;
- } elseif ( $apply_root_padding && 'core/post-content' === $block_name ) {
+ } elseif ( $apply_root_padding && in_array( $block_name, $post_content_block_names, true ) ) {
$children_apply = true;
} elseif ( $wraps_post_content ) {
$children_apply = true;
@@ -119,6 +134,24 @@ class Spacing_Preprocessor implements Preprocessor {
return $parsed_blocks;
}
+ /**
+ * Returns the list of block names treated as "post content" for padding delegation.
+ *
+ * Filterable so that integrations can register custom post-content-like blocks
+ * without modifying this file.
+ *
+ * @return string[]
+ */
+ private function get_post_content_block_names(): array {
+ if ( null === $this->post_content_block_names ) {
+ $this->post_content_block_names = (array) apply_filters(
+ 'woocommerce_email_editor_post_content_block_names',
+ array( 'core/post-content' )
+ );
+ }
+ return $this->post_content_block_names;
+ }
+
/**
* Checks whether a block contains a core/post-content descendant.
*
@@ -130,9 +163,10 @@ class Spacing_Preprocessor implements Preprocessor {
* @return bool True if the block has a post-content descendant.
*/
private function contains_post_content( array $block ): bool {
+ $post_content_block_names = $this->get_post_content_block_names();
foreach ( $block['innerBlocks'] ?? array() as $inner_block ) {
$name = $inner_block['blockName'] ?? '';
- if ( 'core/post-content' === $name ) {
+ if ( in_array( $name, $post_content_block_names, true ) ) {
return true;
}
if ( in_array( $name, self::CONTAINER_BLOCKS, true ) && $this->contains_post_content( $inner_block ) ) {
@@ -143,12 +177,31 @@ class Spacing_Preprocessor implements Preprocessor {
}
/**
- * Checks whether a block explicitly defines its own horizontal padding.
+ * Checks whether a block explicitly sets zero horizontal padding.
+ *
+ * Explicit zero padding (0, 0px, 0em, etc.) signals that the block
+ * intentionally wants edge-to-edge layout. Root padding should not
+ * be added on top.
*
- * When a block has explicit padding-left or padding-right in its style
- * attributes, it is managing its own layout. Root padding should not
- * be added on top, and containers with explicit padding should not
- * delegate root padding to their children.
+ * Non-zero padding (e.g. 20px) is internal content spacing and does
+ * not affect root padding — both can coexist independently.
+ *
+ * @param array $block The block to check.
+ * @return bool True if the block explicitly sets zero horizontal padding.
+ */
+ private function has_zero_horizontal_padding( array $block ): bool {
+ $padding = $block['attrs']['style']['spacing']['padding'] ?? array();
+ $left = $padding['left'] ?? null;
+ $right = $padding['right'] ?? null;
+
+ return $this->is_zero_value( $left ) || $this->is_zero_value( $right );
+ }
+
+ /**
+ * Checks whether a block explicitly defines any horizontal padding.
+ *
+ * Containers with explicit padding (any value) manage their own
+ * layout and should stop delegating root padding to their children.
*
* @param array $block The block to check.
* @return bool True if the block defines horizontal padding.
@@ -158,6 +211,22 @@ class Spacing_Preprocessor implements Preprocessor {
return isset( $padding['left'] ) || isset( $padding['right'] );
}
+ /**
+ * Checks whether a CSS value is explicitly zero.
+ *
+ * Matches '0', '0px', '0em', '0rem', '0%', etc.
+ *
+ * @param mixed $value The CSS value to check.
+ * @return bool True if the value is explicitly zero.
+ */
+ private function is_zero_value( $value ): bool {
+ if ( ! is_string( $value ) && ! is_numeric( $value ) ) {
+ return false;
+ }
+
+ return (bool) preg_match( '/^0(%|[a-z]*)?$/i', trim( (string) $value ) );
+ }
+
/**
* Extracts and sanitizes root horizontal padding from theme styles.
*
diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-typography-preprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-typography-preprocessor.php
index a56f1c3571c..ad5001f6225 100644
--- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-typography-preprocessor.php
+++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/class-typography-preprocessor.php
@@ -46,9 +46,9 @@ class Typography_Preprocessor implements Preprocessor {
/**
* Method to preprocess the content before rendering
*
- * @param array $parsed_blocks Parsed blocks of the email.
- * @param array{contentSize: string} $layout Layout of the email.
- * @param array{spacing: array{padding: array{bottom: string, left: string, right: string, top: string}, blockGap: string}} $styles Styles of the email.
+ * @param array $parsed_blocks Parsed blocks of the email.
+ * @param array{contentSize: string} $layout Layout of the email.
+ * @param array{spacing: array{padding: array{bottom: string, left?: string, right?: string, top: string}, blockGap: string}} $styles Styles of the email.
* @return array
*/
public function preprocess( array $parsed_blocks, array $layout, array $styles ): array {
diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/interface-preprocessor.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/interface-preprocessor.php
index aa3f5d10f21..883a8aa29bc 100644
--- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/interface-preprocessor.php
+++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/Preprocessors/interface-preprocessor.php
@@ -15,9 +15,9 @@ interface Preprocessor {
/**
* Method to preprocess the content before rendering
*
- * @param array $parsed_blocks Parsed blocks of the email.
- * @param array{contentSize: string, wideSize?: string, allowEditing?: bool, allowCustomContentAndWideSize?: bool} $layout Layout of the email.
- * @param array{spacing: array{padding: array{bottom: string, left: string, right: string, top: string}, blockGap: string}} $styles Styles of the email.
+ * @param array $parsed_blocks Parsed blocks of the email.
+ * @param array{contentSize: string, wideSize?: string, allowEditing?: bool, allowCustomContentAndWideSize?: bool} $layout Layout of the email.
+ * @param array{spacing: array{padding: array{bottom: string, left?: string, right?: string, top: string}, blockGap: string}} $styles Styles of the email.
* @return array
*/
public function preprocess( array $parsed_blocks, array $layout, array $styles ): array;
diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-content-renderer.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-content-renderer.php
index 448fa0bdc2b..1858f51b740 100644
--- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-content-renderer.php
+++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-content-renderer.php
@@ -13,9 +13,11 @@ use Automattic\WooCommerce\EmailEditor\Engine\Renderer\Css_Inliner;
use Automattic\WooCommerce\EmailEditor\Engine\Theme_Controller;
use Automattic\WooCommerce\EmailEditor\Integrations\Core\Renderer\Blocks\Fallback;
use Automattic\WooCommerce\EmailEditor\Integrations\Core\Renderer\Blocks\Post_Content;
+use Automattic\WooCommerce\EmailEditor\Integrations\Utils\Table_Wrapper_Helper;
use WP_Block_Template;
use WP_Block_Type_Registry;
use WP_Post;
+use WP_Style_Engine;
/**
* Class Content_Renderer
@@ -93,6 +95,19 @@ class Content_Renderer {
*/
private $backup_post_content_callback;
+ /**
+ * Post-content block's calculated width from the first preprocessing pass.
+ *
+ * When this is narrower than contentSize, it means root padding was applied
+ * to a container above post-content. In that case, the second preprocessing
+ * pass (user blocks) must skip root padding to prevent double application.
+ * When equal to contentSize, the template delegates root padding and user
+ * blocks should receive it directly.
+ *
+ * @var string|null
+ */
+ private ?string $post_content_width = null;
+
/**
* CSS inliner
*
@@ -208,8 +223,65 @@ class Content_Renderer {
*/
public function preprocess_parsed_blocks( array $parsed_blocks ): array {
$styles = $this->theme_controller->get_styles();
+ $layout = $this->theme_controller->get_layout_settings();
+
+ // Pass the CSS variables map so preprocessors can resolve preset
+ // references (e.g. var:preset|spacing|20) in block attributes.
+ $styles['__variables_map'] = $this->theme_controller->get_variables_values_map();
+
+ // Second pass (user blocks inside post-content): if root padding was
+ // applied to a container above post-content in the first pass (indicated
+ // by post_content_width < contentSize), remove root padding from styles
+ // to prevent double application. If the template delegates root padding
+ // (post_content_width == contentSize), keep it for user blocks.
+ if ( null !== $this->post_content_width ) {
+ $post_content_num = (float) str_replace( 'px', '', $this->post_content_width );
+ $content_size_num = (float) str_replace( 'px', '', $layout['contentSize'] );
+ // Use epsilon tolerance for floating-point comparison since width
+ // calculations involve round() and division that may produce imprecision.
+ if ( $post_content_num < $content_size_num - 0.01 ) {
+ unset( $styles['spacing']['padding']['left'], $styles['spacing']['padding']['right'] );
+ }
+ }
+
+ $result = $this->process_manager->preprocess( $parsed_blocks, $layout, $styles );
+
+ // After the first pass: find the post-content block's width.
+ if ( null === $this->post_content_width ) {
+ $this->post_content_width = $this->find_post_content_width( $result );
+ }
- return $this->process_manager->preprocess( $parsed_blocks, $this->theme_controller->get_layout_settings(), $styles );
+ return $result;
+ }
+
+ /**
+ * Recursively find the post-content block's width in preprocessed blocks.
+ *
+ * @param array $blocks Preprocessed blocks.
+ * @param array|null $post_content_block_names Cached block names for recursion.
+ * @return string|null The post-content block's width or null if not found.
+ */
+ private function find_post_content_width( array $blocks, ?array $post_content_block_names = null ): ?string {
+ if ( null === $post_content_block_names ) {
+ $post_content_block_names = (array) apply_filters(
+ 'woocommerce_email_editor_post_content_block_names',
+ array( 'core/post-content' )
+ );
+ }
+
+ foreach ( $blocks as $block ) {
+ $block_name = $block['blockName'] ?? '';
+ if ( in_array( $block_name, $post_content_block_names, true ) ) {
+ return $block['email_attrs']['width'] ?? null;
+ }
+ if ( ! empty( $block['innerBlocks'] ) ) {
+ $found = $this->find_post_content_width( $block['innerBlocks'], $post_content_block_names );
+ if ( null !== $found ) {
+ return $found;
+ }
+ }
+ }
+ return null;
}
/**
@@ -246,9 +318,10 @@ class Content_Renderer {
$context = new Rendering_Context( $this->theme_controller->get_theme(), $email_context );
$block_type = $this->block_type_registry->get_registered( $parsed_block['blockName'] );
+ $result = null;
try {
if ( $block_type && isset( $block_type->render_email_callback ) && is_callable( $block_type->render_email_callback ) ) {
- return call_user_func( $block_type->render_email_callback, $block_content, $parsed_block, $context );
+ $result = call_user_func( $block_type->render_email_callback, $block_content, $parsed_block, $context );
}
} catch ( \Exception $error ) {
$this->logger->error(
@@ -264,7 +337,58 @@ class Content_Renderer {
return $block_content;
}
- return $this->fallback_renderer->render( $block_content, $parsed_block, $context );
+ if ( null === $result ) {
+ $result = $this->fallback_renderer->render( $block_content, $parsed_block, $context );
+ }
+
+ return $this->add_root_horizontal_padding( $result, $parsed_block['email_attrs'] ?? array() );
+ }
+
+ /**
+ * Wrap block output with root horizontal padding.
+ *
+ * Root padding is distributed by the Spacing_Preprocessor from the outer
+ * email container to individual blocks. This method applies it uniformly
+ * to all blocks regardless of whether they use Abstract_Block_Renderer
+ * or a custom render_email_callback.
+ *
+ * @param string $content The rendered block content.
+ * @param array $email_attrs The email attributes from the parsed block.
+ * @return string The content wrapped with horizontal padding, or unchanged if no root padding.
+ */
+ private function add_root_horizontal_padding( string $content, array $email_attrs ): string {
+ $css_attrs = array();
+ if ( isset( $email_attrs['root-padding-left'] ) ) {
+ $css_attrs['padding-left'] = $email_attrs['root-padding-left'];
+ }
+ if ( isset( $email_attrs['root-padding-right'] ) ) {
+ $css_attrs['padding-right'] = $email_attrs['root-padding-right'];
+ }
+ if ( empty( $css_attrs ) ) {
+ return $content;
+ }
+
+ $padding_style = WP_Style_Engine::compile_css( $css_attrs, '' );
+ if ( empty( $padding_style ) ) {
+ return $content;
+ }
+
+ $table_attrs = array(
+ 'align' => 'left',
+ 'width' => '100%',
+ );
+
+ $cell_attrs = array(
+ 'style' => $padding_style,
+ );
+
+ $div_content = sprintf(
+ '<div class="email-root-padding" style="%1$s">%2$s</div>',
+ esc_attr( $padding_style ),
+ $content
+ );
+
+ return Table_Wrapper_Helper::render_outlook_table_wrapper( $div_content, $table_attrs, $cell_attrs );
}
/**
@@ -299,6 +423,8 @@ class Content_Renderer {
remove_filter( 'block_parser_class', array( $this, 'block_parser' ) );
remove_filter( 'woocommerce_email_blocks_renderer_parsed_blocks', array( $this, 'preprocess_parsed_blocks' ) );
+ $this->post_content_width = null;
+
// Restore the original core/post-content render callback.
// Note: We always restore it, even if it was null originally.
$post_content_type = $this->block_type_registry->get_registered( 'core/post-content' );
diff --git a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php
index 97644efe1cc..699ceb0655e 100644
--- a/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php
+++ b/packages/php/email-editor/src/Engine/Renderer/ContentRenderer/class-process-manager.php
@@ -60,9 +60,12 @@ class Process_Manager {
Border_Style_Postprocessor $border_style_postprocessor
) {
$this->register_preprocessor( $cleanup_preprocessor );
+ // Spacing must run before Width: it sets root-padding-left/right in
+ // email_attrs, which the Width preprocessor reads to subtract root
+ // padding only from blocks that actually receive it.
+ $this->register_preprocessor( $spacing_preprocessor );
$this->register_preprocessor( $blocks_width_preprocessor );
$this->register_preprocessor( $typography_preprocessor );
- $this->register_preprocessor( $spacing_preprocessor );
$this->register_preprocessor( $quote_preprocessor );
$this->register_postprocessor( $highlighting_postprocessor );
$this->register_postprocessor( $border_style_postprocessor );
@@ -72,9 +75,9 @@ class Process_Manager {
/**
* Method to preprocess blocks
*
- * @param array $parsed_blocks Parsed blocks.
- * @param array{contentSize: string, wideSize?: string, allowEditing?: bool, allowCustomContentAndWideSize?: bool} $layout Layout.
- * @param array{spacing: array{padding: array{bottom: string, left: string, right: string, top: string}, blockGap: string}} $styles Styles.
+ * @param array $parsed_blocks Parsed blocks.
+ * @param array{contentSize: string, wideSize?: string, allowEditing?: bool, allowCustomContentAndWideSize?: bool} $layout Layout.
+ * @param array{spacing: array{padding: array{bottom: string, left?: string, right?: string, top: string}, blockGap: string}} $styles Styles.
* @return array
*/
public function preprocess( array $parsed_blocks, array $layout, array $styles ): array {
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php
index d0e45f400fb..a6671ac2a3f 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php
@@ -58,7 +58,11 @@ abstract class Abstract_Block_Renderer implements Block_Renderer {
}
/**
- * Add a spacer around the block.
+ * Add a spacer around the block for vertical spacing (margin-top).
+ *
+ * Horizontal root padding is applied uniformly by Content_Renderer::render_block()
+ * so that all blocks — including those using render_email_callback without
+ * Abstract_Block_Renderer — receive consistent padding.
*
* @param string $content The block content.
* @param array $email_attrs The email attributes.
@@ -71,8 +75,7 @@ abstract class Abstract_Block_Renderer implements Block_Renderer {
$margin_top_attrs = array();
}
- $gap_style = WP_Style_Engine::compile_css( $margin_top_attrs, '' ) ?? '';
- $padding_style = WP_Style_Engine::compile_css( array_intersect_key( $email_attrs, array_flip( array( 'padding-left', 'padding-right' ) ) ), '' ) ?? '';
+ $gap_style = WP_Style_Engine::compile_css( $margin_top_attrs, '' ) ?? '';
$table_attrs = array(
'align' => 'left',
@@ -80,18 +83,13 @@ abstract class Abstract_Block_Renderer implements Block_Renderer {
'style' => $gap_style,
);
- $cell_attrs = array(
- 'style' => $padding_style,
- );
-
$div_content = sprintf(
- '<div class="email-block-layout" style="%1$s %2$s">%3$s</div>',
+ '<div class="email-block-layout" style="%1$s">%2$s</div>',
esc_attr( $gap_style ),
- esc_attr( $padding_style ),
$content
);
- return Table_Wrapper_Helper::render_outlook_table_wrapper( $div_content, $table_attrs, $cell_attrs );
+ return Table_Wrapper_Helper::render_outlook_table_wrapper( $div_content, $table_attrs );
}
/**
diff --git a/packages/php/email-editor/tests/integration/Engine/Renderer/ContentRenderer/Content_Renderer_Test.php b/packages/php/email-editor/tests/integration/Engine/Renderer/ContentRenderer/Content_Renderer_Test.php
index b58685c077b..60e6d995392 100644
--- a/packages/php/email-editor/tests/integration/Engine/Renderer/ContentRenderer/Content_Renderer_Test.php
+++ b/packages/php/email-editor/tests/integration/Engine/Renderer/ContentRenderer/Content_Renderer_Test.php
@@ -149,6 +149,157 @@ class Content_Renderer_Test extends \Email_Editor_Integration_Test_Case {
\WP_Block_Type_Registry::get_instance()->unregister( 'test/block' );
}
+ /**
+ * Test render_block applies root horizontal padding from email_attrs
+ */
+ public function testItAppliesRootHorizontalPadding(): void {
+ register_block_type(
+ 'test/padded-block',
+ array(
+ 'render_email_callback' => function () {
+ return '<p>padded content</p>';
+ },
+ )
+ );
+
+ $result = $this->renderer->render_block(
+ 'content',
+ array(
+ 'blockName' => 'test/padded-block',
+ 'email_attrs' => array(
+ 'root-padding-left' => '24px',
+ 'root-padding-right' => '24px',
+ ),
+ )
+ );
+
+ $this->assertStringContainsString( 'padded content', $result );
+ $this->assertStringContainsString( 'email-root-padding', $result );
+ $this->assertStringContainsString( 'padding-left:24px', $result );
+ $this->assertStringContainsString( 'padding-right:24px', $result );
+ \WP_Block_Type_Registry::get_instance()->unregister( 'test/padded-block' );
+ }
+
+ /**
+ * Test render_block skips root padding when no root-padding attrs are set
+ */
+ public function testItSkipsRootPaddingWhenNotSet(): void {
+ register_block_type(
+ 'test/no-padding-block',
+ array(
+ 'render_email_callback' => function () {
+ return '<p>no padding</p>';
+ },
+ )
+ );
+
+ $result = $this->renderer->render_block(
+ 'content',
+ array(
+ 'blockName' => 'test/no-padding-block',
+ 'email_attrs' => array(
+ 'margin-top' => '10px',
+ ),
+ )
+ );
+
+ $this->assertEquals( '<p>no padding</p>', $result );
+ $this->assertStringNotContainsString( 'email-root-padding', $result );
+ \WP_Block_Type_Registry::get_instance()->unregister( 'test/no-padding-block' );
+ }
+
+ /**
+ * Test preprocess_parsed_blocks skips root padding in second pass when
+ * a container above post-content absorbed it (WooCommerce template pattern).
+ */
+ public function testItSkipsRootPaddingInSecondPassWhenAbsorbed(): void {
+ // First pass: template blocks with a group wrapping post-content.
+ // The group has own padding so it absorbs root padding and stops delegation.
+ $template_blocks = array(
+ array(
+ 'blockName' => 'core/group',
+ 'attrs' => array(
+ 'style' => array(
+ 'spacing' => array(
+ 'padding' => array(
+ 'left' => '20px',
+ 'right' => '20px',
+ ),
+ ),
+ ),
+ ),
+ 'innerBlocks' => array(
+ array(
+ 'blockName' => 'core/post-content',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ ),
+ ),
+ );
+
+ $first_result = $this->renderer->preprocess_parsed_blocks( $template_blocks );
+
+ // post-content should have a narrower width than contentSize
+ // (root padding + group padding subtracted).
+ $post_content = $first_result[0]['innerBlocks'][0];
+ $this->assertArrayHasKey( 'width', $post_content['email_attrs'] );
+
+ // Second pass: user blocks (simulating post-content rendering).
+ $user_blocks = array(
+ array(
+ 'blockName' => 'core/paragraph',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ );
+
+ $second_result = $this->renderer->preprocess_parsed_blocks( $user_blocks );
+
+ // User blocks should NOT have root padding (it was absorbed upstream).
+ $this->assertArrayNotHasKey( 'root-padding-left', $second_result[0]['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $second_result[0]['email_attrs'] );
+ }
+
+ /**
+ * Test preprocess_parsed_blocks keeps root padding in second pass when
+ * the template delegates (MailPoet template pattern).
+ */
+ public function testItKeepsRootPaddingInSecondPassWhenDelegated(): void {
+ // First pass: template blocks with a group that has NO own padding.
+ // The group delegates root padding to children.
+ $template_blocks = array(
+ array(
+ 'blockName' => 'core/group',
+ 'attrs' => array(),
+ 'innerBlocks' => array(
+ array(
+ 'blockName' => 'core/post-content',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ ),
+ ),
+ );
+
+ $this->renderer->preprocess_parsed_blocks( $template_blocks );
+
+ // Second pass: user blocks.
+ $user_blocks = array(
+ array(
+ 'blockName' => 'core/paragraph',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ );
+
+ $second_result = $this->renderer->preprocess_parsed_blocks( $user_blocks );
+
+ // User blocks SHOULD have root padding (template delegated, not absorbed).
+ $this->assertArrayHasKey( 'root-padding-left', $second_result[0]['email_attrs'] );
+ $this->assertArrayHasKey( 'root-padding-right', $second_result[0]['email_attrs'] );
+ }
+
/**
* Get the value of the style attribute for a given tag in the HTML.
*
diff --git a/packages/php/email-editor/tests/unit/Engine/Renderer/ContentRenderer/Preprocessors/Blocks_Width_Preprocessor_Test.php b/packages/php/email-editor/tests/unit/Engine/Renderer/ContentRenderer/Preprocessors/Blocks_Width_Preprocessor_Test.php
index 2c013584026..d503859c6bc 100644
--- a/packages/php/email-editor/tests/unit/Engine/Renderer/ContentRenderer/Preprocessors/Blocks_Width_Preprocessor_Test.php
+++ b/packages/php/email-editor/tests/unit/Engine/Renderer/ContentRenderer/Preprocessors/Blocks_Width_Preprocessor_Test.php
@@ -141,9 +141,9 @@ class Blocks_Width_Preprocessor_Test extends \Email_Editor_Unit_Test {
$result = $this->preprocessor->preprocess( $blocks, $this->layout, $this->styles );
$result = $result[0];
$this->assertCount( 3, $result['innerBlocks'] );
- $this->assertEquals( '211px', $result['innerBlocks'][0]['email_attrs']['width'] ); // (660 - 10 - 10) * 0.33
+ $this->assertEquals( '218px', $result['innerBlocks'][0]['email_attrs']['width'] ); // 660 * 0.33
$this->assertEquals( '100px', $result['innerBlocks'][1]['email_attrs']['width'] );
- $this->assertEquals( '128px', $result['innerBlocks'][2]['email_attrs']['width'] ); // (660 - 10 - 10) * 0.2
+ $this->assertEquals( '132px', $result['innerBlocks'][2]['email_attrs']['width'] ); // 660 * 0.2
}
/**
@@ -204,10 +204,10 @@ class Blocks_Width_Preprocessor_Test extends \Email_Editor_Unit_Test {
$inner_blocks = $result[0]['innerBlocks'];
$this->assertCount( 2, $inner_blocks );
- $this->assertEquals( '256px', $inner_blocks[0]['email_attrs']['width'] ); // (660 - 10 - 10) * 0.4
- $this->assertEquals( '236px', $inner_blocks[0]['innerBlocks'][0]['email_attrs']['width'] ); // 256 - 10 - 10
- $this->assertEquals( '384px', $inner_blocks[1]['email_attrs']['width'] ); // (660 - 10 - 10) * 0.6
- $this->assertEquals( '344px', $inner_blocks[1]['innerBlocks'][0]['email_attrs']['width'] ); // 384 - 25 - 15
+ $this->assertEquals( '264px', $inner_blocks[0]['email_attrs']['width'] ); // 660 * 0.4
+ $this->assertEquals( '244px', $inner_blocks[0]['innerBlocks'][0]['email_attrs']['width'] ); // 264 - 10 - 10
+ $this->assertEquals( '396px', $inner_blocks[1]['email_attrs']['width'] ); // 660 * 0.6
+ $this->assertEquals( '356px', $inner_blocks[1]['innerBlocks'][0]['email_attrs']['width'] ); // 396 - 25 - 15
}
/**
@@ -259,12 +259,12 @@ class Blocks_Width_Preprocessor_Test extends \Email_Editor_Unit_Test {
$inner_blocks = $result[0]['innerBlocks'];
$this->assertCount( 3, $inner_blocks );
- $this->assertEquals( '200px', $inner_blocks[0]['email_attrs']['width'] ); // (660 - 10 - 10) * 0.33
- $this->assertEquals( '200px', $inner_blocks[0]['innerBlocks'][0]['email_attrs']['width'] );
- $this->assertEquals( '200px', $inner_blocks[1]['email_attrs']['width'] ); // (660 - 10 - 10) * 0.33
- $this->assertEquals( '200px', $inner_blocks[1]['innerBlocks'][0]['email_attrs']['width'] );
- $this->assertEquals( '200px', $inner_blocks[2]['email_attrs']['width'] ); // (660 - 10 - 10) * 0.33
- $this->assertEquals( '200px', $inner_blocks[2]['innerBlocks'][0]['email_attrs']['width'] );
+ $this->assertEquals( '206.67px', $inner_blocks[0]['email_attrs']['width'] ); // 620 / 3 rounded to 2 decimal places.
+ $this->assertEquals( '207px', $inner_blocks[0]['innerBlocks'][0]['email_attrs']['width'] ); // Rounded to integer.
+ $this->assertEquals( '206.67px', $inner_blocks[1]['email_attrs']['width'] ); // 620 / 3 rounded to 2 decimal places.
+ $this->assertEquals( '207px', $inner_blocks[1]['innerBlocks'][0]['email_attrs']['width'] ); // Rounded to integer.
+ $this->assertEquals( '206.67px', $inner_blocks[2]['email_attrs']['width'] ); // 620 / 3 rounded to 2 decimal places.
+ $this->assertEquals( '207px', $inner_blocks[2]['innerBlocks'][0]['email_attrs']['width'] ); // Rounded to integer.
}
/**
@@ -311,9 +311,9 @@ class Blocks_Width_Preprocessor_Test extends \Email_Editor_Unit_Test {
$inner_blocks = $result[0]['innerBlocks'];
$this->assertCount( 3, $inner_blocks );
- $this->assertEquals( '200px', $inner_blocks[0]['email_attrs']['width'] ); // (620 - 10 - 10) * 0.3333
+ $this->assertEquals( '207px', $inner_blocks[0]['email_attrs']['width'] ); // 620 * 0.3333
$this->assertEquals( '200px', $inner_blocks[1]['email_attrs']['width'] ); // already defined.
- $this->assertEquals( '200px', $inner_blocks[2]['email_attrs']['width'] ); // 600 -200 - 200
+ $this->assertEquals( '213px', $inner_blocks[2]['email_attrs']['width'] ); // 620 - 207 - 200
}
/**
@@ -338,7 +338,49 @@ class Blocks_Width_Preprocessor_Test extends \Email_Editor_Unit_Test {
$this->assertCount( 2, $result );
$this->assertEquals( '660px', $result[0]['email_attrs']['width'] ); // full width.
- $this->assertEquals( '640px', $result[1]['email_attrs']['width'] ); // 660 - 10 - 10
+ $this->assertEquals( '660px', $result[1]['email_attrs']['width'] ); // no root padding in email_attrs, full width.
+ }
+
+ /**
+ * Test it subtracts root padding from block widths when set in email_attrs by Spacing_Preprocessor
+ */
+ public function testItSubtractsRootPaddingFromEmailAttrs(): void {
+ $blocks = array(
+ // Block with root padding (set by Spacing_Preprocessor).
+ array(
+ 'blockName' => 'core/columns',
+ 'attrs' => array(),
+ 'email_attrs' => array(
+ 'root-padding-left' => '10px',
+ 'root-padding-right' => '10px',
+ ),
+ 'innerBlocks' => array(),
+ ),
+ // Block without root padding (e.g., container that manages its own layout).
+ array(
+ 'blockName' => 'core/group',
+ 'attrs' => array(),
+ 'innerBlocks' => array(),
+ ),
+ // Full-width block with root padding should not be subtracted.
+ array(
+ 'blockName' => 'core/columns',
+ 'attrs' => array(
+ 'align' => 'full',
+ ),
+ 'email_attrs' => array(
+ 'root-padding-left' => '10px',
+ 'root-padding-right' => '10px',
+ ),
+ 'innerBlocks' => array(),
+ ),
+ );
+ $result = $this->preprocessor->preprocess( $blocks, $this->layout, $this->styles );
+
+ $this->assertCount( 3, $result );
+ $this->assertEquals( '640px', $result[0]['email_attrs']['width'] ); // 660 - 10 - 10.
+ $this->assertEquals( '660px', $result[1]['email_attrs']['width'] ); // No root padding, full width.
+ $this->assertEquals( '660px', $result[2]['email_attrs']['width'] ); // Full-width, not subtracted.
}
/**
@@ -409,8 +451,8 @@ class Blocks_Width_Preprocessor_Test extends \Email_Editor_Unit_Test {
$result = $this->preprocessor->preprocess( $blocks, $this->layout, $this->styles );
$this->assertCount( 3, $result[0]['innerBlocks'] );
$this->assertEquals( '140px', $result[0]['innerBlocks'][0]['email_attrs']['width'] );
- $this->assertEquals( '220px', $result[0]['innerBlocks'][1]['email_attrs']['width'] );
- $this->assertEquals( '240px', $result[0]['innerBlocks'][2]['email_attrs']['width'] );
+ $this->assertEquals( '230px', $result[0]['innerBlocks'][1]['email_attrs']['width'] );
+ $this->assertEquals( '250px', $result[0]['innerBlocks'][2]['email_attrs']['width'] );
$blocks = array(
array(
@@ -444,7 +486,7 @@ class Blocks_Width_Preprocessor_Test extends \Email_Editor_Unit_Test {
$result = $this->preprocessor->preprocess( $blocks, $this->layout, $this->styles );
$this->assertCount( 2, $result[0]['innerBlocks'] );
$this->assertEquals( '140px', $result[0]['innerBlocks'][0]['email_attrs']['width'] );
- $this->assertEquals( '500px', $result[0]['innerBlocks'][1]['email_attrs']['width'] );
+ $this->assertEquals( '520px', $result[0]['innerBlocks'][1]['email_attrs']['width'] );
}
/**
@@ -532,12 +574,12 @@ class Blocks_Width_Preprocessor_Test extends \Email_Editor_Unit_Test {
$result = $this->preprocessor->preprocess( $blocks, $this->layout, $this->styles );
$this->assertCount( 3, $result[0]['innerBlocks'] );
$this->assertEquals( '140px', $result[0]['innerBlocks'][0]['email_attrs']['width'] );
- $this->assertEquals( '185px', $result[0]['innerBlocks'][1]['email_attrs']['width'] );
- $this->assertEquals( '255px', $result[0]['innerBlocks'][2]['email_attrs']['width'] );
+ $this->assertEquals( '195px', $result[0]['innerBlocks'][1]['email_attrs']['width'] );
+ $this->assertEquals( '265px', $result[0]['innerBlocks'][2]['email_attrs']['width'] );
$image_block = $result[0]['innerBlocks'][1]['innerBlocks'][0];
- $this->assertEquals( '185px', $image_block['email_attrs']['width'] );
+ $this->assertEquals( '195px', $image_block['email_attrs']['width'] );
$image_block = $result[0]['innerBlocks'][2]['innerBlocks'][0];
- $this->assertEquals( '215px', $image_block['email_attrs']['width'] );
+ $this->assertEquals( '225px', $image_block['email_attrs']['width'] );
}
/**
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 3411648d097..267f1f9cdf8 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,15 +112,23 @@ 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'] );
- // Root-level blocks should have root padding.
- $this->assertEquals( '10px', $first_columns['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $first_columns['email_attrs']['padding-right'] );
- $this->assertEquals( '10px', $second_columns['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $second_columns['email_attrs']['padding-right'] );
+ // Root-level blocks should have root padding (new keys).
+ $this->assertEquals( '10px', $first_columns['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $first_columns['email_attrs']['root-padding-right'] );
+ $this->assertEquals( '10px', $second_columns['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $second_columns['email_attrs']['root-padding-right'] );
+
+ // Legacy padding keys should not be present.
+ $this->assertArrayNotHasKey( 'padding-left', $first_columns['email_attrs'] );
+ $this->assertArrayNotHasKey( 'padding-right', $first_columns['email_attrs'] );
+ $this->assertArrayNotHasKey( 'padding-left', $second_columns['email_attrs'] );
+ $this->assertArrayNotHasKey( 'padding-right', $second_columns['email_attrs'] );
// Nested blocks should not have root padding.
- $this->assertArrayNotHasKey( 'padding-right', $nested_column_first_item['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $nested_column_second_item['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $nested_column_first_item['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $nested_column_first_item['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $nested_column_second_item['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $nested_column_second_item['email_attrs'] );
}
/**
@@ -283,14 +291,14 @@ class Spacing_Preprocessor_Test extends \Email_Editor_Unit_Test {
$second_child = $post_content['innerBlocks'][1];
// core/post-content itself should NOT get root padding (it's a pass-through).
- $this->assertArrayNotHasKey( 'padding-left', $post_content['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $post_content['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $post_content['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $post_content['email_attrs'] );
// Direct children of post-content should get root padding.
- $this->assertEquals( '10px', $first_child['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $first_child['email_attrs']['padding-right'] );
- $this->assertEquals( '10px', $second_child['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $second_child['email_attrs']['padding-right'] );
+ $this->assertEquals( '10px', $first_child['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $first_child['email_attrs']['root-padding-right'] );
+ $this->assertEquals( '10px', $second_child['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $second_child['email_attrs']['root-padding-right'] );
}
/**
@@ -329,18 +337,18 @@ class Spacing_Preprocessor_Test extends \Email_Editor_Unit_Test {
$second_child = $post_content['innerBlocks'][1];
// Root-level group is a container — it does NOT get root padding itself.
- $this->assertArrayNotHasKey( 'padding-left', $group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $group['email_attrs'] );
// Nested post-content should NOT get root padding (post-content never gets padding).
- $this->assertArrayNotHasKey( 'padding-left', $post_content['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $post_content['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $post_content['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $post_content['email_attrs'] );
// Children of post-content inside root group SHOULD get padding (delegation chain).
- $this->assertEquals( '10px', $first_child['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $first_child['email_attrs']['padding-right'] );
- $this->assertEquals( '10px', $second_child['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $second_child['email_attrs']['padding-right'] );
+ $this->assertEquals( '10px', $first_child['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $first_child['email_attrs']['root-padding-right'] );
+ $this->assertEquals( '10px', $second_child['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $second_child['email_attrs']['root-padding-right'] );
}
/**
@@ -391,24 +399,24 @@ class Spacing_Preprocessor_Test extends \Email_Editor_Unit_Test {
$footer_group = $root_group['innerBlocks'][2];
// Root group: no padding (root container delegates).
- $this->assertArrayNotHasKey( 'padding-left', $root_group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $root_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $root_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $root_group['email_attrs'] );
// Site title: gets root padding (non-container, receives delegation).
- $this->assertEquals( '10px', $site_title['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $site_title['email_attrs']['padding-right'] );
+ $this->assertEquals( '10px', $site_title['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $site_title['email_attrs']['root-padding-right'] );
// Content group: transparent (wraps post-content, delegates further).
- $this->assertArrayNotHasKey( 'padding-left', $content_group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $content_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $content_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $content_group['email_attrs'] );
// Post-content: no padding (post-content never gets padding).
- $this->assertArrayNotHasKey( 'padding-left', $post_content['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $post_content['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $post_content['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $post_content['email_attrs'] );
// Footer group: gets root padding (doesn't wrap post-content).
- $this->assertEquals( '10px', $footer_group['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $footer_group['email_attrs']['padding-right'] );
+ $this->assertEquals( '10px', $footer_group['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $footer_group['email_attrs']['root-padding-right'] );
}
/**
@@ -455,14 +463,18 @@ class Spacing_Preprocessor_Test extends \Email_Editor_Unit_Test {
$user_block = $post_content['innerBlocks'][0];
// All container groups in the chain should be transparent (no padding).
- $this->assertArrayNotHasKey( 'padding-left', $root_group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-left', $middle_group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-left', $inner_group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-left', $post_content['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $root_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $root_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $middle_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $middle_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $inner_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $inner_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $post_content['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $post_content['email_attrs'] );
// User block inside post-content should get root padding.
- $this->assertEquals( '10px', $user_block['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $user_block['email_attrs']['padding-right'] );
+ $this->assertEquals( '10px', $user_block['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $user_block['email_attrs']['root-padding-right'] );
}
/**
@@ -494,16 +506,16 @@ class Spacing_Preprocessor_Test extends \Email_Editor_Unit_Test {
$normal_child = $root_group['innerBlocks'][1];
// Root-level group is a container — it does NOT get root padding itself.
- $this->assertArrayNotHasKey( 'padding-left', $root_group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $root_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $root_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $root_group['email_attrs'] );
// Alignfull child should NOT get root padding (skipped for full-width).
- $this->assertArrayNotHasKey( 'padding-left', $alignfull_child['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $alignfull_child['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $alignfull_child['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $alignfull_child['email_attrs'] );
// Normal child should get root padding.
- $this->assertEquals( '10px', $normal_child['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $normal_child['email_attrs']['padding-right'] );
+ $this->assertEquals( '10px', $normal_child['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $normal_child['email_attrs']['root-padding-right'] );
}
/**
@@ -542,22 +554,22 @@ class Spacing_Preprocessor_Test extends \Email_Editor_Unit_Test {
$deeply_nested = $nested_group['innerBlocks'][0];
// Root-level group is a container — it does NOT get root padding itself.
- $this->assertArrayNotHasKey( 'padding-left', $root_group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $root_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $root_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $root_group['email_attrs'] );
// Direct children of root group SHOULD get root padding.
- $this->assertEquals( '10px', $nested_paragraph['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $nested_paragraph['email_attrs']['padding-right'] );
- $this->assertEquals( '10px', $nested_group['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $nested_group['email_attrs']['padding-right'] );
+ $this->assertEquals( '10px', $nested_paragraph['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $nested_paragraph['email_attrs']['root-padding-right'] );
+ $this->assertEquals( '10px', $nested_group['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $nested_group['email_attrs']['root-padding-right'] );
// Deeply nested blocks should NOT get root padding.
- $this->assertArrayNotHasKey( 'padding-left', $deeply_nested['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $deeply_nested['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $deeply_nested['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $deeply_nested['email_attrs'] );
}
/**
- * Test blocks with explicit horizontal padding skip root padding
+ * Test blocks with explicit zero padding skip root padding, but non-zero padding does not
*/
public function testItSkipsRootPaddingForBlocksWithExplicitPadding(): void {
$blocks = array(
@@ -642,24 +654,26 @@ class Spacing_Preprocessor_Test extends \Email_Editor_Unit_Test {
$plain_paragraph = $root_group['innerBlocks'][3];
// Banner group (0px padding): skips root padding, children don't get delegation.
- $this->assertArrayNotHasKey( 'padding-left', $banner_group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $banner_group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-left', $banner_child['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $banner_child['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $banner_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $banner_group['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $banner_child['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $banner_child['email_attrs'] );
// Columns (0px padding): skips root padding.
- $this->assertArrayNotHasKey( 'padding-left', $columns['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $columns['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-left', $columns['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $columns['email_attrs'] );
- // Padded group (40px): skips root padding, children don't get delegation.
- $this->assertArrayNotHasKey( 'padding-left', $padded_group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $padded_group['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-left', $padded_child['email_attrs'] );
- $this->assertArrayNotHasKey( 'padding-right', $padded_child['email_attrs'] );
+ // Padded group (40px): non-zero padding does NOT skip root padding.
+ // The group gets root padding (its own 40px is internal content spacing).
+ $this->assertEquals( '10px', $padded_group['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $padded_group['email_attrs']['root-padding-right'] );
+ // Children of a non-zero padded group still get delegation.
+ $this->assertArrayNotHasKey( 'root-padding-left', $padded_child['email_attrs'] );
+ $this->assertArrayNotHasKey( 'root-padding-right', $padded_child['email_attrs'] );
// Plain paragraph (no explicit padding): gets root padding.
- $this->assertEquals( '10px', $plain_paragraph['email_attrs']['padding-left'] );
- $this->assertEquals( '10px', $plain_paragraph['email_attrs']['padding-right'] );
+ $this->assertEquals( '10px', $plain_paragraph['email_attrs']['root-padding-left'] );
+ $this->assertEquals( '10px', $plain_paragraph['email_attrs']['root-padding-right'] );
}
/**