diff --git a/assets/js/blocks/cart-checkout/mini-cart/block.tsx b/assets/js/blocks/cart-checkout/mini-cart/block.tsx index eb754c23bd1..31086d5eba8 100644 --- a/assets/js/blocks/cart-checkout/mini-cart/block.tsx +++ b/assets/js/blocks/cart-checkout/mini-cart/block.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import classNames from 'classnames'; +import classnames from 'classnames'; import { __, _n, sprintf } from '@wordpress/i18n'; import { useState, useEffect, useRef } from '@wordpress/element'; import { @@ -31,10 +31,6 @@ import CartLineItemsTable from '../cart/cart-line-items-table'; import QuantityBadge from './quantity-badge'; import './style.scss'; -interface MiniCartBlockProps { - isInitiallyOpen?: boolean; -} - const PaymentMethodIconsElement = (): JSX.Element => { const { paymentMethods } = usePaymentMethods(); return ( @@ -44,9 +40,18 @@ const PaymentMethodIconsElement = (): JSX.Element => { ); }; +interface Props { + isInitiallyOpen?: boolean; + transparentButton: boolean; + colorClassNames?: string; + style?: Record< string, Record< string, string > >; +} + const MiniCartBlock = ( { isInitiallyOpen = false, -}: MiniCartBlockProps ): JSX.Element => { + colorClassNames, + style, +}: Props ): JSX.Element => { const { cartItems, cartItemsCount, @@ -115,6 +120,11 @@ const MiniCartBlock = ( { formatPrice( subTotal, getCurrencyFromPriceResponse( cartTotals ) ) ); + const colorStyle = { + backgroundColor: style?.color?.background, + color: style?.color?.text, + }; + const contents = ! cartIsLoading && cartItems.length === 0 ? (
{ renderFrontend( { selector: '.wc-block-mini-cart', Block: MiniCartBlock, - getProps: ( el: HTMLElement ) => ( { - isDataOutdated: el.dataset.isDataOutdated, - isInitiallyOpen: el.dataset.isInitiallyOpen === 'true', - } ), + getProps: ( el: HTMLElement ) => { + let colorClassNames = ''; + const button = el.querySelector( '.wc-block-mini-cart__button' ); + if ( button !== null ) { + colorClassNames = button.classList + .toString() + .replace( 'wc-block-mini-cart__button', '' ); + } + return { + isDataOutdated: el.dataset.isDataOutdated, + isInitiallyOpen: el.dataset.isInitiallyOpen === 'true', + colorClassNames, + style: el.dataset.style ? JSON.parse( el.dataset.style ) : {}, + }; + }, } ); // Refocus previously focused button if drawer is not open. diff --git a/assets/js/blocks/cart-checkout/mini-cart/edit.tsx b/assets/js/blocks/cart-checkout/mini-cart/edit.tsx index eeaffcf8866..46bc4b56ec3 100644 --- a/assets/js/blocks/cart-checkout/mini-cart/edit.tsx +++ b/assets/js/blocks/cart-checkout/mini-cart/edit.tsx @@ -1,19 +1,65 @@ /** * External dependencies */ -import { useBlockProps } from '@wordpress/block-editor'; +import { + InspectorControls, + useBlockProps, + getColorClassName, +} from '@wordpress/block-editor'; import type { ReactElement } from 'react'; import { formatPrice } from '@woocommerce/price-format'; import { CartCheckoutCompatibilityNotice } from '@woocommerce/editor-components/compatibility-notices'; +import { PanelBody, ToggleControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import classnames from 'classnames'; /** * Internal dependencies */ import QuantityBadge from './quantity-badge'; -const MiniCartBlock = (): ReactElement => { +interface Attributes { + isInitiallyOpen?: boolean; + transparentButton: boolean; + backgroundColor?: string; + textColor?: string; + style?: Record< string, Record< string, string > >; +} + +interface Props { + attributes: Attributes; + setAttributes: ( attributes: Record< string, unknown > ) => void; +} + +const MiniCartBlock = ( { + attributes, + setAttributes, +}: Props ): ReactElement => { + const { transparentButton, backgroundColor, textColor, style } = attributes; const blockProps = useBlockProps( { - className: 'wc-block-mini-cart', + className: classnames( 'wc-block-mini-cart', { + 'is-transparent': transparentButton, + } ), + } ); + + /** + * @todo Replace `getColorClassName` and manual style manipulation with + * `useColorProps` once the hook is no longer experimental. + */ + const backgroundClass = getColorClassName( + 'background-color', + backgroundColor + ); + const textColorClass = getColorClassName( 'color', textColor ); + + const colorStyle = { + backgroundColor: style?.color?.background, + color: style?.color?.text, + }; + + const colorClassNames = classnames( backgroundClass, textColorClass, { + 'has-background': backgroundClass || style?.color?.background, + 'has-text-color': textColorClass || style?.color?.text, } ); const productCount = 0; @@ -21,11 +67,42 @@ const MiniCartBlock = (): ReactElement => { return (
-
diff --git a/assets/js/blocks/cart-checkout/mini-cart/index.tsx b/assets/js/blocks/cart-checkout/mini-cart/index.tsx index b07203f29f0..c452add3db6 100644 --- a/assets/js/blocks/cart-checkout/mini-cart/index.tsx +++ b/assets/js/blocks/cart-checkout/mini-cart/index.tsx @@ -26,6 +26,19 @@ const settings = { supports: { html: false, multiple: false, + color: { + /** + * Because we don't target the wrapper element, we don't need + * to add color classes and style to the wrapper. + */ + __experimentalSkipSerialization: true, + }, + /** + * We need this experimental flag because we don't want to style the + * wrapper but inner elements. + */ + __experimentalSelector: + '.wc-block-mini-cart__button, .wc-block-mini-cart__badge', }, example: { attributes: { @@ -38,6 +51,10 @@ const settings = { default: false, save: false, }, + transparentButton: { + type: 'boolean', + default: true, + }, }, edit, diff --git a/assets/js/blocks/cart-checkout/mini-cart/quantity-badge/index.tsx b/assets/js/blocks/cart-checkout/mini-cart/quantity-badge/index.tsx index 4cd1825fea6..ac7bfc65c27 100644 --- a/assets/js/blocks/cart-checkout/mini-cart/quantity-badge/index.tsx +++ b/assets/js/blocks/cart-checkout/mini-cart/quantity-badge/index.tsx @@ -8,15 +8,32 @@ import { Icon, miniCart } from '@woocommerce/icons'; */ import './style.scss'; -const QuantityBadge = ( { count }: { count: number } ): JSX.Element => ( - - - { count } - -); +interface Props { + count: number; + colorClassNames?: string; + style?: Record< string, string | undefined >; +} + +const QuantityBadge = ( { + count, + colorClassNames, + style, +}: Props ): JSX.Element => { + return ( + + + + { count } + + + ); +}; export default QuantityBadge; diff --git a/assets/js/blocks/cart-checkout/mini-cart/quantity-badge/style.scss b/assets/js/blocks/cart-checkout/mini-cart/quantity-badge/style.scss index 19654e0ea35..eea3915ad0e 100644 --- a/assets/js/blocks/cart-checkout/mini-cart/quantity-badge/style.scss +++ b/assets/js/blocks/cart-checkout/mini-cart/quantity-badge/style.scss @@ -6,9 +6,8 @@ .wc-block-mini-cart__badge { align-items: center; background: #fff; - border: 2px solid; + border: 0.15em solid; border-radius: 1em; - box-shadow: 0 0 0 2px #fff; box-sizing: border-box; color: #000; display: flex; diff --git a/assets/js/blocks/cart-checkout/mini-cart/style.scss b/assets/js/blocks/cart-checkout/mini-cart/style.scss index 1bcb9c01273..e183ce70b0f 100644 --- a/assets/js/blocks/cart-checkout/mini-cart/style.scss +++ b/assets/js/blocks/cart-checkout/mini-cart/style.scss @@ -1,18 +1,22 @@ -.wc-block-mini-cart { +.wc-block-mini-cart.wp-block-woocommerce-mini-cart { + background-color: transparent !important; display: flex; justify-content: flex-end; + + &.is-transparent .wc-block-mini-cart__button { + background-color: transparent !important; + } } .wc-block-mini-cart__button { align-items: center; - background: transparent; border: none; + color: inherit; display: flex; font-weight: 400; padding: em($gap-small) em($gap-smaller); &:hover { - background: transparent; opacity: 0.6; } } @@ -38,6 +42,7 @@ font-size: 1rem; .components-modal__content { + box-sizing: border-box; display: flex; flex-direction: column; height: 100%; diff --git a/src/BlockTypes/MiniCart.php b/src/BlockTypes/MiniCart.php index 9794bc9b4f5..6a3b4d3a9f0 100644 --- a/src/BlockTypes/MiniCart.php +++ b/src/BlockTypes/MiniCart.php @@ -198,15 +198,17 @@ protected function append_script_and_deps_src( $script ) { * @return string Rendered block type output. */ protected function render( $attributes, $content ) { - return $content . $this->get_markup(); + return $content . $this->get_markup( $attributes ); } /** * Render the markup for the Mini Cart block. * + * @param array $attributes Block attributes. + * * @return string The HTML markup. */ - protected function get_markup() { + protected function get_markup( $attributes ) { if ( is_admin() || WC()->is_rest_api_request() ) { // In the editor we will display the placeholder, so no need to load // real cart data and to print the markup. @@ -222,6 +224,39 @@ protected function get_markup() { $cart_contents_total += $cart->get_subtotal_tax(); } + $wrapper_classes = 'wc-block-mini-cart'; + $classes = ''; + $style = ''; + + if ( ! isset( $attributes['transparentButton'] ) || $attributes['transparentButton'] ) { + $wrapper_classes .= ' is-transparent'; + } + + /** + * Get the color class and inline style. + * + * @todo refactor the logic of color class and style using StyleAttributesUtils. + */ + if ( ! empty( $attributes['textColor'] ) ) { + $classes .= sprintf( + ' has-%s-color has-text-color', + esc_attr( $attributes['textColor'] ) + ); + } elseif ( ! empty( $attributes['style']['color']['text'] ) ) { + $style .= 'color: ' . esc_attr( $attributes['style']['color']['text'] ) . ';'; + $classes .= ' has-text-color'; + } + + if ( ! empty( $attributes['backgroundColor'] ) ) { + $classes .= sprintf( + ' has-%s-background-color has-background', + esc_attr( $attributes['backgroundColor'] ) + ); + } elseif ( ! empty( $attributes['style']['color']['background'] ) ) { + $style .= 'background-color: ' . esc_attr( $attributes['style']['color']['background'] ) . ';'; + $classes .= ' has-background'; + } + $aria_label = sprintf( /* translators: %1$d is the number of products in the cart. %2$s is the cart total */ _n( @@ -258,17 +293,17 @@ protected function get_markup() { $button_html = '' . esc_html( wp_strip_all_tags( wc_price( $cart_contents_total ) ) ) . ' ' . $icon . ' - ' . $cart_contents_count . ' + ' . $cart_contents_count . ' '; if ( is_cart() || is_checkout() ) { - return '
- + return '
+
'; } - return '
- + return '
+