Product Gallery Thumbnails: Fix overflow issues and improve responsiveness (https://github.com/woocommerce/woocommerce-blocks/pull/11665)
* Product Gallery Thumbnails: Refactor sizing in the editor and the front end * Product Gallery Thumbnails: Change default vertical alignment to top and better control the width of the thumbnails * Product Gallery Thumbnails: Restrict the bottom position thumbnails width based on the total number of thumbnails set * Product Gallery: Remove hardcoded width for Thumbnails and the Large Image and update the width inside of the Dialog * Product Gallery Thumbnails: Introduce thumbnails scaling based on the number of thumbnails * Product Gallery Thumbnails: Fix editor thumbnails scaling * Product Gallery Thumbnails: Remove unused column gap variable * Product Gallery Thumbnails: Fix styling for vertical images * Product Gallery: Remove the unused editor.scss file * Product Gallery: Fix the placement of the Thumbnails block in the block template * Product Gallery Dialog: Reset changes to the dialog * update @wordpress/e2e-test-utils-playwright package * don't update node version * remove waitForSiteEditorFinishLoading function * use visitSiteEditor util * Product Gallery Thumbnails: Add code comments * Product Gallery Thumbnails E2E: Fix the test checking the default position of the thumbnails * Product Gallery E2E: Fix the test checking if the cropping setting works correctly * Product Gallery Thumbnails: Hide the Thumbnails block if there aren't at least 2 thumbnails to display --------- Co-authored-by: Paulo Arromba <17236129+wavvves@users.noreply.github.com> Co-authored-by: Luigi Teschio <gigitux@gmail.com>
This commit is contained in:
parent
c7182d9202
commit
38b0001735
|
@ -25,7 +25,13 @@ import type { ProductGalleryAttributes } from './types';
|
|||
const TEMPLATE: InnerBlockTemplate[] = [
|
||||
[
|
||||
'core/group',
|
||||
{ layout: { type: 'flex', flexWrap: 'nowrap' } },
|
||||
{
|
||||
layout: {
|
||||
type: 'flex',
|
||||
flexWrap: 'nowrap',
|
||||
verticalAlignment: 'top',
|
||||
},
|
||||
},
|
||||
[
|
||||
[
|
||||
'woocommerce/product-gallery-thumbnails',
|
||||
|
@ -38,6 +44,10 @@ const TEMPLATE: InnerBlockTemplate[] = [
|
|||
type: 'flex',
|
||||
orientation: 'vertical',
|
||||
justifyContent: 'center',
|
||||
verticalAlignment: 'top',
|
||||
},
|
||||
style: {
|
||||
layout: { selfStretch: 'fixed', flexSize: '100%' },
|
||||
},
|
||||
...getInnerBlocksLockAttributes( 'lock' ),
|
||||
},
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
.wc-block-product-gallery {
|
||||
.block-editor-inner-blocks {
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
.wc-block-editor-product-gallery-large-image {
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
|
|
|
@ -51,7 +51,7 @@ export const ProductGalleryThumbnailsBlockSettings = ( {
|
|||
context,
|
||||
}: ProductGalleryThumbnailsSettingsProps ) => {
|
||||
const maxNumberOfThumbnails = 8;
|
||||
const minNumberOfThumbnails = 2;
|
||||
const minNumberOfThumbnails = 3;
|
||||
const { productGalleryClientId } = context;
|
||||
// @ts-expect-error @wordpress/block-editor/store types not provided
|
||||
const { updateBlockAttributes } = useDispatch( blockEditorStore );
|
||||
|
@ -110,7 +110,7 @@ export const ProductGalleryThumbnailsBlockSettings = ( {
|
|||
} )
|
||||
}
|
||||
help={ __(
|
||||
'Choose how many thumbnails (2-8) will display. If more images exist, a “View all” button will display.',
|
||||
'Choose how many thumbnails (3-8) will display. If more images exist, a “View all” button will display.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
max={ maxNumberOfThumbnails }
|
||||
|
|
|
@ -25,26 +25,29 @@ interface EditProps
|
|||
|
||||
export const Edit = ( { attributes, setAttributes, context }: EditProps ) => {
|
||||
const blockProps = useBlockProps( {
|
||||
className: 'wc-block-product-gallery-thumbnails',
|
||||
className: classNames(
|
||||
'wc-block-product-gallery-thumbnails',
|
||||
`wc-block-product-gallery-thumbnails--number-of-thumbnails-${ context.thumbnailsNumberOfThumbnails }`,
|
||||
`wc-block-product-gallery-thumbnails--position-${ context.thumbnailsPosition }`
|
||||
),
|
||||
} );
|
||||
|
||||
const Placeholder = () => {
|
||||
return context.thumbnailsPosition !== ThumbnailsPosition.OFF ? (
|
||||
<div
|
||||
className={ classNames(
|
||||
'wc-block-editor-product-gallery-thumbnails',
|
||||
`wc-block-editor-product-gallery-thumbnails--${ context.thumbnailsPosition }`
|
||||
) }
|
||||
>
|
||||
<div className="wc-block-editor-product-gallery-thumbnails">
|
||||
{ [
|
||||
...Array( context.thumbnailsNumberOfThumbnails ).keys(),
|
||||
].map( ( index ) => {
|
||||
return (
|
||||
<img
|
||||
<div
|
||||
className="wc-block-product-gallery-thumbnails__thumbnail"
|
||||
key={ index }
|
||||
src={ `${ WC_BLOCKS_IMAGE_URL }block-placeholders/product-image-gallery.svg` }
|
||||
alt="Placeholder"
|
||||
/>
|
||||
>
|
||||
<img
|
||||
src={ `${ WC_BLOCKS_IMAGE_URL }block-placeholders/product-image-gallery.svg` }
|
||||
alt="Placeholder"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} ) }
|
||||
</div>
|
||||
|
|
|
@ -1,17 +1,30 @@
|
|||
.wc-block-product-gallery-thumbnails {
|
||||
width: fit-content;
|
||||
.wc-block-editor-product-gallery-thumbnails {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
$thumbnails: ".wc-block-editor-product-gallery-thumbnails";
|
||||
$thumbnails-gap: 15px;
|
||||
|
||||
&.wc-block-editor-product-gallery-thumbnails--bottom {
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
#{$thumbnails} {
|
||||
display: flex;
|
||||
|
||||
img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 5px;
|
||||
}
|
||||
.wc-block-product-gallery-thumbnails--position-bottom & {
|
||||
flex-direction: row;
|
||||
gap: 0 15px;
|
||||
}
|
||||
|
||||
.wc-block-product-gallery-thumbnails:not(.wc-block-product-gallery-thumbnails--position-bottom) & {
|
||||
flex-direction: column;
|
||||
gap: 15px 0;
|
||||
}
|
||||
}
|
||||
|
||||
@for $i from 3 through 8 {
|
||||
// Calculate the total width occupied by the gaps between thumbnails.
|
||||
$gap-width: $thumbnails-gap * ($i - 1);
|
||||
|
||||
// Calculate the border width, which is multiplied by 2 to account for both sides of each thumbnail.
|
||||
$border-width: ($i * 1px * 2);
|
||||
|
||||
$additional-space: $i * 1px;
|
||||
|
||||
.wc-block-product-gallery-thumbnails--number-of-thumbnails-#{$i}:not(.wc-block-product-gallery-thumbnails--position-bottom) {
|
||||
flex-basis: calc((100% - #{$gap-width} - #{$border-width} - #{$additional-space}) / #{$i});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ $gallery-next-previous-inside-image: "#{$gallery}:not([data-next-previous-button
|
|||
|
||||
$outside-image-offset: 30px;
|
||||
$outside-image-max-width: calc(100% - (2 * $outside-image-offset));
|
||||
$thumbnails-gap: 15px;
|
||||
$default-number-of-thumbnails: 3;
|
||||
|
||||
// Product Gallery
|
||||
#{$gallery} {
|
||||
|
@ -55,6 +57,7 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset));
|
|||
height: fit-content;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
|
||||
// Restrict the width of the Large Image when the Next/Previous buttons are outside the image.
|
||||
#{$gallery-next-previous-outside-image} & .wc-block-product-gallery-large-image__image-element {
|
||||
|
@ -64,10 +67,13 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset));
|
|||
}
|
||||
|
||||
.wc-block-product-gallery-large-image__wrapper {
|
||||
aspect-ratio: 1 / 1;
|
||||
flex-shrink: 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.wc-block-product-gallery-large-image__container {
|
||||
|
@ -93,7 +99,8 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset));
|
|||
margin: 0 auto;
|
||||
z-index: 1;
|
||||
transition: all 0.1s linear;
|
||||
width: auto;
|
||||
aspect-ratio: 1 / 1;
|
||||
object-fit: contain;
|
||||
|
||||
// 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 {
|
||||
|
@ -234,20 +241,82 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset));
|
|||
|
||||
// Thumbnails
|
||||
#{$thumbnails} {
|
||||
display: flex;
|
||||
|
||||
img {
|
||||
cursor: pointer;
|
||||
height: auto;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.is-vertical & {
|
||||
display: flex;
|
||||
#{$gallery}[data-thumbnails-position='bottom'] & {
|
||||
flex-direction: row;
|
||||
gap: 0 15px;
|
||||
}
|
||||
|
||||
#{$gallery}:not([data-thumbnails-position='bottom']) & {
|
||||
flex-direction: column;
|
||||
gap: 15px 0;
|
||||
|
||||
// Calculate the total width occupied by the gaps between thumbnails.
|
||||
$gap-width: $thumbnails-gap * ($default-number-of-thumbnails - 1);
|
||||
|
||||
// Calculate the border width, which is multiplied by 2 to account for both sides of each thumbnail.
|
||||
$border-width: #{$default-number-of-thumbnails * 1px * 2};
|
||||
|
||||
// Calculate the width of each thumbnail by accounting for the gap, border, and additional space.
|
||||
flex-basis: calc((100% - #{$gap-width} - #{$border-width} - 4px) / #{$default-number-of-thumbnails});
|
||||
}
|
||||
|
||||
@for $i from 3 through 8 {
|
||||
#{$gallery}[data-thumbnails-number-of-thumbnails='#{$i}']:not([data-thumbnails-position='bottom']) & {
|
||||
// Calculate the total width occupied by the gaps between thumbnails.
|
||||
$gap-width: $thumbnails-gap * ($i - 1);
|
||||
|
||||
// Calculate the border width, which is multiplied by 2 to account for both sides of each thumbnail.
|
||||
$border-width: $i * 1px * 2;
|
||||
|
||||
flex-basis: calc((100% - #{$gap-width} - #{$border-width}) / $i);
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-product-gallery-thumbnails__thumbnail {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 5px;
|
||||
border: 1px solid rgba($color: #000, $alpha: 0.1);
|
||||
height: auto;
|
||||
width: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
aspect-ratio: 1 / 1;
|
||||
position: relative;
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
|
||||
img {
|
||||
aspect-ratio: 1 / 1;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
display: block;
|
||||
padding-top: 100%;
|
||||
}
|
||||
|
||||
@for $i from 3 through 8 {
|
||||
#{$gallery}[data-thumbnails-number-of-thumbnails='#{$i}'][data-thumbnails-position="bottom"] & {
|
||||
// Calculate the total width occupied by the gaps between thumbnails.
|
||||
$gap-width: $thumbnails-gap * ($i - 1);
|
||||
|
||||
// Calculate the border width, which is multiplied by 2 to account for both sides of each thumbnail.
|
||||
$border-width: $i * 1px * 2;
|
||||
|
||||
$thumbnail-width: calc((100% - #{$gap-width} - #{$border-width}) / $i);
|
||||
|
||||
flex: 0 0 $thumbnail-width;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -133,8 +133,8 @@ class ProductGalleryThumbnails extends AbstractBlock {
|
|||
|
||||
if ( $product ) {
|
||||
$post_thumbnail_id = $product->get_image_id();
|
||||
$product_gallery_images = ProductGalleryUtils::get_product_gallery_images( $post_id, 'thumbnail', array(), 'wc-block-product-gallery-thumbnails__thumbnail' );
|
||||
if ( $product_gallery_images && $post_thumbnail_id ) {
|
||||
$product_gallery_images = ProductGalleryUtils::get_product_gallery_images( $post_id, 'full', array(), 'wc-block-product-gallery-thumbnails__thumbnail' );
|
||||
if ( $product_gallery_images && count( $product_gallery_images ) > 1 && $post_thumbnail_id ) {
|
||||
$html = '';
|
||||
$number_of_thumbnails = isset( $block->context['thumbnailsNumberOfThumbnails'] ) ? $block->context['thumbnailsNumberOfThumbnails'] : 3;
|
||||
$mode = $block->context['mode'] ?? '';
|
||||
|
|
|
@ -52,6 +52,8 @@ test.describe( `${ blockData.name }`, () => {
|
|||
page,
|
||||
editor,
|
||||
pageObject,
|
||||
editorUtils,
|
||||
frontendUtils,
|
||||
} ) => {
|
||||
await editor.insertBlock( {
|
||||
name: 'woocommerce/product-gallery',
|
||||
|
@ -60,22 +62,38 @@ test.describe( `${ blockData.name }`, () => {
|
|||
const thumbnailsBlock = await pageObject.getThumbnailsBlock( {
|
||||
page: 'editor',
|
||||
} );
|
||||
const largeImageBlock = await pageObject.getMainImageBlock( {
|
||||
page: 'editor',
|
||||
} );
|
||||
|
||||
const thumbnailsBlockBoundingRect = await thumbnailsBlock.boundingBox();
|
||||
const largeImageBlockBoundingRect = await largeImageBlock.boundingBox();
|
||||
|
||||
await expect( thumbnailsBlock ).toBeVisible();
|
||||
// Check the default position: on the left of the large image
|
||||
await expect( thumbnailsBlockBoundingRect?.y ).toBeGreaterThan(
|
||||
largeImageBlockBoundingRect?.y as number
|
||||
|
||||
// We should refactor this.
|
||||
// eslint-disable-next-line playwright/no-wait-for-timeout
|
||||
await page.waitForTimeout( 500 );
|
||||
|
||||
// Test the default (left) position of thumbnails by cross-checking:
|
||||
// - The Gallery block has the classes "is-layout-flex" and "is-nowrap".
|
||||
// - The Thumbnails block has a lower index than the Large Image block.
|
||||
|
||||
const groupBlock = (
|
||||
await editorUtils.getBlockByTypeWithParent(
|
||||
'core/group',
|
||||
'woocommerce/product-gallery'
|
||||
)
|
||||
).first();
|
||||
|
||||
const groupBlockClassAttribute = await groupBlock.getAttribute(
|
||||
'class'
|
||||
);
|
||||
await expect( thumbnailsBlockBoundingRect?.x ).toBeLessThan(
|
||||
largeImageBlockBoundingRect?.x as number
|
||||
expect( groupBlockClassAttribute ).toContain( 'is-layout-flex' );
|
||||
expect( groupBlockClassAttribute ).toContain( 'is-nowrap' );
|
||||
|
||||
const isThumbnailsBlockEarlier = await editorUtils.isBlockEarlierThan(
|
||||
groupBlock,
|
||||
'woocommerce/product-gallery-thumbnails',
|
||||
'core/group'
|
||||
);
|
||||
|
||||
expect( isThumbnailsBlockEarlier ).toBe( true );
|
||||
|
||||
await Promise.all( [
|
||||
editor.saveSiteEditorEntities(),
|
||||
page.waitForResponse( ( response ) =>
|
||||
|
@ -87,27 +105,27 @@ test.describe( `${ blockData.name }`, () => {
|
|||
waitUntil: 'commit',
|
||||
} );
|
||||
|
||||
const thumbnailsBlockFrontend = await pageObject.getThumbnailsBlock( {
|
||||
page: 'frontend',
|
||||
} );
|
||||
const groupBlockFrontend = (
|
||||
await frontendUtils.getBlockByClassWithParent(
|
||||
'wp-block-group',
|
||||
'woocommerce/product-gallery'
|
||||
)
|
||||
).first();
|
||||
|
||||
const largeImageBlockFrontend = await pageObject.getMainImageBlock( {
|
||||
page: 'frontend',
|
||||
} );
|
||||
|
||||
const thumbnailsBlockFrontendBoundingRect =
|
||||
await thumbnailsBlockFrontend.boundingBox();
|
||||
const largeImageBlockFrontendBoundingRect =
|
||||
await largeImageBlockFrontend.boundingBox();
|
||||
|
||||
await expect( thumbnailsBlockFrontend ).toBeVisible();
|
||||
// Check the default position: on the left of the large image
|
||||
await expect( thumbnailsBlockFrontendBoundingRect?.y ).toBeGreaterThan(
|
||||
largeImageBlockFrontendBoundingRect?.y as number
|
||||
);
|
||||
await expect( thumbnailsBlockFrontendBoundingRect?.x ).toBeLessThan(
|
||||
largeImageBlockFrontendBoundingRect?.x as number
|
||||
const groupBlockFrontendClassAttribute =
|
||||
await groupBlockFrontend.getAttribute( 'class' );
|
||||
expect( groupBlockFrontendClassAttribute ).toContain(
|
||||
'is-layout-flex'
|
||||
);
|
||||
expect( groupBlockFrontendClassAttribute ).toContain( 'is-nowrap' );
|
||||
|
||||
const isThumbnailsFrontendBlockEarlier =
|
||||
await frontendUtils.isBlockEarlierThanGroupBlock(
|
||||
groupBlockFrontend,
|
||||
'woocommerce/product-gallery-thumbnails'
|
||||
);
|
||||
|
||||
expect( isThumbnailsFrontendBlockEarlier ).toBe( true );
|
||||
} );
|
||||
|
||||
test.describe( `${ blockData.name } Settings`, () => {
|
||||
|
|
|
@ -327,6 +327,8 @@ test.describe( `${ blockData.name }`, () => {
|
|||
const width = image?.width;
|
||||
|
||||
// Allow 1 pixel of difference.
|
||||
expect( width === height + 1 || width === height - 1 ).toBeTruthy();
|
||||
expect(
|
||||
width === height + 1 || width === height - 1 || width === height
|
||||
).toBeTruthy();
|
||||
} );
|
||||
} );
|
||||
|
|
Loading…
Reference in New Issue