Commit 1943a72118 for woocommerce
commit 1943a72118f477c23b436a0f9b9d297232524e80
Author: Karol Manijak <20098064+kmanijak@users.noreply.github.com>
Date: Thu Apr 17 08:51:23 2025 +0200
Product Gallery: improve UI for Next/Prev buttons (#57135)
* Enable supports in arrows
* Provide basic supports
* Simplify the arrows structure
* Align the frontend with editor
* Add some basic styling
* Fix lint
* Fix lint
* Update icons and align them correctly
* Improve classnames and styling
* Apply changes on the frontend
* Add supports styles in editor
* Support colors in editor
* Simplify the arrows structure even more
* Change sprintf to ob_clean
* Fix styles
* Add support for shadow
* Add shadow support in StyleAttributesUtils
* Fix lint
* Change the disabled arrow opacity from 20% to 35%
* Add changelog
* Update selectors in tests
* Remove unnecessary z-index
* Fix the editor util for getting next/prev button
* Replace str_contains with strpos for PHP 7 support
diff --git a/plugins/woocommerce/changelog/wooplug-3792-product-gallery-improve-ui-for-nextprev-buttons b/plugins/woocommerce/changelog/wooplug-3792-product-gallery-improve-ui-for-nextprev-buttons
new file mode 100644
index 0000000000..7329704ffc
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooplug-3792-product-gallery-improve-ui-for-nextprev-buttons
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Product Gallery: revamp Next/Prev arrows UI
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 35e4bccad3..ed4adc2c77 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
@@ -52,7 +52,14 @@ const TEMPLATE: InnerBlockTemplate[] = [
},
},
],
- [ 'woocommerce/product-gallery-large-image-next-previous' ],
+ [
+ 'woocommerce/product-gallery-large-image-next-previous',
+ {
+ style: {
+ border: { radius: '100%' },
+ },
+ },
+ ],
],
],
],
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/block.json
index 6fccb38194..47e520f8f9 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/block.json
@@ -9,7 +9,15 @@
"usesContext": [ "postId" ],
"textdomain": "woocommerce",
"supports": {
- "interactivity": true
+ "interactivity": true,
+ "color": {
+ "background": true,
+ "text": true
+ },
+ "shadow": true,
+ "__experimentalBorder": {
+ "radius": true
+ }
},
"ancestor": [ "woocommerce/product-gallery-large-image" ]
}
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/edit.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/edit.tsx
index 2b17a4399e..418f3138a2 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/edit.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/edit.tsx
@@ -2,31 +2,25 @@
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
-import clsx from 'clsx';
/**
* Internal dependencies
*/
-import { PrevButton, NextButton } from './icons';
+import { PrevIcon, NextIcon } from './icons';
export const Edit = (): JSX.Element => {
const blockProps = useBlockProps( {
- className: clsx(
- 'wc-block-editor-product-gallery-large-image-next-previous',
- 'wc-block-product-gallery-large-image-next-previous'
- ),
+ className: 'wc-block-product-gallery-large-image-next-previous__button',
} );
return (
- <div { ...blockProps }>
- <div
- className={ clsx(
- 'wc-block-product-gallery-large-image-next-previous-container'
- ) }
- >
- <PrevButton />
- <NextButton />
- </div>
+ <div className="wc-block-product-gallery-large-image-next-previous">
+ <button { ...blockProps } disabled>
+ <PrevIcon className="wc-block-product-gallery-large-image-next-previous__icon wc-block-product-gallery-large-image-next-previous__icon--left" />
+ </button>
+ <button { ...blockProps }>
+ <NextIcon className="wc-block-product-gallery-large-image-next-previous__icon wc-block-product-gallery-large-image-next-previous__icon--right" />
+ </button>
</div>
);
};
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/icons.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/icons.tsx
index 3892e7adca..38ea96860e 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/icons.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-next-previous-buttons/icons.tsx
@@ -1,8 +1,3 @@
-/**
- * External dependencies
- */
-import { SVG } from '@wordpress/primitives';
-
export const Icon = () => (
<svg
width="18"
@@ -20,104 +15,36 @@ export const Icon = () => (
</svg>
);
-export const NextButton = () => (
- <SVG
+export const PrevIcon = ( { className }: { className: string } ) => (
+ <svg
xmlns="http://www.w3.org/2000/svg"
- width="49"
- height="48"
- viewBox="0 0 49 48"
+ width="8"
+ height="12"
fill="none"
- className={ `wc-block-product-gallery-large-image-next-previous-right` }
+ className={ className }
>
- <g filter="url(#filter0_b_397_11354)">
- <rect
- x="0.5"
- width="48"
- height="48"
- rx="5"
- fill="black"
- fillOpacity="0.5"
- />
- <path
- d="M21.7001 12L19.3 14L28.5 24L19.3 34L21.7001 36L32.5 24L21.7001 12Z"
- fill="white"
- />
- </g>
- <defs>
- <filter
- id="filter0_b_397_11354"
- x="-9.5"
- y="-10"
- width="68"
- height="68"
- filterUnits="userSpaceOnUse"
- colorInterpolationFilters="sRGB"
- >
- <feFlood floodOpacity="0" result="BackgroundImageFix" />
- <feGaussianBlur in="BackgroundImageFix" stdDeviation="5" />
- <feComposite
- in2="SourceAlpha"
- operator="in"
- result="effect1_backgroundBlur_397_11354"
- />
- <feBlend
- mode="normal"
- in="SourceGraphic"
- in2="effect1_backgroundBlur_397_11354"
- result="shape"
- />
- </filter>
- </defs>
- </SVG>
+ <path
+ fill="currentColor"
+ fillRule="evenodd"
+ d="M6.445 12.005.986 6 6.445-.005l1.11 1.01L3.014 6l4.54 4.995-1.109 1.01Z"
+ clipRule="evenodd"
+ />
+ </svg>
);
-export const PrevButton = () => (
- <SVG
+export const NextIcon = ( { className }: { className: string } ) => (
+ <svg
xmlns="http://www.w3.org/2000/svg"
- width="49"
- height="48"
- viewBox="0 0 49 48"
+ width="8"
+ height="12"
fill="none"
- className={ `wc-block-product-gallery-large-image-next-previous-left` }
+ className={ className }
>
- <g filter="url(#filter0_b_397_11356)">
- <rect
- x="0.5"
- width="48"
- height="48"
- rx="5"
- fill="black"
- fillOpacity="0.5"
- />
- <path
- d="M28.1 12L30.5 14L21.3 24L30.5 34L28.1 36L17.3 24L28.1 12Z"
- fill="white"
- />
- </g>
- <defs>
- <filter
- id="filter0_b_397_11356"
- x="-9.5"
- y="-10"
- width="68"
- height="68"
- filterUnits="userSpaceOnUse"
- colorInterpolationFilters="sRGB"
- >
- <feFlood floodOpacity="0" result="BackgroundImageFix" />
- <feGaussianBlur in="BackgroundImageFix" stdDeviation="5" />
- <feComposite
- in2="SourceAlpha"
- operator="in"
- result="effect1_backgroundBlur_397_11356"
- />
- <feBlend
- mode="normal"
- in="SourceGraphic"
- in2="effect1_backgroundBlur_397_11356"
- result="shape"
- />
- </filter>
- </defs>
- </SVG>
+ <path
+ fill="currentColor"
+ fillRule="evenodd"
+ d="M1.555-.004 7.014 6l-5.459 6.005-1.11-1.01L4.986 6 .446 1.005l1.109-1.01Z"
+ clipRule="evenodd"
+ />
+ </svg>
);
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 463e970dd0..0c7859fb0e 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
@@ -78,32 +78,40 @@ $dialog-padding: 20px;
}
.wc-block-product-gallery-large-image-next-previous {
- position: absolute;
display: flex;
align-items: center;
+ justify-content: space-between;
width: 100%;
height: 100%;
- &-container {
- width: 100%;
- display: flex;
- justify-content: space-between;
+ &__icon {
+ color: inherit;
}
- svg {
- z-index: 1;
+ // Icons are centred but they are perceived off hence adjusting with padding.
+ &__icon--left {
+ padding: 2px 2px 0 0;
+ }
+
+ &__icon--right {
+ padding: 2px 0 0 2px;
}
- button {
+ &__button {
cursor: pointer;
z-index: 3;
pointer-events: all;
- padding: 0;
border: none;
- background: none;
+ width: 40px;
+ height: 40px;
+ font-size: 12px;
+ padding: 0;
+ background: #fff;
&:disabled {
- opacity: 50%;
+ .wc-block-product-gallery-large-image-next-previous__icon {
+ opacity: 35%;
+ }
cursor: not-allowed;
}
}
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/product-gallery/inner-blocks/product-gallery-large-image-next-previous/product-gallery-large-image-next-previous.block_theme.spec.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/product-gallery/inner-blocks/product-gallery-large-image-next-previous/product-gallery-large-image-next-previous.block_theme.spec.ts
index bed023f691..5b9cd39ad3 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/product-gallery/inner-blocks/product-gallery-large-image-next-previous/product-gallery-large-image-next-previous.block_theme.spec.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/product-gallery/inner-blocks/product-gallery-large-image-next-previous/product-gallery-large-image-next-previous.block_theme.spec.ts
@@ -17,9 +17,9 @@ const blockData = {
frontend: {},
editor: {
leftArrow:
- '.wc-block-product-gallery-large-image-next-previous-left',
+ '.wc-block-product-gallery-large-image-next-previous__icon--left',
rightArrow:
- '.wc-block-product-gallery-large-image-next-previous-right',
+ '.wc-block-product-gallery-large-image-next-previous__icon--right',
},
},
slug: 'single-product',
@@ -89,11 +89,13 @@ test.describe( `${ blockData.name }`, () => {
name: 'woocommerce/product-gallery',
} );
- const block = await pageObject.getNextPreviousButtonsBlock( {
+ const blocks = await pageObject.getNextPreviousButtonsBlock( {
page: 'editor',
} );
- await expect( block ).toBeVisible();
+ // There are two "instances" of the block in the editor, so we need to check both.
+ await expect( blocks.nth( 0 ) ).toBeVisible();
+ await expect( blocks.nth( 1 ) ).toBeVisible();
} );
test( 'Renders Next/Previous Button block on the frontend side', async ( {
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/product-gallery/product-gallery.block_theme.spec.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/product-gallery/product-gallery.block_theme.spec.ts
index c29ed50c16..3a89b57078 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/product-gallery/product-gallery.block_theme.spec.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/product-gallery/product-gallery.block_theme.spec.ts
@@ -212,7 +212,7 @@ test.describe( `${ blockData.name }`, () => {
const nextButton = page
.locator(
- '.wc-block-product-gallery-large-image-next-previous--button'
+ '.wc-block-product-gallery-large-image-next-previous__button'
)
.nth( 1 );
await nextButton.click();
@@ -227,7 +227,7 @@ test.describe( `${ blockData.name }`, () => {
const previousButton = page
.locator(
- '.wc-block-product-gallery-large-image-next-previous--button'
+ '.wc-block-product-gallery-large-image-next-previous__button'
)
.first();
await previousButton.click();
@@ -275,7 +275,7 @@ test.describe( `${ blockData.name }`, () => {
const nextButton = page
.locator(
- '.wc-block-product-gallery-large-image-next-previous--button'
+ '.wc-block-product-gallery-large-image-next-previous__button'
)
.nth( 1 );
await nextButton.click();
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductGalleryLargeImageNextPrevious.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductGalleryLargeImageNextPrevious.php
index f5cf4b30f5..ba80644b36 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductGalleryLargeImageNextPrevious.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductGalleryLargeImageNextPrevious.php
@@ -3,7 +3,7 @@
namespace Automattic\WooCommerce\Blocks\BlockTypes;
use Automattic\WooCommerce\Blocks\Utils\ProductGalleryUtils;
-
+use Automattic\WooCommerce\Blocks\Utils\StyleAttributesUtils;
/**
* ProductGalleryLargeImage class.
*/
@@ -59,96 +59,62 @@ class ProductGalleryLargeImageNextPrevious extends AbstractBlock {
return '';
}
- $prev_button = $this->get_button( 'previous' );
- $p = new \WP_HTML_Tag_Processor( $prev_button );
-
- if ( $p->next_tag() ) {
- $p->set_attribute(
- 'data-wp-on--click',
- 'actions.selectPreviousImage'
- );
- $p->set_attribute(
- 'aria-label',
- __( 'Previous image', 'woocommerce' )
- );
- $prev_button = $p->get_updated_html();
- }
-
- $next_button = $this->get_button( 'next' );
- $p = new \WP_HTML_Tag_Processor( $next_button );
-
- if ( $p->next_tag() ) {
- $p->set_attribute(
- 'data-wp-on--click',
- 'actions.selectNextImage'
- );
- $p->set_attribute(
- 'aria-label',
- __( 'Next image', 'woocommerce' )
- );
- $next_button = $p->get_updated_html();
- }
-
- return strtr(
- '<div
- class="wc-block-product-gallery-large-image-next-previous wp-block-woocommerce-product-gallery-large-image-next-previous"
- data-wp-interactive=\'{data_wp_interactive}\'
+ $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes );
+
+ ob_start();
+ ?>
+ <div
+ class="wc-block-product-gallery-large-image-next-previous"
+ data-wp-interactive="woocommerce/product-gallery"
+ >
+ <button
+ class="wc-block-product-gallery-large-image-next-previous__button <?php echo esc_attr( $classes_and_styles['classes'] ); ?>"
+ style="<?php echo esc_attr( $classes_and_styles['styles'] ); ?>"
+ data-wp-on--click="actions.selectPreviousImage"
+ data-wp-bind--disabled="context.disableLeft"
+ aria-label="Previous image"
>
- <div class="wc-block-product-gallery-large-image-next-previous-container">
- {prev_button}
- {next_button}
- </div>
- </div>',
- array(
- '{prev_button}' => $prev_button,
- '{next_button}' => $next_button,
- '{data_wp_interactive}' => 'woocommerce/product-gallery',
- )
- );
- }
-
- /**
- * Generates the HTML for a next or previous button for the product gallery large image.
- *
- * @param string $button_type The type of button to generate. Either 'previous' or 'next'.
- * @return string The HTML for the generated button.
- */
- protected function get_button( $button_type ) {
- $previous_button_icon_path = 'M28.1 12L30.5 14L21.3 24L30.5 34L28.1 36L17.3 24L28.1 12Z';
- $next_button_icon_path = 'M21.7001 12L19.3 14L28.5 24L19.3 34L21.7001 36L32.5 24L21.7001 12Z';
- $icon_path = $previous_button_icon_path;
- $button_side_class = 'left';
- $button_disabled_directive = 'context.disableLeft';
-
- if ( 'next' === $button_type ) {
- $icon_path = $next_button_icon_path;
- $button_side_class = 'right';
- $button_disabled_directive = 'context.disableRight';
- }
-
- return sprintf(
- '<button
- data-wp-bind--disabled="%1$s"
- class="wc-block-product-gallery-large-image-next-previous--button wc-block-product-gallery-large-image-next-previous-%2$s"
+ <svg
+ class="wc-block-product-gallery-large-image-next-previous__icon wc-block-product-gallery-large-image-next-previous__icon--left"
+ xmlns="http://www.w3.org/2000/svg"
+ width="8"
+ height="12"
+ fill="none"
+ >
+ <path
+ fill="currentColor"
+ fillRule="evenodd"
+ d="M6.445 12.005.986 6 6.445-.005l1.11 1.01L3.014 6l4.54 4.995-1.109 1.01Z"
+ clipRule="evenodd"
+ />
+ </svg>
+ </button>
+ <button
+ class="wc-block-product-gallery-large-image-next-previous__button <?php echo esc_attr( $classes_and_styles['classes'] ); ?>"
+ style="<?php echo esc_attr( $classes_and_styles['styles'] ); ?>"
+ data-wp-on--click="actions.selectNextImage"
+ data-wp-bind--disabled="context.disableRight"
+ aria-label="Next image"
>
- <svg xmlns="http://www.w3.org/2000/svg" width="49" height="48" viewBox="0 0 49 48" fill="none">
- <g filter="url(#filter0_b_397_11354)">
- <rect x="0.5" width="48" height="48" rx="5" fill="black" fill-opacity="0.5"/>
- <path d="%3$s" fill="white"/>
- </g>
- <defs>
- <filter id="filter0_b_397_11354" x="-9.5" y="-10" width="68" height="68" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
- <feFlood flood-opacity="0" result="BackgroundImageFix"/>
- <feGaussianBlur in="BackgroundImageFix" stdDeviation="5"/>
- <feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_397_11354"/>
- <feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_397_11354" result="shape"/>
- </filter>
- </defs>
+ <svg
+ class="wc-block-product-gallery-large-image-next-previous__icon wc-block-product-gallery-large-image-next-previous__icon--right"
+ xmlns="http://www.w3.org/2000/svg"
+ width="8"
+ height="12"
+ fill="none"
+ >
+ <path
+ fill="currentColor"
+ fillRule="evenodd"
+ d="M1.555-.004 7.014 6l-5.459 6.005-1.11-1.01L4.986 6 .446 1.005l1.109-1.01Z"
+ clipRule="evenodd"
+ />
</svg>
- </button>',
- $button_disabled_directive,
- $button_side_class,
- $icon_path
- );
+ </button>
+ </div>
+ <?php
+ $template = ob_get_clean();
+
+ return $template;
}
}
diff --git a/plugins/woocommerce/src/Blocks/Utils/StyleAttributesUtils.php b/plugins/woocommerce/src/Blocks/Utils/StyleAttributesUtils.php
index ea106afa5b..8314c0c964 100644
--- a/plugins/woocommerce/src/Blocks/Utils/StyleAttributesUtils.php
+++ b/plugins/woocommerce/src/Blocks/Utils/StyleAttributesUtils.php
@@ -24,7 +24,7 @@ class StyleAttributesUtils {
* @return (string)
*/
public static function get_color_value( $color_value ) {
- if ( is_string( $color_value ) && str_contains( $color_value, 'var:preset|color|' ) ) {
+ if ( is_string( $color_value ) && strpos( $color_value, 'var:preset|color|' ) !== false ) {
$color_value = str_replace( 'var:preset|color|', '', $color_value );
return sprintf( 'var(--wp--preset--color--%s)', $color_value );
}
@@ -43,6 +43,22 @@ class StyleAttributesUtils {
return "var(--wp--preset--color--$preset_name)";
}
+ /**
+ * Get CSS value for shadow preset. Returns the same value if it's not a preset.
+ *
+ * @param string $shadow_name Shadow name.
+ *
+ * @return string CSS value for shadow preset.
+ */
+ public static function get_shadow_value( $shadow_name ) {
+ if ( is_string( $shadow_name ) && strpos( $shadow_name, 'var:preset|shadow|' ) !== false ) {
+ $shadow_name = str_replace( 'var:preset|shadow|', '', $shadow_name );
+ return "var(--wp--preset--shadow--{$shadow_name})";
+ }
+
+ return $shadow_name;
+ }
+
/**
* If spacing value is in preset format, convert it to a CSS var. Else return same value
* For example:
@@ -55,7 +71,7 @@ class StyleAttributesUtils {
*/
public static function get_spacing_value( $spacing_value ) {
// Used following code as reference: https://github.com/WordPress/gutenberg/blob/cff6d70d6ff5a26e212958623dc3130569f95685/lib/block-supports/layout.php/#L219-L225.
- if ( is_string( $spacing_value ) && str_contains( $spacing_value, 'var:preset|spacing|' ) ) {
+ if ( is_string( $spacing_value ) && strpos( $spacing_value, 'var:preset|spacing|' ) !== false ) {
$spacing_value = str_replace( 'var:preset|spacing|', '', $spacing_value );
return sprintf( 'var(--wp--preset--spacing--%s)', $spacing_value );
}
@@ -583,6 +599,25 @@ class StyleAttributesUtils {
);
}
+ /**
+ * Get class and style for shadow from attributes.
+ *
+ * @param array $attributes Block attributes.
+ * @return array
+ */
+ public static function get_shadow_class_and_style( $attributes ) {
+ $shadow = $attributes['style']['shadow'] ?? null;
+
+ if ( ! $shadow ) {
+ return self::EMPTY_STYLE;
+ }
+
+ return array(
+ 'class' => null,
+ 'style' => sprintf( 'box-shadow: %s;', self::get_shadow_value( $shadow ) ),
+ );
+ }
+
/**
* Get space-separated style rules from block attributes.
*
@@ -734,6 +769,7 @@ class StyleAttributesUtils {
'line_height' => self::get_line_height_class_and_style( $attributes ),
'margin' => self::get_margin_class_and_style( $attributes ),
'padding' => self::get_padding_class_and_style( $attributes ),
+ 'shadow' => self::get_shadow_class_and_style( $attributes ),
'text_align' => self::get_text_align_class_and_style( $attributes ),
'text_color' => self::get_text_color_class_and_style( $attributes ),
'text_decoration' => self::get_text_decoration_class_and_style( $attributes ),