Commit 17419a9cf24 for woocommerce
commit 17419a9cf241a7a5de0abe9cb9da2a3d520c73e7
Author: Karol Manijak <20098064+kmanijak@users.noreply.github.com>
Date: Mon Jul 13 08:49:56 2026 +0200
Fix Product Gallery custom aspect ratios (#66441)
* Fix Product Gallery image aspect ratios
* Add changelog for Product Gallery aspect ratios
* Fix Product Gallery aspect ratio CI checks
* Strengthen Product Gallery aspect ratio tests
* Clean up Product Gallery aspect ratio editor code
* Reduce Product Gallery aspect ratio duplication
* Tidy Product Gallery aspect ratio style handling
* Scope Product Gallery ratio to large image
* Remove Product Image utility extraction
* Remove redundant Product Gallery aspect ratio styles
diff --git a/plugins/woocommerce/changelog/fix-product-gallery-custom-aspect-ratios b/plugins/woocommerce/changelog/fix-product-gallery-custom-aspect-ratios
new file mode 100644
index 00000000000..8598ec01cee
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-product-gallery-custom-aspect-ratios
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Make Product Gallery layout honor custom Product Image aspect ratios.
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/edit.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/edit.tsx
index c8ab29c5d12..e997076e38e 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/edit.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/edit.tsx
@@ -4,11 +4,18 @@
import {
InnerBlocks,
InspectorControls,
+ store as blockEditorStore,
useBlockProps,
} from '@wordpress/block-editor';
-import { BlockEditProps, InnerBlockTemplate } from '@wordpress/blocks';
+import {
+ BlockEditProps,
+ InnerBlockTemplate,
+ type BlockInstance,
+} from '@wordpress/blocks';
+import { useSelect } from '@wordpress/data';
import { withProductDataContext } from '@woocommerce/shared-hocs';
import { useProductDataContext } from '@woocommerce/shared-context';
+import { getSetting } from '@woocommerce/settings';
import clsx from 'clsx';
/**
@@ -16,6 +23,8 @@ import clsx from 'clsx';
*/
import { ProductGalleryBlockSettings } from './block-settings/index';
import type { ProductGalleryBlockAttributes } from './types';
+import { resolveAspectRatio } from '../../atomic/blocks/product-elements/image/utils';
+import type { BlockAttributes as ProductImageBlockAttributes } from '../../atomic/blocks/product-elements/image/types';
const TEMPLATE: InnerBlockTemplate[] = [
[ 'woocommerce/product-gallery-thumbnails' ],
@@ -41,8 +50,88 @@ const TEMPLATE: InnerBlockTemplate[] = [
],
];
+const PRODUCT_IMAGE_BLOCK_NAME = 'woocommerce/product-image';
+
+type ParsedAspectRatio = {
+ width: string;
+ height: string;
+};
+
+const DEFAULT_ASPECT_RATIO: ParsedAspectRatio = { width: '1', height: '1' };
+
+type ProductImageBlock = BlockInstance<
+ Partial< ProductImageBlockAttributes >
+>;
+
+const isProductImageBlock = (
+ block: BlockInstance
+): block is ProductImageBlock => block.name === PRODUCT_IMAGE_BLOCK_NAME;
+
+const findProductImageBlock = (
+ blocks: BlockInstance[]
+): ProductImageBlock | undefined => {
+ for ( const block of blocks ) {
+ if ( isProductImageBlock( block ) ) {
+ return block;
+ }
+
+ const innerProductImageBlock = findProductImageBlock(
+ block.innerBlocks
+ );
+ if ( innerProductImageBlock ) {
+ return innerProductImageBlock;
+ }
+ }
+
+ return undefined;
+};
+
+const resolveProductImageAspectRatio = (
+ attributes: Partial< ProductImageBlockAttributes > | undefined,
+ storeAspectRatio: string | null
+): string | undefined => {
+ const { style, aspectRatio, imageSizing } = attributes ?? {};
+
+ return resolveAspectRatio(
+ style,
+ aspectRatio,
+ storeAspectRatio,
+ imageSizing
+ );
+};
+
+const parseAspectRatio = (
+ aspectRatio: string | undefined
+): ParsedAspectRatio => {
+ if ( ! aspectRatio ) {
+ return DEFAULT_ASPECT_RATIO;
+ }
+
+ const ratioParts = aspectRatio
+ .split( '/' )
+ .map( ( part ) => part.trim() )
+ .filter( Boolean );
+
+ if ( ratioParts.length === 0 || ratioParts.length > 2 ) {
+ return DEFAULT_ASPECT_RATIO;
+ }
+
+ const width = Number( ratioParts[ 0 ] );
+ const height = Number( ratioParts[ 1 ] ?? ratioParts[ 0 ] );
+
+ if ( ! Number.isFinite( width ) || ! Number.isFinite( height ) ) {
+ return DEFAULT_ASPECT_RATIO;
+ }
+
+ return {
+ width: String( width ),
+ height: String( height ),
+ };
+};
+
export const Edit = withProductDataContext(
( {
+ clientId,
attributes,
setAttributes,
context,
@@ -56,11 +145,37 @@ export const Edit = withProductDataContext(
const hasProductContext = Boolean( context?.postId && product?.id );
const hasOneOrNoImages =
hasProductContext && ! isLoading && productImages.length <= 1;
+ const storeAspectRatio = getSetting< string | null >(
+ 'thumbnailAspectRatio',
+ null
+ );
+ const productImageAspectRatio = useSelect(
+ ( select ) => {
+ const productImageBlock = findProductImageBlock(
+ select( blockEditorStore ).getBlocks( clientId )
+ );
+
+ return resolveProductImageAspectRatio(
+ productImageBlock?.attributes,
+ storeAspectRatio
+ );
+ },
+ [ clientId, storeAspectRatio ]
+ );
+ const productGalleryAspectRatio = parseAspectRatio(
+ productImageAspectRatio
+ );
const blockProps = useBlockProps( {
className: clsx( 'wc-block-product-gallery', {
'wc-block-product-gallery--has-one-or-no-images':
hasOneOrNoImages,
} ),
+ style: {
+ '--wc-block-product-gallery-large-image-ratio-width':
+ productGalleryAspectRatio.width,
+ '--wc-block-product-gallery-large-image-ratio-height':
+ productGalleryAspectRatio.height,
+ },
} );
return (
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/editor.scss b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/editor.scss
index c2b0585f989..7f28d2a89ca 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/editor.scss
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/editor.scss
@@ -8,35 +8,8 @@
}
}
-
-// In Editor there's different HTML structure than in the frontend
-// due to usage of inner blocks template (we cannot have wrapper elements)
-// hence we need to use manually assign aspect ratios that are otherwise handled
-// in the frontend.
+// In the editor there is no frontend wrapper, so the gallery ratio is inherited
+// from the parent Product Gallery block CSS variables.
:where(.wc-block-product-gallery-large-image) {
- &:has(.wc-block-components-product-image--aspect-ratio-auto),
- &:has(.wc-block-components-product-image--aspect-ratio-1) {
- aspect-ratio: 1/1;
- }
-
- &:has(.wc-block-components-product-image--aspect-ratio-4-3) {
- aspect-ratio: 4/3;
- }
-
- &:has(.wc-block-components-product-image--aspect-ratio-3-4) {
- aspect-ratio: 3/4;
- }
-
- &:has(.wc-block-components-product-image--aspect-ratio-3-2) {
- aspect-ratio: 3/2;
- }
- &:has(.wc-block-components-product-image--aspect-ratio-2-3) {
- aspect-ratio: 2/3;
- }
- &:has(.wc-block-components-product-image--aspect-ratio-16-9) {
- aspect-ratio: 16/9;
- }
- &:has(.wc-block-components-product-image--aspect-ratio-9-16) {
- aspect-ratio: 9/16;
- }
+ aspect-ratio: var(--wc-block-product-gallery-large-image-ratio-width, 1) / var(--wc-block-product-gallery-large-image-ratio-height, 1);
}
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/style.scss b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/style.scss
index b54d339bde1..a6b3c7335c4 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/style.scss
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/style.scss
@@ -3,18 +3,17 @@ $thumbnails-gradient-size: 20%;
$thumbnails-gap: 2%;
$dialog-padding: 20px;
+:where(.wc-block-product-gallery) {
+ --wc-block-product-gallery-large-image-ratio-width: 1;
+ --wc-block-product-gallery-large-image-ratio-height: 1;
+}
+
:where(.wc-block-product-gallery-large-image) {
width: 100%;
position: relative;
flex-grow: 1;
overflow: hidden;
- //When Product Image aspect ratio is auto, we are setting constraints
- //on the viewer container and display full images in square container.
- &:has(.wc-block-components-product-image--aspect-ratio-auto) {
- aspect-ratio: 1/1;
- }
-
:where(.wc-block-product-gallery-large-image__container) {
display: flex;
overflow: hidden;
@@ -33,10 +32,7 @@ $dialog-padding: 20px;
display: flex;
align-items: center;
justify-content: center;
-
- &:has(.wc-block-components-product-image--aspect-ratio-auto) {
- aspect-ratio: 1/1;
- }
+ aspect-ratio: var(--wc-block-product-gallery-large-image-ratio-width, 1) / var(--wc-block-product-gallery-large-image-ratio-height, 1);
}
.wc-block-product-gallery-large-image__wrapper[hidden] {
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/test/block.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/test/block.ts
index dad090075de..9d59f3084ac 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/test/block.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/test/block.ts
@@ -203,8 +203,14 @@ describe( 'Product Gallery Block', () => {
expect( productImage ).toHaveAttribute( 'alt', 'Test 1' );
} );
- it( 'should ensure thumbnail height matches viewer height with custom aspect ratio', async () => {
- await setup( { aspectRatio: '16/9' } );
+ it( 'should expose custom product image ratios as large image CSS variables', async () => {
+ await setup( {
+ style: {
+ dimensions: {
+ aspectRatio: '3/5',
+ },
+ },
+ } );
// Get the viewer
const productImage = screen.getByTestId( 'product-image' );
@@ -215,22 +221,21 @@ describe( 'Product Gallery Block', () => {
'.wc-block-components-product-image'
);
expect( imageContainer ).toHaveClass(
- 'wc-block-components-product-image--aspect-ratio-16-9'
+ 'wc-block-components-product-image--aspect-ratio-3-5'
);
+ const productGalleryBlock = screen.getByRole( 'document', {
+ name: /Block: Product Gallery/i,
+ } );
+ expect( productGalleryBlock ).toHaveStyle( {
+ '--wc-block-product-gallery-large-image-ratio-width': '3',
+ '--wc-block-product-gallery-large-image-ratio-height': '5',
+ } );
- // Get the thumbnails block
const thumbnailsBlock = screen.getByRole( 'document', {
name: /Block: Thumbnails/i,
} );
expect( thumbnailsBlock ).toBeInTheDocument();
- // Get the heights
- const viewerHeight = productImage.getBoundingClientRect().height;
- const thumbnailHeight = thumbnailsBlock.getBoundingClientRect().height;
-
- // Check that the heights match
- expect( thumbnailHeight ).toBe( viewerHeight );
-
// wp-6.8: upstream @wordpress/* deprecation warnings that we cannot
// opt out of without changing the visual output.
expect( console ).toHaveWarned();
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 8ab124ee289..4ea2973c9c5 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -52437,24 +52437,12 @@ parameters:
count: 1
path: src/Blocks/BlockTypes/ProductFilters.php
- -
- message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
- identifier: class.notFound
- count: 1
- path: src/Blocks/BlockTypes/ProductGallery.php
-
-
message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductGallery\:\:render_dialog\(\) should return string but returns string\|false\.$#'
identifier: return.type
count: 1
path: src/Blocks/BlockTypes/ProductGallery.php
- -
- message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductGallery\:\:render\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
- identifier: class.notFound
- count: 1
- path: src/Blocks/BlockTypes/ProductGallery.php
-
-
message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\ProductGalleryLargeImage\:\:enqueue_assets\(\) has no return type specified\.$#'
identifier: missingType.return
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductGallery.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductGallery.php
index c8711f6233e..dc0133ffb9d 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductGallery.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductGallery.php
@@ -112,12 +112,138 @@ class ProductGallery extends AbstractBlock {
return $gallery_html;
}
+ /**
+ * Find the Product Image block attributes used by the gallery.
+ *
+ * @param iterable<\WP_Block> $inner_blocks Inner blocks to search.
+ * @return array|null
+ */
+ private function get_product_image_attributes( $inner_blocks ) {
+ foreach ( $inner_blocks as $inner_block ) {
+ if ( 'woocommerce/product-image' === $inner_block->name ) {
+ return $inner_block->parsed_block['attrs'] ?? array();
+ }
+
+ $found_attributes = $this->get_product_image_attributes( $inner_block->inner_blocks );
+ if ( null !== $found_attributes ) {
+ return $found_attributes;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the store thumbnail aspect ratio from WooCommerce Customizer settings.
+ *
+ * @return string|null CSS aspect ratio value, or null when uncropped.
+ */
+ private function get_store_thumbnail_aspect_ratio() {
+ $cropping = get_option( 'woocommerce_thumbnail_cropping', '1:1' );
+
+ if ( 'uncropped' === $cropping ) {
+ return null;
+ }
+
+ if ( 'custom' === $cropping ) {
+ $width = max( 1, (float) get_option( 'woocommerce_thumbnail_cropping_custom_width', '4' ) );
+ $height = max( 1, (float) get_option( 'woocommerce_thumbnail_cropping_custom_height', '3' ) );
+
+ return $width . '/' . $height;
+ }
+
+ return str_replace( ':', '/', $cropping );
+ }
+
+ /**
+ * Resolve the gallery aspect ratio from the nested Product Image block.
+ *
+ * @param \WP_Block $block The Product Gallery block.
+ * @return string|null CSS aspect ratio value, or null when no ratio should be applied.
+ */
+ private function resolve_product_image_aspect_ratio( $block ) {
+ $attributes = $this->get_product_image_attributes( $block->inner_blocks );
+
+ if ( empty( $attributes ) ) {
+ return null;
+ }
+
+ if ( ! empty( $attributes['style']['dimensions']['aspectRatio'] ) ) {
+ return $attributes['style']['dimensions']['aspectRatio'];
+ }
+
+ if ( ! empty( $attributes['aspectRatio'] ) ) {
+ return $attributes['aspectRatio'];
+ }
+
+ $image_sizing = $attributes['imageSizing'] ?? 'single';
+
+ if ( 'thumbnail' === $image_sizing || 'cropped' === $image_sizing ) {
+ return $this->get_store_thumbnail_aspect_ratio();
+ }
+
+ return null;
+ }
+
+ /**
+ * Parse an aspect ratio into CSS variable values.
+ *
+ * @param string|null $aspect_ratio CSS aspect ratio value.
+ * @return array
+ */
+ private function parse_aspect_ratio( $aspect_ratio ) {
+ $default_aspect_ratio = array(
+ 'width' => '1',
+ 'height' => '1',
+ );
+
+ if ( empty( $aspect_ratio ) || ! is_string( $aspect_ratio ) ) {
+ return $default_aspect_ratio;
+ }
+
+ $parts = array_map( 'trim', explode( '/', $aspect_ratio ) );
+ if ( count( $parts ) > 2 ) {
+ return $default_aspect_ratio;
+ }
+
+ $width = (float) $parts[0];
+ $height = isset( $parts[1] ) ? (float) $parts[1] : $width;
+
+ if ( $width <= 0 || $height <= 0 ) {
+ return $default_aspect_ratio;
+ }
+
+ return array(
+ 'width' => (string) $width,
+ 'height' => (string) $height,
+ );
+ }
+
+ /**
+ * Get the gallery aspect ratio CSS variables.
+ *
+ * @param \WP_Block $block The Product Gallery block.
+ * @return string
+ */
+ private function get_aspect_ratio_style( $block ) {
+ $aspect_ratio = $this->parse_aspect_ratio(
+ $this->resolve_product_image_aspect_ratio( $block )
+ );
+
+ return sprintf(
+ '--wc-block-product-gallery-large-image-ratio-width:%1$s;' .
+ '--wc-block-product-gallery-large-image-ratio-height:%2$s;',
+ $aspect_ratio['width'],
+ $aspect_ratio['height']
+ );
+ }
+
/**
* Include and render the block.
*
- * @param array $attributes Block attributes. Default empty array.
- * @param string $content Block content. Default empty string.
- * @param WP_Block $block Block instance.
+ * @param array $attributes Block attributes. Default empty array.
+ * @param string $content Block content. Default empty string.
+ * @param \WP_Block $block Block instance.
* @return string Rendered block type output.
*/
protected function render( $attributes, $content, $block ) {
@@ -199,6 +325,15 @@ class ProductGallery extends AbstractBlock {
$p->add_class( $classname );
$p->add_class( $classname_single_image );
+
+ $style = $p->get_attribute( 'style' );
+ $style = is_string( $style ) ? trim( $style ) : '';
+
+ if ( '' !== $style && ';' !== substr( $style, -1 ) ) {
+ $style .= ';';
+ }
+
+ $p->set_attribute( 'style', $style . $this->get_aspect_ratio_style( $block ) );
$html = $p->get_updated_html();
}
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductGallery.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductGallery.php
index e88f16fa246..c7c1a711fb8 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductGallery.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductGallery.php
@@ -11,6 +11,45 @@ use WC_Helper_Product;
*/
class ProductGallery extends \WP_UnitTestCase {
+ /**
+ * Previous thumbnail cropping option values to restore after tests.
+ *
+ * @var array<string, mixed>
+ */
+ private $prev_thumbnail_cropping_options = array();
+
+ /**
+ * Set up.
+ */
+ public function setUp(): void {
+ parent::setUp();
+
+ $option_names = array(
+ 'woocommerce_thumbnail_cropping',
+ 'woocommerce_thumbnail_cropping_custom_width',
+ 'woocommerce_thumbnail_cropping_custom_height',
+ );
+
+ foreach ( $option_names as $option_name ) {
+ $this->prev_thumbnail_cropping_options[ $option_name ] = get_option( $option_name, null );
+ }
+ }
+
+ /**
+ * Tear down.
+ */
+ public function tearDown(): void {
+ foreach ( $this->prev_thumbnail_cropping_options as $option_name => $value ) {
+ if ( null === $value ) {
+ delete_option( $option_name );
+ } else {
+ update_option( $option_name, $value );
+ }
+ }
+
+ parent::tearDown();
+ }
+
/**
* Helper method to create a product with multiple images.
*
@@ -56,9 +95,25 @@ class ProductGallery extends \WP_UnitTestCase {
*
* @param int $product_id The product ID.
* @param string $gallery_attributes Optional gallery attributes.
+ * @param array $product_image_attributes Optional Product Image block attributes.
* @return string The rendered markup.
*/
- private function render_product_gallery( $product_id, $gallery_attributes = '' ) {
+ private function render_product_gallery(
+ $product_id,
+ $gallery_attributes = '',
+ $product_image_attributes = array()
+ ) {
+ $product_image_attributes = wp_json_encode(
+ array_merge(
+ array(
+ 'showProductLink' => false,
+ 'showSaleBadge' => false,
+ 'isDescendentOfSingleProductBlock' => true,
+ ),
+ $product_image_attributes
+ )
+ );
+
return do_blocks(
sprintf(
'<!-- wp:woocommerce/single-product {"productId":%d} -->
@@ -69,7 +124,7 @@ class ProductGallery extends \WP_UnitTestCase {
<!-- wp:woocommerce/product-gallery-large-image -->
<div class="wp-block-woocommerce-product-gallery-large-image wc-block-product-gallery-large-image__inner-blocks">
- <!-- wp:woocommerce/product-image {"showProductLink":false,"showSaleBadge":false,"isDescendentOfSingleProductBlock":true} /-->
+ <!-- wp:woocommerce/product-image %s /-->
<!-- wp:woocommerce/product-sale-badge {"align":"right"} /-->
@@ -83,7 +138,8 @@ class ProductGallery extends \WP_UnitTestCase {
</div>
<!-- /wp:woocommerce/single-product -->',
$product_id,
- $gallery_attributes
+ $gallery_attributes,
+ $product_image_attributes
)
);
}
@@ -105,6 +161,26 @@ class ProductGallery extends \WP_UnitTestCase {
}
}
+ /**
+ * Assert that rendered gallery markup contains the expected large image aspect ratio CSS variables.
+ *
+ * @param string $markup Rendered gallery markup.
+ * @param string $width Expected ratio width.
+ * @param string $height Expected ratio height.
+ */
+ private function assert_product_gallery_large_image_ratio_variables( string $markup, string $width, string $height ): void {
+ $this->assertStringContainsString(
+ "--wc-block-product-gallery-large-image-ratio-width:{$width};",
+ $markup,
+ "Expected Product Gallery markup to expose a {$width}:{$height} large image aspect ratio width variable."
+ );
+ $this->assertStringContainsString(
+ "--wc-block-product-gallery-large-image-ratio-height:{$height};",
+ $markup,
+ "Expected Product Gallery markup to expose a {$width}:{$height} large image aspect ratio height variable."
+ );
+ }
+
/**
* Test that the ProductGallery block renders correctly with multiple images.
*/
@@ -134,6 +210,86 @@ class ProductGallery extends \WP_UnitTestCase {
$this->cleanup_product_data( $data );
}
+ /**
+ * @testdox Should expose Product Image style aspect ratios as large image CSS variables.
+ */
+ public function test_product_gallery_exposes_custom_product_image_aspect_ratio_css_variables(): void {
+ $data = $this->create_product_with_gallery( 3 );
+
+ $markup = $this->render_product_gallery(
+ $data['product']->get_id(),
+ '',
+ array(
+ 'style' => array(
+ 'dimensions' => array(
+ 'aspectRatio' => '3/5',
+ ),
+ ),
+ )
+ );
+
+ $this->assert_product_gallery_large_image_ratio_variables( $markup, '3', '5' );
+
+ $this->cleanup_product_data( $data );
+ }
+
+ /**
+ * @testdox Should expose Product Image aspectRatio attributes as large image CSS variables.
+ */
+ public function test_product_gallery_exposes_legacy_product_image_aspect_ratio_css_variables(): void {
+ $data = $this->create_product_with_gallery( 3 );
+
+ $markup = $this->render_product_gallery(
+ $data['product']->get_id(),
+ '',
+ array(
+ 'aspectRatio' => '7/4',
+ )
+ );
+
+ $this->assert_product_gallery_large_image_ratio_variables( $markup, '7', '4' );
+
+ $this->cleanup_product_data( $data );
+ }
+
+ /**
+ * @testdox Should use custom store thumbnail ratios for Product Image thumbnail sizing.
+ * @dataProvider product_image_thumbnail_sizing_data
+ *
+ * @param string $image_sizing Product Image sizing mode.
+ */
+ public function test_product_gallery_uses_store_thumbnail_ratio_for_thumbnail_product_image_sizing( string $image_sizing ): void {
+ $data = $this->create_product_with_gallery( 3 );
+
+ update_option( 'woocommerce_thumbnail_cropping', 'custom' );
+ update_option( 'woocommerce_thumbnail_cropping_custom_width', '5' );
+ update_option( 'woocommerce_thumbnail_cropping_custom_height', '8' );
+
+ $markup = $this->render_product_gallery(
+ $data['product']->get_id(),
+ '',
+ array(
+ 'imageSizing' => $image_sizing,
+ )
+ );
+
+ $this->assert_product_gallery_large_image_ratio_variables( $markup, '5', '8' );
+
+ $this->cleanup_product_data( $data );
+ }
+
+ /**
+ * Data provider for Product Image thumbnail sizing modes.
+ *
+ * @return array[]
+ */
+ public function product_image_thumbnail_sizing_data(): array {
+ return array(
+ 'thumbnail sizing' => array( 'thumbnail' ),
+ 'cropped sizing' => array( 'cropped' ),
+ );
+ }
+
/**
* Test that the ProductGallery block renders correctly with hover zoom enabled.
*/