Commit 30977082677 for woocommerce

commit 3097708267734791140b053cbf9f2d810dc20724
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date:   Wed Jul 15 14:13:17 2026 +0300

    Fix fractional product gallery alignment (#66631)

    * fix: preserve fractional product gallery transforms

    Product galleries use FlexSlider slide widths that can resolve to fractional pixels when the viewport width is fractional.

    The horizontal transform path truncated the computed pixel target with parseInt(), shifting the active slide by up to almost one pixel and allowing adjacent gallery images to bleed into the visible viewport.

    Preserve the calculated pixel target in translate3d() and add a Product Image Gallery regression that verifies active-slide alignment after thumbnail navigation at a fractional width.

    Refs #29753

    * test(e2e): Make fractional gallery regression deterministic

    The initial regression could pass before FlexSlider finished initialization. A tap transiently marked the target thumbnail active while the gallery stayed on the first slide, so the old integer-truncated transform also passed.

    Wait for the gallery's observable ready state, click the target thumbnail, and compare Playwright locator bounds. This catches the old 0.5px offset while removing document-wide DOM reconstruction.

    Refs #29753

    * Loosen fractional-gallery E2E tolerance to avoid CI flakiness

    The regression test asserted toBeCloseTo(0, 3) — a 0.0005px tolerance —
    while the bug it guards against produced a roughly constant ~0.5px error.
    That precision is far tighter than needed and risks intermittent CI
    failures from ordinary sub-pixel float variance in getBoundingClientRect,
    unrelated to the production fix being correct.

    Loosen to toBeCloseTo(0, 1) (0.05px), still ~10x tighter than the pre-fix
    error margin, so the guard keeps its teeth without the flakiness risk.

diff --git a/plugins/woocommerce/changelog/fix-29753-product-gallery-fractional-transform b/plugins/woocommerce/changelog/fix-29753-product-gallery-fractional-transform
new file mode 100644
index 00000000000..34b74426c73
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-29753-product-gallery-fractional-transform
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix product image gallery alignment when slide widths are fractional.
diff --git a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js
index 64348f87259..543531b4968 100755
--- a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js
+++ b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js
@@ -816,7 +816,7 @@
       slider.container.css("transition-duration", dur);

       if (slider.transforms) {
-        target = (vertical) ? "translate3d(0," + target + ",0)" : "translate3d(" + (parseInt(target)+'px') + ",0,0)";
+        target = (vertical) ? "translate3d(0," + target + ",0)" : "translate3d(" + target + ",0,0)";
       } else {
         slider.container.css("transition-timing-function", easing);
       }
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/product-image-gallery/product-image-gallery.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/product-image-gallery/product-image-gallery.block_theme.spec.ts
index d26fe50470f..f27ee69f972 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/product-image-gallery/product-image-gallery.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/product-image-gallery/product-image-gallery.block_theme.spec.ts
@@ -98,6 +98,44 @@ test.describe( `${ blockData.name } frontend`, () => {
 			activeImageSrc as string
 		);
 	} );
+
+	test( 'aligns the active slide when the gallery width is fractional', async ( {
+		page,
+	} ) => {
+		await page.goto( blockData.productPage );
+
+		const gallery = page.locator( '.woocommerce-product-gallery' );
+		const viewport = page.locator( '.flex-viewport' );
+		const thumbnails = page.locator(
+			'.flex-control-nav.flex-control-thumbs img'
+		);
+
+		await expect( gallery ).toBeVisible();
+		await expect( viewport ).toBeVisible();
+		await expect( gallery ).toHaveCSS( 'opacity', '1' );
+
+		await gallery.evaluate( ( element ) => {
+			( element as HTMLElement ).style.width = '320.5px';
+			window.dispatchEvent( new Event( 'resize' ) );
+		} );
+
+		const targetThumbnail = thumbnails.nth( 1 );
+		await targetThumbnail.click();
+		await expect( targetThumbnail ).toHaveClass( /flex-active/ );
+
+		await expect
+			.poll( async () => {
+				const activeSlideBox = await gallery
+					.locator( '.flex-active-slide' )
+					.boundingBox();
+				const viewportBox = await viewport.boundingBox();
+
+				return activeSlideBox && viewportBox
+					? activeSlideBox.x - viewportBox.x
+					: null;
+			} )
+			.toBeCloseTo( 0, 1 );
+	} );
 } );

 test.describe( `${ blockData.name } editor`, () => {