Product Gallery Thumbnails: Add View all overlay (https://github.com/woocommerce/woocommerce-blocks/pull/11087)
* Product Gallery Thumbnails: Add View All link to the last thumbnail (non-interactive) * Product Gallery Thumbnails: Add interactivity to the View All overlay * Product Gallery Thumbnails: Refactor View all html to make it more readable * Product Gallery Thumbnails: Fix woocommerce/woocommerce-blocks#11100 - Load all thumbnails and hide the View all overlay when in Dialog * Product Gallery Thumbnails: Fix woocommerce/woocommerce-blocks#11099 - Enable the dialog for the View all thumbnails overlay even when the 'Full-screen when clicked' setting is disabled * Product Gallery Thumbnails: Remove unnecessary concatenation from the View all html * Product Gallery Thumbnails: Abstract the View All conditions into separate functions for readability * Product Gallery Thumbnails: Add escaping to the View all plain text string * E2E: Fix the Sale Badge and Single Product Template tests by selecting the first Sale Badge
This commit is contained in:
parent
5ccfecef60
commit
f74aafe1bf
|
@ -22,6 +22,7 @@
|
|||
"pagerDisplayMode": "pagerDisplayMode",
|
||||
"hoverZoom": "hoverZoom",
|
||||
"fullScreenOnClick": "fullScreenOnClick",
|
||||
"mode": "mode",
|
||||
"cropImages": "cropImages"
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -87,7 +87,11 @@ interactivityStore(
|
|||
context: Context;
|
||||
event: Event;
|
||||
} ) => {
|
||||
if ( ( event.target as HTMLElement ).tagName === 'IMG' ) {
|
||||
if (
|
||||
( event.target as HTMLElement ).classList.contains(
|
||||
'wc-block-product-gallery-dialog-on-click'
|
||||
)
|
||||
) {
|
||||
context.woocommerce.isDialogOpen = true;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"description": "Display the Thumbnails of a product.",
|
||||
"category": "woocommerce",
|
||||
"keywords": [ "WooCommerce" ],
|
||||
"usesContext": [ "postId", "thumbnailsPosition", "thumbnailsNumberOfThumbnails", "productGalleryClientId" ],
|
||||
"usesContext": [ "postId", "thumbnailsPosition", "thumbnailsNumberOfThumbnails", "productGalleryClientId", "mode" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"ancestor": [ "woocommerce/product-gallery" ],
|
||||
"supports": {
|
||||
|
|
|
@ -245,6 +245,35 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset));
|
|||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 5px;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
.wc-block-product-gallery-thumbnails__thumbnail__overlay {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.wc-block-product-gallery-thumbnails__thumbnail__remaining-thumbnails-count {
|
||||
@include font-size(large);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.wc-block-product-gallery-thumbnails__thumbnail__view-all {
|
||||
@include font-size(smaller);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.wc-block-product-gallery-thumbnails__thumbnail__remaining-thumbnails-count,
|
||||
.wc-block-product-gallery-thumbnails__thumbnail__view-all {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ class ProductGallery extends AbstractBlock {
|
|||
|
||||
$number_of_thumbnails = $block->attributes['thumbnailsNumberOfThumbnails'] ?? 0;
|
||||
$classname = $attributes['className'] ?? '';
|
||||
$dialog = ( true === $attributes['fullScreenOnClick'] && isset( $attributes['mode'] ) && 'full' !== $attributes['mode'] ) ? $this->render_dialog() : '';
|
||||
$dialog = isset( $attributes['mode'] ) && 'full' !== $attributes['mode'] ? $this->render_dialog() : '';
|
||||
$post_id = $block->context['postId'] ?? '';
|
||||
$product = wc_get_product( $post_id );
|
||||
$product_id = strval( $product->get_id() );
|
||||
|
|
|
@ -124,7 +124,7 @@ class ProductGalleryLargeImage extends AbstractBlock {
|
|||
);
|
||||
|
||||
if ( $context['fullScreenOnClick'] ) {
|
||||
$attributes['class'] .= ' wc-block-woocommerce-product-gallery-large-image__image--full-screen-on-click';
|
||||
$attributes['class'] .= ' wc-block-woocommerce-product-gallery-large-image__image--full-screen-on-click wc-block-product-gallery-dialog-on-click';
|
||||
}
|
||||
|
||||
if ( $context['hoverZoom'] ) {
|
||||
|
|
|
@ -37,7 +37,77 @@ class ProductGalleryThumbnails extends AbstractBlock {
|
|||
* @return string[]
|
||||
*/
|
||||
protected function get_block_type_uses_context() {
|
||||
return [ 'productGalleryClientId', 'postId', 'thumbnailsNumberOfThumbnails', 'thumbnailsPosition' ];
|
||||
return [ 'productGalleryClientId', 'postId', 'thumbnailsNumberOfThumbnails', 'thumbnailsPosition', 'mode' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the View All markup.
|
||||
*
|
||||
* @param int $remaining_thumbnails_count The number of thumbnails that are not displayed.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_view_all_html( $remaining_thumbnails_count ) {
|
||||
$view_all_html = '<div class="wc-block-product-gallery-thumbnails__thumbnail__overlay wc-block-product-gallery-dialog-on-click" data-wc-on--click="actions.woocommerce.handleClick">
|
||||
<span class="wc-block-product-gallery-thumbnails__thumbnail__remaining-thumbnails-count wc-block-product-gallery-dialog-on-click">+%1$s</span>
|
||||
<span class="wc-block-product-gallery-thumbnails__thumbnail__view-all wc-block-product-gallery-dialog-on-click">%2$s</span>
|
||||
</div>';
|
||||
|
||||
return sprintf(
|
||||
$view_all_html,
|
||||
esc_html( $remaining_thumbnails_count ),
|
||||
esc_html__( 'View all', 'woo-gutenberg-products-block' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject View All markup into the product thumbnail HTML.
|
||||
*
|
||||
* @param string $thumbnail_html The thumbnail HTML.
|
||||
* @param string $view_all_html The view all HTML.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function inject_view_all( $thumbnail_html, $view_all_html ) {
|
||||
|
||||
// Find the position of the last </div>.
|
||||
$pos = strrpos( $thumbnail_html, '</div>' );
|
||||
|
||||
if ( false !== $pos ) {
|
||||
// Inject the view_all_html at the correct position.
|
||||
$html = substr_replace( $thumbnail_html, $view_all_html, $pos, 0 );
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the thumbnails should be limited.
|
||||
*
|
||||
* @param string $mode Mode of the gallery. Expected values: 'standard'.
|
||||
* @param int $thumbnails_count Current count of processed thumbnails.
|
||||
* @param int $number_of_thumbnails Number of thumbnails configured to display.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function should_limit_thumbnails( $mode, $thumbnails_count, $number_of_thumbnails ) {
|
||||
return 'standard' === $mode && $thumbnails_count > $number_of_thumbnails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if View All markup should be displayed.
|
||||
*
|
||||
* @param string $mode Mode of the gallery. Expected values: 'standard'.
|
||||
* @param int $thumbnails_count Current count of processed thumbnails.
|
||||
* @param array $product_gallery_images Array of product gallery image HTML strings.
|
||||
* @param int $number_of_thumbnails Number of thumbnails configured to display.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function should_display_view_all( $mode, $thumbnails_count, $product_gallery_images, $number_of_thumbnails ) {
|
||||
return 'standard' === $mode &&
|
||||
$thumbnails_count === $number_of_thumbnails &&
|
||||
count( $product_gallery_images ) > $number_of_thumbnails;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,22 +137,31 @@ class ProductGalleryThumbnails extends AbstractBlock {
|
|||
if ( $product_gallery_images && $post_thumbnail_id ) {
|
||||
$html = '';
|
||||
$number_of_thumbnails = isset( $block->context['thumbnailsNumberOfThumbnails'] ) ? $block->context['thumbnailsNumberOfThumbnails'] : 3;
|
||||
$mode = $block->context['mode'] ?? '';
|
||||
$thumbnails_count = 1;
|
||||
|
||||
foreach ( $product_gallery_images as $product_gallery_image_html ) {
|
||||
if ( $thumbnails_count > $number_of_thumbnails ) {
|
||||
// Limit the number of thumbnails only in the standard mode (and not in dialog).
|
||||
if ( $this->should_limit_thumbnails( $mode, $thumbnails_count, $number_of_thumbnails ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
$processor = new \WP_HTML_Tag_Processor( $product_gallery_image_html );
|
||||
// If not in dialog and it's the last thumbnail and the number of product gallery images is greater than the number of thumbnails settings output the View All markup.
|
||||
if ( $this->should_display_view_all( $mode, $thumbnails_count, $product_gallery_images, $number_of_thumbnails ) ) {
|
||||
$remaining_thumbnails_count = count( $product_gallery_images ) - $number_of_thumbnails;
|
||||
$product_gallery_image_html = $this->inject_view_all( $product_gallery_image_html, $this->generate_view_all_html( $remaining_thumbnails_count ) );
|
||||
$html .= $product_gallery_image_html;
|
||||
} else {
|
||||
$processor = new \WP_HTML_Tag_Processor( $product_gallery_image_html );
|
||||
|
||||
if ( $processor->next_tag( 'img' ) ) {
|
||||
$processor->set_attribute(
|
||||
'data-wc-on--click',
|
||||
'actions.woocommerce.thumbnails.handleClick'
|
||||
);
|
||||
if ( $processor->next_tag( 'img' ) ) {
|
||||
$processor->set_attribute(
|
||||
'data-wc-on--click',
|
||||
'actions.woocommerce.thumbnails.handleClick'
|
||||
);
|
||||
|
||||
$html .= $processor->get_updated_html();
|
||||
$html .= $processor->get_updated_html();
|
||||
}
|
||||
}
|
||||
|
||||
$thumbnails_count++;
|
||||
|
|
|
@ -59,12 +59,14 @@ const getBoundingClientRect = async ( {
|
|||
blockData.selectors[ isFrontend ? 'frontend' : 'editor' ]
|
||||
.productSaleBadge
|
||||
)
|
||||
.first()
|
||||
.evaluate( ( el ) => el.getBoundingClientRect() ),
|
||||
productSaleBadgeContainer: await page
|
||||
.locator(
|
||||
blockData.selectors[ isFrontend ? 'frontend' : 'editor' ]
|
||||
.productSaleBadgeContainer
|
||||
)
|
||||
.first()
|
||||
.evaluate( ( el ) => el.getBoundingClientRect() ),
|
||||
};
|
||||
};
|
||||
|
@ -127,7 +129,7 @@ test.describe( `${ blockData.name }`, () => {
|
|||
|
||||
const block = await frontendUtils.getBlockByName( blockData.name );
|
||||
|
||||
await expect( block ).toBeVisible();
|
||||
await expect( block.first() ).toBeVisible();
|
||||
} );
|
||||
|
||||
test( `should be not rendered when the product isn't on sale the frontend side`, async ( {
|
||||
|
|
Loading…
Reference in New Issue