From f0528939b6b5273f6d99bb66f87ee4fa153836ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Thu, 27 Apr 2023 12:56:12 +0200 Subject: [PATCH 1/5] Only show the Mini Cart count badge when there are items in the cart --- .../blocks/mini-cart/quantity-badge/index.tsx | 24 +++++--- .../mini-cart/quantity-badge/style.scss | 5 +- assets/js/icons/index.js | 1 + .../js/icons/library/mini-cart-with-badge.tsx | 29 ++++++++++ assets/js/icons/library/mini-cart.tsx | 27 +++++---- src/BlockTypes/MiniCart.php | 58 +++++++++++++++---- 6 files changed, 114 insertions(+), 30 deletions(-) create mode 100644 assets/js/icons/library/mini-cart-with-badge.tsx diff --git a/assets/js/blocks/mini-cart/quantity-badge/index.tsx b/assets/js/blocks/mini-cart/quantity-badge/index.tsx index 68a4c042398..5f323f17daf 100644 --- a/assets/js/blocks/mini-cart/quantity-badge/index.tsx +++ b/assets/js/blocks/mini-cart/quantity-badge/index.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import { miniCart } from '@woocommerce/icons'; +import { miniCart, miniCartWithBadge } from '@woocommerce/icons'; import { Icon } from '@wordpress/icons'; /** @@ -17,12 +17,22 @@ interface Props { const QuantityBadge = ( { count }: Props ): JSX.Element => { return ( - - { count } + { count > 0 ? ( + <> + + { count } + + ) : ( + + ) } ); }; diff --git a/assets/js/blocks/mini-cart/quantity-badge/style.scss b/assets/js/blocks/mini-cart/quantity-badge/style.scss index 55afcd37285..42125190935 100644 --- a/assets/js/blocks/mini-cart/quantity-badge/style.scss +++ b/assets/js/blocks/mini-cart/quantity-badge/style.scss @@ -1,6 +1,7 @@ .wc-block-mini-cart__quantity-badge { align-items: center; display: flex; + position: relative; } .wc-block-mini-cart__badge { @@ -15,9 +16,11 @@ font-weight: 600; height: math.div(em(20px), 0.875); justify-content: center; - margin-left: math.div(em(-10px), 0.875); + left: 100%; + margin-left: -44%; min-width: math.div(em(20px), 0.875); padding: 0 em($gap-smallest); + position: absolute; transform: translateY(-50%); white-space: nowrap; z-index: 1; diff --git a/assets/js/icons/index.js b/assets/js/icons/index.js index 70acf6bdd56..1abccd1ad4c 100644 --- a/assets/js/icons/index.js +++ b/assets/js/icons/index.js @@ -12,6 +12,7 @@ export { default as totals } from './library/totals'; export { default as woo } from './library/woo'; export { default as miniCart } from './library/mini-cart'; export { default as miniCartAlt } from './library/mini-cart-alt'; +export { default as miniCartWithBadge } from './library/mini-cart-with-badge'; export { default as stacks } from './library/stacks'; export { default as Alert } from './library/alert'; export { default as customerAccount } from './library/customer-account'; diff --git a/assets/js/icons/library/mini-cart-with-badge.tsx b/assets/js/icons/library/mini-cart-with-badge.tsx new file mode 100644 index 00000000000..4eb8b960d68 --- /dev/null +++ b/assets/js/icons/library/mini-cart-with-badge.tsx @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import { SVG } from '@wordpress/primitives'; + +const miniCartWithBadge = ( + + + + + +); + +export default miniCartWithBadge; diff --git a/assets/js/icons/library/mini-cart.tsx b/assets/js/icons/library/mini-cart.tsx index bbf153045c3..7732463dcd1 100644 --- a/assets/js/icons/library/mini-cart.tsx +++ b/assets/js/icons/library/mini-cart.tsx @@ -6,22 +6,25 @@ import { SVG } from '@wordpress/primitives'; const miniCart = ( ); diff --git a/src/BlockTypes/MiniCart.php b/src/BlockTypes/MiniCart.php index ea674438ff5..a6352aca3f0 100644 --- a/src/BlockTypes/MiniCart.php +++ b/src/BlockTypes/MiniCart.php @@ -409,6 +409,40 @@ protected function render( $attributes, $content, $block ) { return $content . $this->get_markup( $attributes ); } + /** + * Returns whether the cart is empty. If the cart can't be found, it returns + * true as well. + * + * @return boolean Whether the cart is empty. + */ + protected function is_cart_empty() { + $cart = $this->get_cart_instance(); + if ( $cart ) { + return $cart->get_cart_contents_count() === 0; + } + return true; + } + + /** + * Returns the cart icon, depending on whether the cart is empty or not. + * + * @return string Cart SVG icon. + */ + protected function get_mini_cart_icon() { + if ( $this->is_cart_empty() ) { + return ' + + + + '; + } + return ' + + + + '; + } + /** * Render the markup for the Mini Cart block. * @@ -449,16 +483,20 @@ protected function get_markup( $attributes ) { $cart_contents_count, wp_strip_all_tags( wc_price( $cart_contents_total ) ) ); - $icon = ' - - - - '; - $button_html = $this->get_cart_price_markup( $attributes ) . ' - - ' . $icon . ' - ' . $cart_contents_count . ' - '; + $icon = $this->get_mini_cart_icon(); + + if ( $this->is_cart_empty() ) { + $button_html = $this->get_cart_price_markup( $attributes ) . ' + + ' . $icon . ' + '; + } else { + $button_html = $this->get_cart_price_markup( $attributes ) . ' + + ' . $icon . ' + ' . $cart_contents_count . ' + '; + } if ( is_cart() || is_checkout() ) { if ( $this->should_not_render_mini_cart( $attributes ) ) { From 2d2bb498dc595cc3260fc8e41f3d4ca8c4252732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 9 May 2023 08:58:57 +0200 Subject: [PATCH 2/5] Update badge to new design --- assets/js/blocks/mini-cart/frontend.ts | 28 +++++++++ .../blocks/mini-cart/quantity-badge/index.tsx | 26 +++------ .../mini-cart/quantity-badge/style.scss | 12 +++- assets/js/icons/index.js | 1 - .../js/icons/library/mini-cart-with-badge.tsx | 29 ---------- src/BlockTypes/MiniCart.php | 58 ++++--------------- 6 files changed, 57 insertions(+), 97 deletions(-) delete mode 100644 assets/js/icons/library/mini-cart-with-badge.tsx diff --git a/assets/js/blocks/mini-cart/frontend.ts b/assets/js/blocks/mini-cart/frontend.ts index 79bc9a65ec8..aead5843308 100644 --- a/assets/js/blocks/mini-cart/frontend.ts +++ b/assets/js/blocks/mini-cart/frontend.ts @@ -14,6 +14,20 @@ interface dependencyData { translations?: string; } +function getClosestColor( + element: Element | null, + colorType: 'color' | 'backgroundColor' +): string | null { + if ( ! element ) { + return null; + } + const color = window.getComputedStyle( element )[ colorType ]; + if ( color !== 'rgba(0, 0, 0, 0)' && color !== 'transparent' ) { + return color; + } + return getClosestColor( element.parentElement, colorType ); +} + window.addEventListener( 'load', () => { const miniCartBlocks = document.querySelectorAll( '.wc-block-mini-cart' ); let wasLoadScriptsCalled = false; @@ -174,11 +188,25 @@ window.addEventListener( 'load', () => { */ const style = document.createElement( 'style' ); const backgroundColor = getComputedStyle( document.body ).backgroundColor; + // For simplicity, we only consider the background color of the first Mini Cart button. + const firstMiniCartButton = document.querySelector( + '.wc-block-mini-cart__button' + ); + const badgeTextColor = firstMiniCartButton + ? getClosestColor( firstMiniCartButton, 'backgroundColor' ) + : 'inherit'; + const badgeBackgroundColor = firstMiniCartButton + ? getClosestColor( firstMiniCartButton, 'color' ) + : 'inherit'; style.appendChild( document.createTextNode( `:where(.wp-block-woocommerce-mini-cart-contents) { background-color: ${ backgroundColor }; + } + .wc-block-mini-cart__badge { + background-color: ${ badgeBackgroundColor }; + color: ${ badgeTextColor }; }` ) ); diff --git a/assets/js/blocks/mini-cart/quantity-badge/index.tsx b/assets/js/blocks/mini-cart/quantity-badge/index.tsx index 5f323f17daf..e69a50f80e7 100644 --- a/assets/js/blocks/mini-cart/quantity-badge/index.tsx +++ b/assets/js/blocks/mini-cart/quantity-badge/index.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import { miniCart, miniCartWithBadge } from '@woocommerce/icons'; +import { miniCart } from '@woocommerce/icons'; import { Icon } from '@wordpress/icons'; /** @@ -17,22 +17,14 @@ interface Props { const QuantityBadge = ( { count }: Props ): JSX.Element => { return ( - { count > 0 ? ( - <> - - { count } - - ) : ( - - ) } + + + { count > 0 ? count : '' } + ); }; diff --git a/assets/js/blocks/mini-cart/quantity-badge/style.scss b/assets/js/blocks/mini-cart/quantity-badge/style.scss index 42125190935..cadbf5740f5 100644 --- a/assets/js/blocks/mini-cart/quantity-badge/style.scss +++ b/assets/js/blocks/mini-cart/quantity-badge/style.scss @@ -6,11 +6,8 @@ .wc-block-mini-cart__badge { align-items: center; - background: transparent; - border: 0.15em solid; border-radius: 1em; box-sizing: border-box; - color: inherit; display: flex; font-size: 0.875em; font-weight: 600; @@ -22,8 +19,17 @@ padding: 0 em($gap-smallest); position: absolute; transform: translateY(-50%); + transition: all 0.15s; white-space: nowrap; z-index: 1; + + // These values will be overridden in JS. + background-color: transparent; + color: transparent; +} + +.wc-block-mini-cart__badge:empty { + opacity: 0; } .wc-block-mini-cart__icon { diff --git a/assets/js/icons/index.js b/assets/js/icons/index.js index 1abccd1ad4c..70acf6bdd56 100644 --- a/assets/js/icons/index.js +++ b/assets/js/icons/index.js @@ -12,7 +12,6 @@ export { default as totals } from './library/totals'; export { default as woo } from './library/woo'; export { default as miniCart } from './library/mini-cart'; export { default as miniCartAlt } from './library/mini-cart-alt'; -export { default as miniCartWithBadge } from './library/mini-cart-with-badge'; export { default as stacks } from './library/stacks'; export { default as Alert } from './library/alert'; export { default as customerAccount } from './library/customer-account'; diff --git a/assets/js/icons/library/mini-cart-with-badge.tsx b/assets/js/icons/library/mini-cart-with-badge.tsx deleted file mode 100644 index 4eb8b960d68..00000000000 --- a/assets/js/icons/library/mini-cart-with-badge.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/** - * External dependencies - */ -import { SVG } from '@wordpress/primitives'; - -const miniCartWithBadge = ( - - - - - -); - -export default miniCartWithBadge; diff --git a/src/BlockTypes/MiniCart.php b/src/BlockTypes/MiniCart.php index a6352aca3f0..f929ba6b6e7 100644 --- a/src/BlockTypes/MiniCart.php +++ b/src/BlockTypes/MiniCart.php @@ -409,40 +409,6 @@ protected function render( $attributes, $content, $block ) { return $content . $this->get_markup( $attributes ); } - /** - * Returns whether the cart is empty. If the cart can't be found, it returns - * true as well. - * - * @return boolean Whether the cart is empty. - */ - protected function is_cart_empty() { - $cart = $this->get_cart_instance(); - if ( $cart ) { - return $cart->get_cart_contents_count() === 0; - } - return true; - } - - /** - * Returns the cart icon, depending on whether the cart is empty or not. - * - * @return string Cart SVG icon. - */ - protected function get_mini_cart_icon() { - if ( $this->is_cart_empty() ) { - return ' - - - - '; - } - return ' - - - - '; - } - /** * Render the markup for the Mini Cart block. * @@ -483,20 +449,18 @@ protected function get_markup( $attributes ) { $cart_contents_count, wp_strip_all_tags( wc_price( $cart_contents_total ) ) ); - $icon = $this->get_mini_cart_icon(); + $icon = ' + + + + '; - if ( $this->is_cart_empty() ) { - $button_html = $this->get_cart_price_markup( $attributes ) . ' - - ' . $icon . ' - '; - } else { - $button_html = $this->get_cart_price_markup( $attributes ) . ' - - ' . $icon . ' - ' . $cart_contents_count . ' - '; - } + $cart_contents_count = $cart_contents_count > 0 ? $cart_contents_count : ''; + $button_html = $this->get_cart_price_markup( $attributes ) . ' + + ' . $icon . ' + ' . $cart_contents_count . ' + '; if ( is_cart() || is_checkout() ) { if ( $this->should_not_render_mini_cart( $attributes ) ) { From 1ddb6d4859d17c925ce07dd53c2227b90ebe7fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 9 May 2023 08:59:13 +0200 Subject: [PATCH 3/5] Add tests --- assets/js/blocks/mini-cart/test/block.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/assets/js/blocks/mini-cart/test/block.js b/assets/js/blocks/mini-cart/test/block.js index 03483a03aa4..8ad302f12a7 100644 --- a/assets/js/blocks/mini-cart/test/block.js +++ b/assets/js/blocks/mini-cart/test/block.js @@ -65,6 +65,24 @@ describe( 'Testing Mini Cart', () => { fetchMock.resetMocks(); } ); + it( 'shows Mini Cart count badge when there are items in the cart', async () => { + render( ); + await waitFor( () => expect( fetchMock ).toHaveBeenCalled() ); + + await waitFor( () => + expect( screen.getByText( '3' ) ).toBeInTheDocument() + ); + } ); + + it( "doesn't show Mini Cart count badge when cart is empty", async () => { + mockEmptyCart(); + render( ); + await waitFor( () => expect( fetchMock ).toHaveBeenCalled() ); + const badgeWith0Count = screen.queryByText( '0' ); + + expect( badgeWith0Count ).toBeNull(); + } ); + it( 'opens Mini Cart drawer when clicking on button', async () => { render( ); await waitFor( () => expect( fetchMock ).toHaveBeenCalled() ); From a6b6303e4472beb1a60e07d4f7933e45cfb1d0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 9 May 2023 09:20:32 +0200 Subject: [PATCH 4/5] Make sure colors don't break existing themes --- assets/js/blocks/mini-cart/frontend.ts | 7 ++++--- assets/js/blocks/mini-cart/quantity-badge/style.scss | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/assets/js/blocks/mini-cart/frontend.ts b/assets/js/blocks/mini-cart/frontend.ts index aead5843308..ee0e0c82c44 100644 --- a/assets/js/blocks/mini-cart/frontend.ts +++ b/assets/js/blocks/mini-cart/frontend.ts @@ -180,8 +180,7 @@ window.addEventListener( 'load', () => { /** * Get the background color of the body then set it as the background color - * of the Mini Cart Contents block. We use :where here to make customized - * background color alway have higher priority. + * of the Mini Cart Contents block. * * We only set the background color, instead of the whole background. As * we only provide the option to customize the background color. @@ -199,12 +198,14 @@ window.addEventListener( 'load', () => { ? getClosestColor( firstMiniCartButton, 'color' ) : 'inherit'; + // We use :where here to reduce specificity so customized colors and theme + // CSS take priority. style.appendChild( document.createTextNode( `:where(.wp-block-woocommerce-mini-cart-contents) { background-color: ${ backgroundColor }; } - .wc-block-mini-cart__badge { + :where(.wc-block-mini-cart__badge) { background-color: ${ badgeBackgroundColor }; color: ${ badgeTextColor }; }` diff --git a/assets/js/blocks/mini-cart/quantity-badge/style.scss b/assets/js/blocks/mini-cart/quantity-badge/style.scss index cadbf5740f5..35203487961 100644 --- a/assets/js/blocks/mini-cart/quantity-badge/style.scss +++ b/assets/js/blocks/mini-cart/quantity-badge/style.scss @@ -22,7 +22,9 @@ transition: all 0.15s; white-space: nowrap; z-index: 1; +} +:where(.wc-block-mini-cart__badge) { // These values will be overridden in JS. background-color: transparent; color: transparent; From 585fedb208f170db205adc069d8799d10cd2675d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Thu, 11 May 2023 16:35:47 +0200 Subject: [PATCH 5/5] Update Mini Cart e2e test --- tests/e2e/specs/shopper/mini-cart.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/specs/shopper/mini-cart.test.js b/tests/e2e/specs/shopper/mini-cart.test.js index e651a100f1c..a8da7b626a3 100644 --- a/tests/e2e/specs/shopper/mini-cart.test.js +++ b/tests/e2e/specs/shopper/mini-cart.test.js @@ -109,7 +109,7 @@ describe( 'Shopper → Mini-Cart', () => { } ); await expect( page ).toMatchElement( '.wc-block-mini-cart__badge', { - text: '0', + text: '', } ); } ); } );