Add global style support to Mini Cart (button) (https://github.com/woocommerce/woocommerce-blocks/pull/5100)

This commit is contained in:
Tung Du 2021-11-17 22:39:07 +07:00 committed by GitHub
parent af8f5e857c
commit 2b7d906133
8 changed files with 234 additions and 40 deletions

View File

@ -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 ? (
<div
@ -176,7 +186,8 @@ const MiniCartBlock = ( {
return (
<>
<button
className="wc-block-mini-cart__button"
className={ `wc-block-mini-cart__button ${ colorClassNames }` }
style={ colorStyle }
onClick={ () => {
if ( ! isOpen ) {
setIsOpen( true );
@ -191,10 +202,14 @@ const MiniCartBlock = ( {
getCurrencyFromPriceResponse( cartTotals )
) }
</span>
<QuantityBadge count={ cartItemsCount } />
<QuantityBadge
count={ cartItemsCount }
colorClassNames={ colorClassNames }
style={ colorStyle }
/>
</button>
<Drawer
className={ classNames(
className={ classnames(
'wc-block-mini-cart__drawer',
'is-mobile',
{

View File

@ -28,10 +28,21 @@ const renderMiniCartFrontend = () => {
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.

View File

@ -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 (
<div { ...blockProps }>
<button className="wc-block-mini-cart__button">
<InspectorControls>
<PanelBody
title={ __(
'Button style',
'woo-gutenberg-products-block'
) }
>
<ToggleControl
label={ __(
'Use transparent button',
'woo-gutenberg-products-block'
) }
checked={ transparentButton }
onChange={ () =>
setAttributes( {
transparentButton: ! transparentButton,
} )
}
/>
</PanelBody>
</InspectorControls>
<button
className={ classnames(
'wc-block-mini-cart__button',
colorClassNames
) }
style={ colorStyle }
>
<span className="wc-block-mini-cart__amount">
{ formatPrice( productTotal ) }
</span>
<QuantityBadge count={ productCount } />
<QuantityBadge
count={ productCount }
colorClassNames={ colorClassNames }
style={ colorStyle }
/>
</button>
<CartCheckoutCompatibilityNotice blockName="mini-cart" />
</div>

View File

@ -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,

View File

@ -8,15 +8,32 @@ import { Icon, miniCart } from '@woocommerce/icons';
*/
import './style.scss';
const QuantityBadge = ( { count }: { count: number } ): JSX.Element => (
<span className="wc-block-mini-cart__quantity-badge">
<Icon
className="wc-block-mini-cart__icon"
size={ 20 }
srcElement={ miniCart }
/>
<span className="wc-block-mini-cart__badge">{ count }</span>
</span>
);
interface Props {
count: number;
colorClassNames?: string;
style?: Record< string, string | undefined >;
}
const QuantityBadge = ( {
count,
colorClassNames,
style,
}: Props ): JSX.Element => {
return (
<span className="wc-block-mini-cart__quantity-badge">
<Icon
className="wc-block-mini-cart__icon"
size={ 20 }
srcElement={ miniCart }
/>
<span
className={ `wc-block-mini-cart__badge ${ colorClassNames }` }
style={ style }
>
{ count }
</span>
</span>
);
};
export default QuantityBadge;

View File

@ -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;

View File

@ -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%;

View File

@ -198,15 +198,17 @@ class MiniCart extends AbstractBlock {
* @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 @@ class MiniCart extends AbstractBlock {
$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 @@ class MiniCart extends AbstractBlock {
$button_html = '<span class="wc-block-mini-cart__amount">' . esc_html( wp_strip_all_tags( wc_price( $cart_contents_total ) ) ) . '</span>
<span class="wc-block-mini-cart__quantity-badge">
' . $icon . '
<span class="wc-block-mini-cart__badge">' . $cart_contents_count . '</span>
<span class="wc-block-mini-cart__badge ' . $classes . '" style="' . $style . '">' . $cart_contents_count . '</span>
</span>';
if ( is_cart() || is_checkout() ) {
return '<div class="wc-block-mini-cart">
<button class="wc-block-mini-cart__button" aria-label="' . esc_attr( $aria_label ) . '" disabled>' . $button_html . '</button>
return '<div class="' . $wrapper_classes . '">
<button class="wc-block-mini-cart__button ' . $classes . '" aria-label="' . esc_attr( $aria_label ) . '" style="' . $style . '" disabled>' . $button_html . '</button>
</div>';
}
return '<div class="wc-block-mini-cart">
<button class="wc-block-mini-cart__button" aria-label="' . esc_attr( $aria_label ) . '">' . $button_html . '</button>
return '<div class="' . $wrapper_classes . '">
<button class="wc-block-mini-cart__button ' . $classes . '" aria-label="' . esc_attr( $aria_label ) . '" style="' . $style . '">' . $button_html . '</button>
<div class="wc-block-mini-cart__drawer is-loading is-mobile wc-block-components-drawer__screen-overlay wc-block-components-drawer__screen-overlay--is-hidden" aria-hidden="true">
<div class="components-modal__frame wc-block-components-drawer">
<div class="components-modal__content">
@ -335,4 +370,22 @@ class MiniCart extends AbstractBlock {
</td>
</tr>';
}
/**
* Get the supports array for this block type.
*
* @see $this->register_block_type()
* @return string;
*/
protected function get_block_type_supports() {
return array_merge(
parent::get_block_type_supports(),
array(
'html' => false,
'multiple' => false,
'color' => true,
'__experimentalSelector' => '.wc-block-mini-cart__button, .wc-block-mini-cart__badge',
)
);
}
}