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 7 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
38 changes: 29 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 All @@ -23,18 +23,16 @@ import PaymentMethodIcons from '@woocommerce/base-components/cart-checkout/payme
import { CART_URL, CHECKOUT_URL } from '@woocommerce/block-settings';
import Button from '@woocommerce/base-components/button';
import { PaymentMethodDataProvider } from '@woocommerce/base-context';
import { getColorClassName } from '@wordpress/block-editor';

/**
* Internal dependencies
*/
import CartLineItemsTable from '../cart/cart-line-items-table';
import QuantityBadge from './quantity-badge';
import { Attributes } from './types';
import './style.scss';

interface MiniCartBlockProps {
isInitiallyOpen?: boolean;
}

const PaymentMethodIconsElement = (): JSX.Element => {
const { paymentMethods } = usePaymentMethods();
return (
Expand All @@ -46,7 +44,10 @@ const PaymentMethodIconsElement = (): JSX.Element => {

const MiniCartBlock = ( {
isInitiallyOpen = false,
}: MiniCartBlockProps ): JSX.Element => {
backgroundColor,
textColor,
style,
}: Attributes ): JSX.Element => {
const {
cartItems,
cartItemsCount,
Expand Down Expand Up @@ -173,10 +174,23 @@ const MiniCartBlock = ( {
</>
);

const backgroundClass = getColorClassName(
'background-color',
backgroundColor
);
const textColorClass = getColorClassName( 'color', textColor );

return (
<>
<button
className="wc-block-mini-cart__button"
className={ classnames( 'wc-block-mini-cart__button', {
[ backgroundClass ]: backgroundClass,
[ textColorClass ]: textColorClass,
} ) }
style={ {
backgroundColor: style?.color?.background,
color: style?.color?.text,
} }
onClick={ () => {
if ( ! isOpen ) {
setIsOpen( true );
Expand All @@ -191,10 +205,16 @@ const MiniCartBlock = ( {
getCurrencyFromPriceResponse( cartTotals )
) }
</span>
<QuantityBadge count={ cartItemsCount } />
<QuantityBadge
count={ cartItemsCount }
backgroundColor={
backgroundColor || style?.color?.background
}
textColor={ textColor || style?.color?.text }
/>
</button>
<Drawer
className={ classNames(
className={ classnames(
'wc-block-mini-cart__drawer',
'is-mobile',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const renderMiniCartFrontend = () => {
getProps: ( el: HTMLElement ) => ( {
isDataOutdated: el.dataset.isDataOutdated,
isInitiallyOpen: el.dataset.isInitiallyOpen === 'true',
backgroundColor: el.dataset.backgroundColor,
Copy link
Contributor

Choose a reason for hiding this comment

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

You will need to define textColor here too, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Taking another look at this... I wonder if we could read the class names from the elements instead of passing the colors in data attributes. I'm thinking something along these lines:

quantityBadgeClassnames: el.querySelector('.wc-block-mini-cart__quantity-badge').classList

Then, we would just pass that array to the React component so it can apply those classes. This way, we wouldn't need to call getColorClassName() from the JS code, which allows us to avoid one dependency: @wordpress/block-editor. 🙂

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks so much, it's a great idea!

Copy link
Member Author

@dinhtungdu dinhtungdu Nov 12, 2021

Choose a reason for hiding this comment

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

I update the related components to follow this approach.

style: el.dataset.style ? JSON.parse( el.dataset.style ) : {},
} ),
} );

Expand Down
73 changes: 68 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,94 @@
/**
* 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';
import { Attributes } from './types';
export interface Props {
attributes: Attributes;
setAttributes: ( attributes: Record< string, unknown > ) => void;
}

const MiniCartBlock = (): ReactElement => {
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 wp-block-woocommerce-mini-cart',
dinhtungdu marked this conversation as resolved.
Show resolved Hide resolved
{
'transparent-button': transparentButton,
dinhtungdu marked this conversation as resolved.
Show resolved Hide resolved
}
),
} );

const backgroundClass = getColorClassName(
dinhtungdu marked this conversation as resolved.
Show resolved Hide resolved
'background-color',
backgroundColor
);
const textColorClass = getColorClassName( 'color', textColor );

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', {
[ backgroundClass ]: backgroundClass,
[ textColorClass ]: textColorClass,
} ) }
dinhtungdu marked this conversation as resolved.
Show resolved Hide resolved
style={ {
backgroundColor: style?.color?.background,
color: style?.color?.text,
} }
>
<span className="wc-block-mini-cart__amount">
{ formatPrice( productTotal ) }
</span>
<QuantityBadge count={ productCount } />
<QuantityBadge
count={ productCount }
backgroundColor={
backgroundColor || style?.color?.background
}
textColor={ textColor || style?.color?.text }
/>
</button>
<CartCheckoutCompatibilityNotice blockName="mini-cart" />
</div>
Expand Down
3 changes: 3 additions & 0 deletions assets/js/blocks/cart-checkout/mini-cart/editor.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.editor-styles-wrapper .wp-block-woocommerce-mini-cart.wc-block-mini-cart {
background-color: transparent !important;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this 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.

Oh, I'm sorry, this is from the previous implementation. Thanks for pointing this out!

Copy link
Member Author

Choose a reason for hiding this comment

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

Update: We still need it. The preset style classes in the editor come with !important that override our button style.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, right, I could reproduce the issue. However, I think that might be a sign that there is something else that we are not doing correctly here. I took a look at the Gutenberg Table block as an example: it also has to apply the background to an inner element (<table>) instead to the wrapper (<figure>); but they don't have this issue.

After some investigation, I think the difference is that they use __experimentalSkipSerialization. Do you think we could use it too?

Copy link
Member Author

Choose a reason for hiding this comment

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

@Aljullu I added __experimentalSkipSerialization in 27a2584 (#5100)

7 changes: 7 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,9 @@ const settings = {
supports: {
html: false,
multiple: false,
color: true,
__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 +41,10 @@ const settings = {
default: false,
save: false,
},
transparentButton: {
type: 'boolean',
default: true,
},
},

edit,
Expand Down
63 changes: 53 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 @@ -2,21 +2,64 @@
* External dependencies
*/
import { Icon, miniCart } from '@woocommerce/icons';
import classnames from 'classnames';
import { getColorClassName } from '@wordpress/block-editor';

/**
* Internal dependencies
*/
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;
backgroundColor?: string;
textColor?: string;
}

const isColorValue = ( color?: string ) => {
if ( ! color ) return false;
return (
color.startsWith( 'rgb(' ) ||
color.startsWith( 'rgba(' ) ||
color.startsWith( '#' ) ||
color.startsWith( 'hsl(' )
);
};

const QuantityBadge = ( {
count,
backgroundColor,
textColor,
}: Props ): JSX.Element => {
const colorClass = getColorClassName( 'color', textColor );
const backgroundClass = getColorClassName(
'background-color',
backgroundColor
);
return (
<span className="wc-block-mini-cart__quantity-badge">
<Icon
className="wc-block-mini-cart__icon"
size={ 20 }
srcElement={ miniCart }
/>
<span
className={ classnames( 'wc-block-mini-cart__badge', {
[ colorClass ]: colorClass,
[ backgroundClass ]: backgroundClass,
'has-background': backgroundClass,
} ) }
style={ {
backgroundColor: isColorValue( backgroundColor )
? backgroundColor
: undefined,
color: isColorValue( textColor ) ? textColor : undefined,
} }
>
{ 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
10 changes: 9 additions & 1 deletion assets/js/blocks/cart-checkout/mini-cart/style.scss
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
.wc-block-mini-cart {
.wc-block-mini-cart.wp-block-woocommerce-mini-cart {
background-color: transparent !important;
display: flex;
justify-content: flex-end;

&.transparent-button .wc-block-mini-cart__button {
background: transparent !important;
dinhtungdu marked this conversation as resolved.
Show resolved Hide resolved
}
dinhtungdu marked this conversation as resolved.
Show resolved Hide resolved
}

.wc-block-mini-cart__button {
align-items: center;
background: transparent;
border: none;
color: inherit;
cursor: pointer;
dinhtungdu marked this conversation as resolved.
Show resolved Hide resolved
display: flex;
font-weight: 400;
padding: em($gap-small) em($gap-smaller);
Expand Down Expand Up @@ -38,6 +45,7 @@
font-size: 1rem;

.components-modal__content {
box-sizing: border-box;
display: flex;
flex-direction: column;
height: 100%;
Expand Down
7 changes: 7 additions & 0 deletions assets/js/blocks/cart-checkout/mini-cart/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface Attributes {
isInitiallyOpen?: boolean;
transparentButton: boolean;
backgroundColor?: string;
textColor?: string;
style?: Record< string, Record< string, string > >;
}
Loading