Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Add global style support to Mini Cart (button) #5100

Merged
merged 16 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions assets/js/blocks/cart-checkout/mini-cart/block.tsx
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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 (
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 );
Expand All @@ -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',
{
Expand Down
19 changes: 15 additions & 4 deletions assets/js/blocks/cart-checkout/mini-cart/component-frontend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
87 changes: 82 additions & 5 deletions assets/js/blocks/cart-checkout/mini-cart/edit.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,108 @@
/**
* 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(
dinhtungdu marked this conversation as resolved.
Show resolved Hide resolved
'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;
const productTotal = 0;

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>
Expand Down
17 changes: 17 additions & 0 deletions assets/js/blocks/cart-checkout/mini-cart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Comment on lines +40 to +41
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything seemed to work fine for me when I removed these lines. Why was it needed? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you remove this, the global style doesn't work correctly. Without this, Gutenberg will add global style to .wp-block-woocommerce-mini-cart instead of the above selectors. This is fine for text color (by inheriting the parent element) but doesn't work for background color.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the GB team aware that we're using this experimental supports property and why we need to use it? If not, it might be handy to make sure they know somehow so there's awareness of it's usefulness for other projects when it comes time to evaluate graduating it.

Copy link
Member Author

@dinhtungdu dinhtungdu Nov 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nerrad I'm not sure if they know about it or not. I will add comments explaining why we need them.

Edit: added comments for __experimentalSelector and __experimentalSkipSerialization.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the discussion in p1637141712378200-slack-C02FL3X7KR6, I think we can go ahead using those __experimental features for now, given that the Mini Cart block is gated to the Feature Plugin.

In the future, while we research global styles in a bigger scale for all blocks, we can get a list of all experimental features we need to use and get in contact with Gutenberg devs to know their plans with them.

},
example: {
attributes: {
Expand All @@ -38,6 +51,10 @@ const settings = {
default: false,
save: false,
},
transparentButton: {
type: 'boolean',
default: true,
},
},

edit,
Expand Down
37 changes: 27 additions & 10 deletions assets/js/blocks/cart-checkout/mini-cart/quantity-badge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 8 additions & 3 deletions assets/js/blocks/cart-checkout/mini-cart/style.scss
Original file line number Diff line number Diff line change
@@ -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;
}
}
Expand All @@ -38,6 +42,7 @@
font-size: 1rem;

.components-modal__content {
box-sizing: border-box;
display: flex;
flex-direction: column;
height: 100%;
Expand Down
Loading