Commit c3cf96c9f88 for woocommerce
commit c3cf96c9f88acf7d465b621564b6c0935935ff24
Author: Allison Levine <1689238+allilevine@users.noreply.github.com>
Date: Mon Jul 20 16:32:05 2026 -0400
[Email Editor] Render email galleries at the author's chosen column count (up to 8) (#66801)
Render email galleries at the author's chosen column count (up to 8)
The gallery renderer clamped `columns` to `max(1, min(5, ...))`, so a gallery
with more than 5 columns silently rendered fewer columns than the author chose.
Because rows are chunked by the clamped column count, this also forced extra
partial rows whose trailing images stretched to a size the author never set
(e.g. a 6-column, 12-image gallery rendered 5 + 5 + 2, ballooning the final
pair to ~50% each).
Raise the cap to 8 to match the core gallery block's own maximum. The per-row
width redistribution is intentionally kept: the block's flex layout grows a
partial last row's images to fill the width, so the email now matches the block
for every valid column count.
Adds integration tests covering the 6- and 8-column cases, the clamp at 8, and
the block-matching partial-row stretch.
Claude-Session: https://claude.ai/code/session_01TVSRHZTL9L62w5nSn8Z6WR
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
diff --git a/packages/php/email-editor/changelog/fix-nl-739-gallery-columns-clamp b/packages/php/email-editor/changelog/fix-nl-739-gallery-columns-clamp
new file mode 100644
index 00000000000..e4cdb41ed2c
--- /dev/null
+++ b/packages/php/email-editor/changelog/fix-nl-739-gallery-columns-clamp
@@ -0,0 +1,4 @@
+Significance: patch
+Type: update
+
+Render email galleries with the same number of columns the block author chose (up to 8) instead of clamping to 5. Clamping wide galleries forced extra partial rows whose trailing images stretched to a size the author never set.
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-gallery.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-gallery.php
index 35f57498a57..3e3079c670e 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-gallery.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-gallery.php
@@ -445,13 +445,16 @@ class Gallery extends Abstract_Block_Renderer {
* Get the columns value from block attributes.
*
* @param array $block_attrs Block attributes.
- * @return int Number of columns (1-5).
+ * @return int Number of columns (1-8).
*/
private function get_columns_from_attributes( array $block_attrs ): int {
$columns = $block_attrs['columns'] ?? 3;
- // Ensure the columns are within reasonable bounds.
- $columns = max( 1, min( 5, (int) $columns ) );
+ // Clamp to the same 1-8 range the core gallery block allows, so the email doesn't
+ // silently render fewer columns than the author chose. A lower cap forced wide galleries
+ // into extra partial rows whose images then stretched to a size the author never set
+ // (e.g. a 6-column gallery clamped to 5 rendered 5 + 5 + 2, ballooning the trailing pair).
+ $columns = max( 1, min( 8, (int) $columns ) );
return $columns;
}
diff --git a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Gallery_Test.php b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Gallery_Test.php
index e4ca0de4deb..f47bc8dd41d 100644
--- a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Gallery_Test.php
+++ b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Gallery_Test.php
@@ -145,7 +145,7 @@ class Gallery_Test extends \Email_Editor_Integration_Test_Case {
$rendered_0_col = $this->gallery_renderer->render( '', $parsed_gallery_0_col, $this->rendering_context );
$this->assertStringContainsString( 'image1.jpg', $rendered_0_col );
- // Test 10 columns (should be limited to 5).
+ // Test 10 columns (should be limited to 8).
$parsed_gallery_10_col = $this->parsed_gallery;
$parsed_gallery_10_col['attrs']['columns'] = 10;
@@ -588,4 +588,99 @@ class Gallery_Test extends \Email_Editor_Integration_Test_Case {
$this->assertGreaterThan( $widths[0], $widths[3], 'A lone trailing image is sized to the full row width.' );
$this->assertGreaterThanOrEqual( 2 * $widths[0], $widths[3], 'The full-width cell is substantially wider than a one-third cell.' );
}
+
+ /**
+ * Build a parsed gallery block with a given number of images and a columns attribute.
+ *
+ * @param int $image_count Number of core/image inner blocks to generate.
+ * @param int $columns Value for the gallery's columns attribute.
+ * @return array Parsed gallery block.
+ */
+ private function build_gallery_with_images( int $image_count, int $columns ): array {
+ $inner_blocks = array();
+ for ( $i = 1; $i <= $image_count; $i++ ) {
+ $image_html = sprintf(
+ '<figure class="wp-block-image size-large"><img src="https://example.com/image%1$d.jpg" alt="Image %1$d" class="wp-image-%1$d"/></figure>',
+ $i
+ );
+ $inner_blocks[] = array(
+ 'blockName' => 'core/image',
+ 'attrs' => array(
+ 'id' => $i,
+ 'sizeSlug' => 'large',
+ 'linkDestination' => 'none',
+ ),
+ 'innerBlocks' => array(),
+ 'innerHTML' => $image_html,
+ 'innerContent' => array( 0 => $image_html ),
+ );
+ }
+
+ return array(
+ 'blockName' => 'core/gallery',
+ 'attrs' => array(
+ 'columns' => $columns,
+ 'linkTo' => 'none',
+ ),
+ 'innerHTML' => sprintf( '<figure class="wp-block-gallery has-nested-images columns-%d is-cropped"></figure>', $columns ),
+ 'innerBlocks' => $inner_blocks,
+ );
+ }
+
+ /**
+ * A 6-column gallery must render 6 images per row, not silently clamp to 5.
+ *
+ * Regression test: clamping 6 columns down to 5 chunked a 12-image gallery into 5 + 5 + 2 rows,
+ * and the trailing pair then ballooned to 50% each (100 / images-in-row). Honoring 6 columns
+ * renders two even rows of six (100 / 6 = 16.67% per cell) with no widened partial row.
+ */
+ public function testItRendersSixColumnsWithoutClampingToFive(): void {
+ $parsed_gallery = $this->build_gallery_with_images( 12, 6 );
+
+ $rendered = $this->gallery_renderer->render( '', $parsed_gallery, $this->rendering_context );
+
+ $this->assertStringContainsString( 'width: 16.67%', $rendered, 'Six-column cells are one-sixth wide.' );
+ $this->assertStringNotContainsString( 'width: 20.00%', $rendered, 'Columns must not be clamped to five.' );
+ $this->assertStringNotContainsString( 'width: 50.00%', $rendered, 'There is no partial row to balloon.' );
+ $this->assertStringContainsString( 'image12.jpg', $rendered, 'All twelve images are rendered.' );
+ }
+
+ /**
+ * The renderer supports up to 8 columns, matching the core gallery block's maximum.
+ */
+ public function testItSupportsUpToEightColumns(): void {
+ $parsed_gallery = $this->build_gallery_with_images( 8, 8 );
+
+ $rendered = $this->gallery_renderer->render( '', $parsed_gallery, $this->rendering_context );
+
+ $this->assertStringContainsString( 'width: 12.50%', $rendered, 'Eight-column cells are one-eighth wide.' );
+ $this->assertStringContainsString( 'image8.jpg', $rendered, 'All eight images are rendered.' );
+ }
+
+ /**
+ * Columns beyond 8 are clamped to 8 (the block's own maximum), not rendered at a smaller width.
+ */
+ public function testItClampsColumnsToEight(): void {
+ $parsed_gallery = $this->build_gallery_with_images( 16, 10 );
+
+ $rendered = $this->gallery_renderer->render( '', $parsed_gallery, $this->rendering_context );
+
+ $this->assertStringContainsString( 'width: 12.50%', $rendered, 'Cells are sized for eight columns.' );
+ $this->assertStringNotContainsString( 'width: 10.00%', $rendered, 'Columns must be clamped to eight, not ten.' );
+ $this->assertStringContainsString( 'image16.jpg', $rendered, 'All sixteen images are rendered.' );
+ }
+
+ /**
+ * A genuine partial last row stretches its images to fill the width, matching the core gallery
+ * block (whose flex items grow to fill the final row). Ten images in an 8-column gallery render
+ * as a full row of eight (12.50% each) plus a trailing pair that stretches to 50% each.
+ */
+ public function testItStretchesPartialLastRowLikeTheBlock(): void {
+ $parsed_gallery = $this->build_gallery_with_images( 10, 8 );
+
+ $rendered = $this->gallery_renderer->render( '', $parsed_gallery, $this->rendering_context );
+
+ $this->assertStringContainsString( 'width: 12.50%', $rendered, 'The full row uses the column width.' );
+ $this->assertStringContainsString( 'width: 50.00%', $rendered, 'The trailing pair stretches to fill the row, as in the block.' );
+ }
}