Commit ed643397813 for woocommerce

commit ed643397813ebe5013b1614d3b849b9ce5349cdc
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date:   Fri Jul 17 19:19:56 2026 +0300

    Fix product brand shortcode image dimensions (#66745)

    * fix: validate brand shortcode image dimensions

    Context: The [product_brand] shortcode passes width and height values through to the single-brand image template.

    Problem: Empty dimensions rendered an invalid inline style, and numeric shortcode dimensions rendered unitless CSS values.

    Solution: Normalize numeric dimensions to pixels, prepare an optional style string before rendering, and omit the template style attribute when no valid dimensions are present.

    Refs #51490

    * chore: add brand shortcode style changelog

    Context: WooCommerce package changes require release-note metadata for PR review and release assembly.

    Problem: The product brand shortcode style fix did not yet have a package changelog entry.

    Solution: Add a patch fix changelog entry for preventing empty or unitless inline dimensions on brand shortcode images.

    Refs #51490

    * refactor: simplify brand shortcode dimensions

    The initial fix derived a new style template argument and normalized values for every brand, which made direct template use fragile and repeated invariant work.\n\nKeep width and height as the template contract, normalize once per shortcode, and consolidate exact output coverage with isolated upload cleanup.\n\nRefs #51490

    * refactor: Make brand shortcode dimension normalizer static

    The normalize_product_brand_shortcode_dimension() helper does not use
    any instance state — it is a pure transform of its argument. Declaring
    it static communicates that intent and matches how the two call sites
    already treat it.

    Refs #66745

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---------

    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/fix-51490-brand-shortcode-style-units b/plugins/woocommerce/changelog/fix-51490-brand-shortcode-style-units
new file mode 100644
index 00000000000..c82bf535dd4
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-51490-brand-shortcode-style-units
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent product brand shortcode images from rendering empty or unitless inline dimensions.
diff --git a/plugins/woocommerce/includes/class-wc-brands.php b/plugins/woocommerce/includes/class-wc-brands.php
index da9956f3719..24d58c42988 100644
--- a/plugins/woocommerce/includes/class-wc-brands.php
+++ b/plugins/woocommerce/includes/class-wc-brands.php
@@ -562,6 +562,14 @@ class WC_Brands {
 			return '';
 		}

+		$args['width']  = self::normalize_product_brand_shortcode_dimension( $args['width'] );
+		$args['height'] = self::normalize_product_brand_shortcode_dimension( $args['height'] );
+
+		if ( '' !== $args['width'] || '' !== $args['height'] ) {
+			$args['width']  = '' !== $args['width'] ? $args['width'] : 'auto';
+			$args['height'] = '' !== $args['height'] ? $args['height'] : 'auto';
+		}
+
 		ob_start();

 		foreach ( $brands as $brand ) {
@@ -573,11 +581,6 @@ class WC_Brands {
 			$args['thumbnail'] = $thumbnail;
 			$args['term']      = get_term_by( 'id', $brand, 'product_brand' );

-			if ( $args['width'] || $args['height'] ) {
-				$args['width']  = ! empty( $args['width'] ) ? $args['width'] : 'auto';
-				$args['height'] = ! empty( $args['height'] ) ? $args['height'] : 'auto';
-			}
-
 			wc_get_template(
 				'shortcodes/single-brand.php',
 				$args,
@@ -589,6 +592,18 @@ class WC_Brands {
 		return ob_get_clean();
 	}

+	/**
+	 * Normalize a product brand shortcode image dimension.
+	 *
+	 * @param mixed $dimension Shortcode dimension value.
+	 * @return string Normalized dimension.
+	 */
+	private static function normalize_product_brand_shortcode_dimension( $dimension ) {
+		$dimension = is_scalar( $dimension ) ? trim( (string) $dimension ) : '';
+
+		return is_numeric( $dimension ) ? $dimension . 'px' : $dimension;
+	}
+
 	/**
 	 * Displays product brand list.
 	 *
diff --git a/plugins/woocommerce/templates/brands/shortcodes/single-brand.php b/plugins/woocommerce/templates/brands/shortcodes/single-brand.php
index 556ae2055e9..e1f0094afac 100644
--- a/plugins/woocommerce/templates/brands/shortcodes/single-brand.php
+++ b/plugins/woocommerce/templates/brands/shortcodes/single-brand.php
@@ -25,7 +25,7 @@
  * @see     https://woocommerce.com/document/template-structure/
  * @package WooCommerce\Templates
  * @usedby  [product_brand]
- * @version 9.4.0
+ * @version 11.1.0
  */

 declare( strict_types = 1);
@@ -34,5 +34,8 @@ declare( strict_types = 1);
 	<img src="<?php echo esc_url( $thumbnail ); ?>"
 	     alt="<?php echo esc_attr( $term->name ); ?>"
 	     class="<?php echo esc_attr( $class ); ?>"
-	     style="width: <?php echo esc_attr( $width ); ?>; height: <?php echo esc_attr( $height ); ?>;"/>
+		<?php if ( '' !== $width || '' !== $height ) : ?>
+	     style="width: <?php echo esc_attr( $width ); ?>; height: <?php echo esc_attr( $height ); ?>;"
+		<?php endif; ?>
+	/>
 </a>
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-brands-test.php b/plugins/woocommerce/tests/php/includes/class-wc-brands-test.php
index 74bbf6405f3..dcb937c4377 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-brands-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-brands-test.php
@@ -16,6 +16,7 @@ class WC_Brands_Test extends WC_Unit_Test_Case {
 	 * Tear down test data.
 	 */
 	public function tearDown(): void {
+		$this->remove_added_uploads();
 		parent::tearDown();

 		// Clear term cache to prevent interference between tests.
@@ -122,6 +123,54 @@ class WC_Brands_Test extends WC_Unit_Test_Case {
 		$this->assertStringContainsString( 'Empty Brand', $output );
 	}

+	/**
+	 * @testdox Product brand shortcode renders a valid image style.
+	 * @dataProvider product_brand_shortcode_dimension_provider
+	 *
+	 * @param array<string, string> $dimensions     Shortcode dimensions.
+	 * @param string|null           $expected_style Expected image style.
+	 */
+	public function test_product_brand_shortcode_renders_valid_image_style( array $dimensions, ?string $expected_style ): void {
+		$data = $this->setup_single_brand_shortcode_test_data();
+
+		$output = $data['brands_instance']->output_product_brand(
+			array_merge(
+				array( 'post_id' => $data['product']->get_id() ),
+				$dimensions
+			)
+		);
+		$image  = new WP_HTML_Tag_Processor( $output );
+
+		$this->assertTrue( $image->next_tag( array( 'tag_name' => 'img' ) ) );
+		$this->assertSame( $expected_style, $image->get_attribute( 'style' ) );
+	}
+
+	/**
+	 * Data provider for product brand shortcode dimensions.
+	 *
+	 * @return array<string, array{array<string, string>, string|null}>
+	 */
+	public function product_brand_shortcode_dimension_provider(): array {
+		return array(
+			'empty dimensions'            => array( array(), null ),
+			'numeric dimensions'          => array(
+				array(
+					'width'  => '123',
+					'height' => '567',
+				),
+				'width: 123px; height: 567px;',
+			),
+			'unit width with auto height' => array(
+				array( 'width' => '4rem' ),
+				'width: 4rem; height: auto;',
+			),
+			'unit height with auto width' => array(
+				array( 'height' => '50%' ),
+				'width: auto; height: 50%;',
+			),
+		);
+	}
+
 	/**
 	 * Test that brand counts are correctly calculated and cached.
 	 */
@@ -244,6 +293,20 @@ class WC_Brands_Test extends WC_Unit_Test_Case {
 		);
 	}

+	/**
+	 * Helper method to set up test data for single brand shortcode tests.
+	 *
+	 * @return array Contains brands instance, brand term IDs, and product ID.
+	 */
+	private function setup_single_brand_shortcode_test_data() {
+		$data         = $this->setup_brand_test_data();
+		$thumbnail_id = $this->factory->attachment->create_upload_object( DIR_TESTDATA . '/images/canola.jpg' );
+
+		update_term_meta( $data['brand_with_products']['term_id'], 'thumbnail_id', $thumbnail_id );
+
+		return $data;
+	}
+
 	/**
 	 * Helper method to get the first brand term.
 	 *