Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Product Gallery: Add animation when large image changes #11113

Merged
merged 24 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
678a25f
Add slide animation
gigitux Oct 3, 2023
34a36c4
Remove placeholder and pagination (#11145)
albarin Oct 6, 2023
ea0b1f5
improve animation
gigitux Oct 6, 2023
0a88c32
Merge branch 'trunk' into fix/animation-dialog
gigitux Oct 6, 2023
c7fb032
improve naming
gigitux Oct 6, 2023
ed39279
Merge branch 'fix/animation-dialog' of github.com:woocommerce/woocomm…
gigitux Oct 6, 2023
aace907
fix regression
gigitux Oct 6, 2023
6539cd0
Merge branch 'trunk' of github.com:woocommerce/woocommerce-blocks int…
gigitux Oct 10, 2023
69daeae
fix css
gigitux Oct 11, 2023
8fe7233
improve code style
gigitux Oct 11, 2023
7ff3614
remove check on tag image
gigitux Oct 11, 2023
4d3008e
Merge branch 'trunk' into fix/animation-dialog
gigitux Oct 11, 2023
bb6f4ae
Merge branch 'trunk' of github.com:woocommerce/woocommerce-blocks int…
gigitux Oct 16, 2023
72f45b0
Merge branch 'trunk' into fix/animation-dialog
gigitux Oct 16, 2023
73c7bac
Merge branch 'trunk' into fix/animation-dialog
gigitux Oct 17, 2023
b1a8236
Merge branch 'trunk' into fix/animation-dialog
gigitux Oct 17, 2023
f1883db
Merge branch 'trunk' into fix/animation-dialog
gigitux Oct 17, 2023
e45c403
Merge branch 'trunk' into fix/animation-dialog
gigitux Oct 18, 2023
70ba9b5
Merge branch 'trunk' of github.com:woocommerce/woocommerce-blocks int…
gigitux Oct 19, 2023
b6db909
Merge branch 'fix/animation-dialog' of github.com:woocommerce/woocomm…
gigitux Oct 19, 2023
05a3eb6
align image
gigitux Oct 19, 2023
18d416e
fix crash when zoom is disabled
gigitux Oct 19, 2023
b6d0088
fix E2E tests
gigitux Oct 19, 2023
0592174
improve CSS
gigitux Oct 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions assets/js/blocks/product-gallery/frontend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface Context {
};
}

interface Selectors {
export interface ProductGallerySelectors {
woocommerce: {
isSelected: ( store: unknown ) => boolean;
pagerDotFillOpacity: ( store: SelectorsStore ) => number;
Expand All @@ -36,7 +36,7 @@ interface Actions {
interface Store {
state: State;
context: Context;
selectors: Selectors;
selectors: ProductGallerySelectors;
actions: Actions;
ref?: HTMLElement;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,56 @@
*/
import { store as interactivityStore } from '@woocommerce/interactivity';

/**
* Internal dependencies
*/
import { ProductGallerySelectors } from '../../frontend';

type Context = {
woocommerce: {
styles: {
// eslint-disable-next-line @typescript-eslint/naming-convention
'transform-origin': string;
transform: string;
transition: string;
};
styles:
| {
// eslint-disable-next-line @typescript-eslint/naming-convention
'transform-origin': string;
transform: string;
transition: string;
}
| undefined;
isDialogOpen: boolean;
};
};

type Store = {
context: Context;
selectors: typeof productButtonSelectors;
selectors: typeof productGalleryLargeImageSelectors &
ProductGallerySelectors;
ref: HTMLElement;
};

const productButtonSelectors = {
const productGalleryLargeImageSelectors = {
woocommerce: {
styles: ( { context }: Store ) => {
const { styles } = context.woocommerce;
productGalleryLargeImage: {
styles: ( { context }: Store ) => {
const { styles } = context.woocommerce;

return Object.entries( styles ).reduce( ( acc, [ key, value ] ) => {
const style = `${ key }:${ value };`;
return acc.length > 0 ? `${ acc } ${ style }` : style;
}, '' );
return Object.entries( styles ?? [] ).reduce(
( acc, [ key, value ] ) => {
const style = `${ key }:${ value };`;
return acc.length > 0 ? `${ acc } ${ style }` : style;
},
''
);
},
},
},
};

let isDialogStatusChanged = false;

interactivityStore(
// @ts-expect-error: Store function isn't typed.
{
selectors: productButtonSelectors,
selectors: productGalleryLargeImageSelectors,
actions: {
woocommerce: {
handleMouseMove: ( {
Expand Down Expand Up @@ -78,5 +93,58 @@ interactivityStore(
},
},
},
effects: {
woocommerce: {
scrollInto: ( store: Store ) => {
if ( ! store.selectors.woocommerce.isSelected( store ) ) {
return;
}

// Scroll to the selected image with a smooth animation.
if (
store.context.woocommerce.isDialogOpen ===
isDialogStatusChanged
) {
store.ref.scrollIntoView( {
behavior: 'smooth',
block: 'nearest',
inline: 'center',
} );
}

// Scroll to the selected image when the dialog is being opened without an animation.
if (
store.context.woocommerce.isDialogOpen &&
store.context.woocommerce.isDialogOpen !==
isDialogStatusChanged &&
store.ref.closest( 'dialog' )
) {
store.ref.scrollIntoView( {
behavior: 'instant',
block: 'nearest',
inline: 'center',
} );

isDialogStatusChanged =
store.context.woocommerce.isDialogOpen;
}

// Scroll to the selected image when the dialog is being closed without an animation.
if (
! store.context.woocommerce.isDialogOpen &&
store.context.woocommerce.isDialogOpen !==
isDialogStatusChanged
) {
store.ref.scrollIntoView( {
behavior: 'instant',
block: 'nearest',
inline: 'center',
} );
isDialogStatusChanged =
store.context.woocommerce.isDialogOpen;
}
},
},
},
}
);
35 changes: 25 additions & 10 deletions assets/js/blocks/product-gallery/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,43 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset));
overflow: hidden;

// Restrict the width of the Large Image when the Next/Previous buttons are outside the image.
#{$gallery-next-previous-outside-image} & .wc-block-woocommerce-product-gallery-large-image__container {
#{$gallery-next-previous-outside-image} & .wc-block-product-gallery-large-image__image-element {
overflow: hidden;
max-width: $outside-image-max-width;
margin: 0 auto;
max-width: $outside-image-max-width;
}

.wc-block-product-gallery-large-image__wrapper {
flex-shrink: 0;
max-width: 100%;
overflow: hidden;
width: 100%;
}

#{$gallery-next-previous-inside-image} & .wc-block-woocommerce-product-gallery-large-image__container {
overflow: unset;
max-width: unset;
margin: unset;
.wc-block-product-gallery-large-image__container {
display: flex;
overflow-x: hidden;
scroll-snap-type: x mandatory;
width: fit-content;
height: fit-content;
scroll-behavior: auto;
align-items: center;
}

#{$gallery-next-previous-inside-image} & .wc-block-product-gallery-large-image__image-element {
width: fit-content;
overflow: hidden;
margin: 0 auto;
}


img {
display: block;
position: relative;
margin: 0 auto;
z-index: 1;
transition: all 0.1s linear;
width: auto;

// Keep the order in this way. The hoverZoom class should override the full-screen-on-click class when both are applied.
&.wc-block-woocommerce-product-gallery-large-image__image--full-screen-on-click {
Expand All @@ -84,10 +103,6 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset));
&.wc-block-woocommerce-product-gallery-large-image__image--hoverZoom {
cursor: zoom-in;
}

&[hidden] {
display: none;
}
}

.wc-block-product-gallery-large-image__inner-blocks {
Expand Down
31 changes: 16 additions & 15 deletions src/BlockTypes/ProductGalleryLargeImage.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,10 @@ protected function render( $attributes, $content, $block ) {

return strtr(
'<div class="wc-block-product-gallery-large-image wp-block-woocommerce-product-gallery-large-image" {directives}>
{visible_main_image}
{main_images}
<div class="wc-block-woocommerce-product-gallery-large-image__content">
{content}
<div class="wc-block-product-gallery-large-image__container">
{main_images}
</div>
{content}
</div>',
array(
'{visible_main_image}' => $visible_main_image,
Expand All @@ -118,9 +117,9 @@ function( $carry, $key ) use ( $directives ) {
*/
private function get_main_images_html( $context, $product_id ) {
$attributes = array(
'data-wc-bind--hidden' => '!selectors.woocommerce.isSelected',
'hidden' => true,
'class' => 'wc-block-woocommerce-product-gallery-large-image__image',
'data-wc-bind--style' => 'selectors.woocommerce.productGalleryLargeImage.styles',
'data-wc-effect' => 'effects.woocommerce.scrollInto',
'class' => 'wc-block-woocommerce-product-gallery-large-image__image',

);

Expand All @@ -130,23 +129,25 @@ private function get_main_images_html( $context, $product_id ) {

if ( $context['hoverZoom'] ) {
$attributes['class'] .= ' wc-block-woocommerce-product-gallery-large-image__image--hoverZoom';
$attributes['data-wc-bind--style'] = 'selectors.woocommerce.styles';
$attributes['data-wc-bind--style'] = 'selectors.woocommerce.productGalleryLargeImage.styles';
}

$main_images = ProductGalleryUtils::get_product_gallery_images(
$product_id,
'full',
$attributes,
'wc-block-woocommerce-product-gallery-large-image__container'
'wc-block-product-gallery-large-image__image-element'
);

$visible_main_image = array_shift( $main_images );
$visible_main_image_processor = new \WP_HTML_Tag_Processor( $visible_main_image );
$visible_main_image_processor->next_tag();
$visible_main_image_processor->remove_attribute( 'hidden' );
$visible_main_image = $visible_main_image_processor->get_updated_html();
$main_image_with_wrapper = array_map(
function( $main_image_element ) {
return "<div class='wc-block-product-gallery-large-image__wrapper'>" . $main_image_element . '</div>';
},
$main_images
);

return array( $visible_main_image, $main_images );
$visible_main_image = array_shift( $main_images );
return array( $visible_main_image, $main_image_with_wrapper );

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,7 @@ test.describe( `${ blockData.name }`, () => {
} );

// img[style] is the selector because the style attribute is Interactivity API.
const imgElement = blockFrontend.locator(
'img[style]:not([hidden])'
);
const imgElement = blockFrontend.locator( 'img' ).first();
const style = await imgElement.evaluate( ( el ) => el.style );

await expect( style.transform ).toBe( 'scale(1)' );
Expand Down Expand Up @@ -137,14 +135,18 @@ test.describe( `${ blockData.name }`, () => {
page: 'frontend',
} );

// img[style] is the selector because the style attribute is added by Interactivity API. In this case, the style attribute should not be added.
const imgElement = blockFrontend.locator(
'img[style]:not([hidden])'
const imgElement = blockFrontend.locator( 'img' ).first();
const style = await imgElement.evaluate( ( el ) => el.style );

await expect( style.transform ).toBe( '' );

await imgElement.hover();

const styleOnHover = await imgElement.evaluate(
( el ) => el.style
);
await expect( imgElement ).toBeHidden();
await expect(
blockFrontend.locator( 'img:not([hidden])' )
).toBeVisible();

await expect( styleOnHover.transform ).toBe( '' );
} );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ const test = base.extend< { pageObject: ProductGalleryPage } >( {
export const getVisibleLargeImageId = async (
mainImageBlockLocator: Locator
) => {
const mainImage = mainImageBlockLocator.locator(
'img:not([hidden])'
) as Locator;
const mainImage = mainImageBlockLocator.locator( 'img' ).first() as Locator;

const mainImageContext = ( await mainImage.getAttribute(
'data-wc-context'
Expand Down
Loading