* Create new car button inner block

* Create new checkout button inner block

* Render inner buttons on the frontend

* Fix buttons size

* Add outline to the cart button

* Add color props

* Add urls

* Fix button classes

* Add classes to edit

* Start using editable button

* Add buttons to mini cart tempalte

* Reorder buttons in template

* Add cart & checkout buttons scripts

* Remove unnecessary divs

* Change checkout button block name

* Change cart button block name

* Add deprecation to the buttons

* Fix lint and inline save

* Remove commented out code and fix condition

* Render buttons if no children

* Change migrate condition

* Simplify condition

Co-authored-by: Albert Juhé Lluveras <contact@albertjuhe.com>

* Make props optional

* Add missing import

---------

Co-authored-by: Albert Juhé Lluveras <contact@albertjuhe.com>
This commit is contained in:
Alba Rincón 2023-03-23 16:55:53 +01:00 committed by GitHub
parent c9a6391e20
commit 2d9519b3ba
25 changed files with 479 additions and 72 deletions

View File

@ -8,3 +8,5 @@ import './mini-cart-items-block';
import './mini-cart-products-table-block';
import './mini-cart-footer-block';
import './mini-cart-shopping-button-block';
import './mini-cart-cart-button-block';
import './mini-cart-checkout-button-block';

View File

@ -0,0 +1,11 @@
/**
* Internal dependencies
*/
import { defaultCartButtonLabel } from './constants';
export default {
cartButtonLabel: {
type: 'string',
default: defaultCartButtonLabel,
},
};

View File

@ -0,0 +1,30 @@
{
"name": "woocommerce/mini-cart-cart-button-block",
"version": "1.0.0",
"title": "Mini Cart View Cart Button",
"description": "Block that displays the cart button when the Mini Cart has products.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": true,
"color": {
"text": true,
"background": true
}
},
"attributes": {
"lock": {
"type": "object",
"default": {
"remove": false,
"move": false
}
}
},
"parent": [ "woocommerce/mini-cart-footer-block" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2
}

View File

@ -0,0 +1,47 @@
/**
* External dependencies
*/
import { CART_URL } from '@woocommerce/block-settings';
import Button from '@woocommerce/base-components/button';
import classNames from 'classnames';
import { useColorProps } from '@woocommerce/base-hooks';
/**
* Internal dependencies
*/
import { defaultCartButtonLabel } from './constants';
type MiniCartCartButtonBlockProps = {
cartButtonLabel?: string;
className?: string;
style?: string;
};
const Block = ( {
className,
cartButtonLabel,
style,
}: MiniCartCartButtonBlockProps ): JSX.Element | null => {
const colorProps = useColorProps( { style } );
if ( ! CART_URL ) {
return null;
}
return (
<Button
className={ classNames(
className,
colorProps.className,
'wc-block-mini-cart__footer-cart'
) }
style={ { ...colorProps.style } }
href={ CART_URL }
variant="outlined"
>
{ cartButtonLabel || defaultCartButtonLabel }
</Button>
);
};
export default Block;

View File

@ -0,0 +1,9 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
export const defaultCartButtonLabel = __(
'View my cart',
'woo-gutenberg-products-block'
);

View File

@ -0,0 +1,42 @@
/**
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
import EditableButton from '@woocommerce/editor-components/editable-button';
/**
* Internal dependencies
*/
import { defaultCartButtonLabel } from './constants';
export const Edit = ( {
attributes,
setAttributes,
}: {
attributes: {
cartButtonLabel: string;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const blockProps = useBlockProps();
const { cartButtonLabel } = attributes;
return (
<EditableButton
{ ...blockProps }
className="wc-block-mini-cart__footer-cart"
variant="outlined"
value={ cartButtonLabel }
placeholder={ defaultCartButtonLabel }
onChange={ ( content ) => {
setAttributes( {
cartButtonLabel: content,
} );
} }
/>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() }></div>;
};

View File

@ -0,0 +1,28 @@
/**
* External dependencies
*/
import { Icon, button } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore -- TypeScript expects some required properties which we already
// registered in PHP.
registerBlockType( 'woocommerce/mini-cart-cart-button-block', {
icon: {
src: (
<Icon
icon={ button }
className="wc-block-editor-components-block-icon"
/>
),
},
attributes,
edit: Edit,
save: Save,
} );

View File

@ -0,0 +1,11 @@
/**
* Internal dependencies
*/
import { defaultCheckoutButtonLabel } from './constants';
export default {
checkoutButtonLabel: {
type: 'string',
default: defaultCheckoutButtonLabel,
},
};

View File

@ -0,0 +1,30 @@
{
"name": "woocommerce/mini-cart-checkout-button-block",
"version": "1.0.0",
"title": "Mini Cart Proceed to Checkout Button",
"description": "Block that displays the checkout button when the Mini Cart has products.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": true,
"color": {
"text": true,
"background": true
}
},
"attributes": {
"lock": {
"type": "object",
"default": {
"remove": false,
"move": false
}
}
},
"parent": [ "woocommerce/mini-cart-footer-block" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2
}

View File

@ -0,0 +1,46 @@
/**
* External dependencies
*/
import { CHECKOUT_URL } from '@woocommerce/block-settings';
import Button from '@woocommerce/base-components/button';
import classNames from 'classnames';
import { useColorProps } from '@woocommerce/base-hooks';
/**
* Internal dependencies
*/
import { defaultCheckoutButtonLabel } from './constants';
type MiniCartCheckoutButtonBlockProps = {
checkoutButtonLabel?: string;
className?: string;
style?: string;
};
const Block = ( {
className,
checkoutButtonLabel,
style,
}: MiniCartCheckoutButtonBlockProps ): JSX.Element | null => {
const colorProps = useColorProps( { style } );
if ( ! CHECKOUT_URL ) {
return null;
}
return (
<Button
className={ classNames(
className,
colorProps.className,
'wc-block-mini-cart__footer-checkout'
) }
style={ { ...colorProps.style } }
href={ CHECKOUT_URL }
>
{ checkoutButtonLabel || defaultCheckoutButtonLabel }
</Button>
);
};
export default Block;

View File

@ -0,0 +1,9 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
export const defaultCheckoutButtonLabel = __(
'Go to checkout',
'woo-gutenberg-products-block'
);

View File

@ -0,0 +1,41 @@
/**
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
import EditableButton from '@woocommerce/editor-components/editable-button';
/**
* Internal dependencies
*/
import { defaultCheckoutButtonLabel } from './constants';
export const Edit = ( {
attributes,
setAttributes,
}: {
attributes: {
checkoutButtonLabel: string;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const blockProps = useBlockProps();
const { checkoutButtonLabel } = attributes;
return (
<EditableButton
{ ...blockProps }
className="wc-block-mini-cart__footer-checkout"
value={ checkoutButtonLabel }
placeholder={ defaultCheckoutButtonLabel }
onChange={ ( content ) => {
setAttributes( {
checkoutButtonLabel: content,
} );
} }
/>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() }></div>;
};

View File

@ -0,0 +1,28 @@
/**
* External dependencies
*/
import { Icon, button } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore -- TypeScript expects some required properties which we already
// registered in PHP.
registerBlockType( 'woocommerce/mini-cart-checkout-button-block', {
icon: {
src: (
<Icon
icon={ button }
className="wc-block-editor-components-block-icon"
/>
),
},
attributes,
edit: Edit,
save: Save,
} );

View File

@ -11,18 +11,15 @@ import {
import PaymentMethodIcons from '@woocommerce/base-components/cart-checkout/payment-method-icons';
import { getIconsFromPaymentMethods } from '@woocommerce/base-utils';
import { getSetting } from '@woocommerce/settings';
import { CART_URL, CHECKOUT_URL } from '@woocommerce/block-settings';
import Button from '@woocommerce/base-components/button';
import { PaymentEventsProvider } from '@woocommerce/base-context';
import classNames from 'classnames';
import { isObject } from '@woocommerce/types';
/**
* Internal dependencies
*/
import {
defaultCartButtonLabel,
defaultCheckoutButtonLabel,
} from './constants';
import CartButton from '../mini-cart-cart-button-block/block';
import CheckoutButton from '../mini-cart-checkout-button-block/block';
const PaymentMethodIconsElement = (): JSX.Element => {
const { paymentMethods } = usePaymentMethods();
@ -34,12 +31,23 @@ const PaymentMethodIconsElement = (): JSX.Element => {
};
interface Props {
children: JSX.Element | JSX.Element[];
className?: string;
cartButtonLabel: string;
checkoutButtonLabel: string;
}
const hasChildren = ( children ): boolean => {
return children.some( ( child ) => {
if ( Array.isArray( child ) ) {
return hasChildren( child );
}
return isObject( child ) && child.key !== null;
} );
};
const Block = ( {
children,
className,
cartButtonLabel,
checkoutButtonLabel,
@ -50,6 +58,8 @@ const Block = ( {
parseInt( cartTotals.total_items_tax, 10 )
: parseInt( cartTotals.total_items, 10 );
const hasButtons = hasChildren( children );
return (
<div
className={ classNames( className, 'wc-block-mini-cart__footer' ) }
@ -65,22 +75,15 @@ const Block = ( {
) }
/>
<div className="wc-block-mini-cart__footer-actions">
{ CART_URL && (
<Button
className="wc-block-mini-cart__footer-cart"
href={ CART_URL }
variant="outlined"
>
{ cartButtonLabel || defaultCartButtonLabel }
</Button>
) }
{ CHECKOUT_URL && (
<Button
className="wc-block-mini-cart__footer-checkout"
href={ CHECKOUT_URL }
>
{ checkoutButtonLabel || defaultCheckoutButtonLabel }
</Button>
{ hasButtons ? (
children
) : (
<>
<CartButton cartButtonLabel={ cartButtonLabel } />
<CheckoutButton
checkoutButtonLabel={ checkoutButtonLabel }
/>
</>
) }
</div>
<PaymentEventsProvider>

View File

@ -3,8 +3,7 @@
*/
import { __ } from '@wordpress/i18n';
import { TotalsItem } from '@woocommerce/blocks-checkout';
import EditableButton from '@woocommerce/editor-components/editable-button';
import { useBlockProps } from '@wordpress/block-editor';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import {
usePaymentMethods,
@ -15,14 +14,6 @@ import { getIconsFromPaymentMethods } from '@woocommerce/base-utils';
import { getSetting } from '@woocommerce/settings';
import { PaymentEventsProvider } from '@woocommerce/base-context';
/**
* Internal dependencies
*/
import {
defaultCartButtonLabel,
defaultCheckoutButtonLabel,
} from './constants';
const PaymentMethodIconsElement = (): JSX.Element => {
const { paymentMethods } = usePaymentMethods();
return (
@ -32,24 +23,19 @@ const PaymentMethodIconsElement = (): JSX.Element => {
);
};
export const Edit = ( {
attributes,
setAttributes,
}: {
attributes: {
cartButtonLabel: string;
checkoutButtonLabel: string;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
export const Edit = (): JSX.Element => {
const blockProps = useBlockProps();
const { cartButtonLabel, checkoutButtonLabel } = attributes;
const { cartTotals } = useStoreCart();
const subTotal = getSetting( 'displayCartPricesIncludingTax', false )
? parseInt( cartTotals.total_items, 10 ) +
parseInt( cartTotals.total_items_tax, 10 )
: parseInt( cartTotals.total_items, 10 );
const TEMPLATE = [
[ 'woocommerce/mini-cart-cart-button-block', {} ],
[ 'woocommerce/mini-cart-checkout-button-block', {} ],
];
return (
<div { ...blockProps }>
<div className="wc-block-mini-cart__footer">
@ -63,29 +49,7 @@ export const Edit = ( {
'woo-gutenberg-products-block'
) }
/>
<div className="wc-block-mini-cart__footer-actions">
<EditableButton
className="wc-block-mini-cart__footer-cart"
variant="outlined"
value={ cartButtonLabel }
placeholder={ defaultCartButtonLabel }
onChange={ ( content ) => {
setAttributes( {
cartButtonLabel: content,
} );
} }
/>
<EditableButton
className="wc-block-mini-cart__footer-checkout"
value={ checkoutButtonLabel }
placeholder={ defaultCheckoutButtonLabel }
onChange={ ( content ) => {
setAttributes( {
checkoutButtonLabel: content,
} );
} }
/>
</div>
<InnerBlocks template={ TEMPLATE } />
<PaymentEventsProvider>
<PaymentMethodIconsElement />
</PaymentEventsProvider>
@ -95,5 +59,9 @@ export const Edit = ( {
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() }></div>;
return (
<div { ...useBlockProps.save() }>
<InnerBlocks.Content />
</div>
);
};

View File

@ -2,13 +2,14 @@
* External dependencies
*/
import { Icon, payment } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
import { createBlock, registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
import deprecatedAttributes from './attributes';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore -- TypeScript expects some required properties which we already
@ -22,7 +23,44 @@ registerBlockType( 'woocommerce/mini-cart-footer-block', {
/>
),
},
attributes,
deprecated: [
{
attributes: deprecatedAttributes,
migrate( attributes, innerBlocks ) {
const {
cartButtonLabel,
checkoutButtonLabel,
...restAttributes
} = attributes;
return [
restAttributes,
[
createBlock(
'woocommerce/mini-cart-cart-button-block',
{
cartButtonLabel,
}
),
createBlock(
'woocommerce/mini-cart-checkout-button-block',
{
checkoutButtonLabel,
}
),
...innerBlocks,
],
];
},
isEligible: ( attributes, innerBlocks ) => {
return ! innerBlocks.length;
},
save: (): JSX.Element => {
return <div { ...useBlockProps.save() }></div>;
},
},
],
edit: Edit,
save: Save,
} );

View File

@ -14,6 +14,8 @@ import miniCartProductsTableMetadata from './mini-cart-products-table-block/bloc
import miniCartFooterMetadata from './mini-cart-footer-block/block.json';
import miniCartItemsMetadata from './mini-cart-items-block/block.json';
import miniCartShoppingButtonMetadata from './mini-cart-shopping-button-block/block.json';
import miniCartCartButtonMetadata from './mini-cart-cart-button-block/block.json';
import miniCartCheckoutButtonMetadata from './mini-cart-checkout-button-block/block.json';
// Modify webpack publicPath at runtime based on location of WordPress Plugin.
// eslint-disable-next-line no-undef,camelcase
@ -89,3 +91,23 @@ registerCheckoutBlock( {
)
),
} );
registerCheckoutBlock( {
metadata: miniCartCartButtonMetadata,
component: lazy(
() =>
import(
/* webpackChunkName: "mini-cart-contents-block/cart-button" */ './mini-cart-cart-button-block/block'
)
),
} );
registerCheckoutBlock( {
metadata: miniCartCheckoutButtonMetadata,
component: lazy(
() =>
import(
/* webpackChunkName: "mini-cart-contents-block/checkout-button" */ './mini-cart-checkout-button-block/block'
)
),
} );

View File

@ -171,12 +171,15 @@ h2.wc-block-mini-cart__title {
}
}
.wc-block-mini-cart__footer-actions {
.wc-block-mini-cart__footer-actions,
.block-editor-block-list__layout {
display: flex;
gap: $gap;
.wc-block-components-button {
.wc-block-components-button,
.wp-block-button {
flex-grow: 1;
display: inline-flex;
}
.wc-block-components-button.outlined {

View File

@ -26,6 +26,7 @@ export enum innerBlockAreas {
EMPTY_MINI_CART = 'woocommerce/empty-mini-cart-contents-block',
FILLED_MINI_CART = 'woocommerce/filled-mini-cart-contents-block',
MINI_CART_ITEMS = 'woocommerce/mini-cart-items-block',
MINI_CART_FOOTER = 'woocommerce/mini-cart-footer-block',
CART_ORDER_SUMMARY = 'woocommerce/cart-order-summary-block',
CHECKOUT_ORDER_SUMMARY = 'woocommerce/checkout-order-summary-block',
}

View File

@ -280,6 +280,8 @@ class MiniCart extends AbstractBlock {
'items-frontend',
'footer-frontend',
'products-table-frontend',
'cart-button-frontend',
'checkout-button-frontend',
);
}
foreach ( $inner_blocks_frontend_scripts as $inner_block_frontend_script ) {

View File

@ -0,0 +1,14 @@
<?php
namespace Automattic\WooCommerce\Blocks\BlockTypes;
/**
* MiniCartCartButtonBlock class.
*/
class MiniCartCartButtonBlock extends AbstractInnerBlock {
/**
* Block name.
*
* @var string
*/
protected $block_name = 'mini-cart-cart-button-block';
}

View File

@ -0,0 +1,14 @@
<?php
namespace Automattic\WooCommerce\Blocks\BlockTypes;
/**
* MiniCartCheckoutButtonBlock class.
*/
class MiniCartCheckoutButtonBlock extends AbstractInnerBlock {
/**
* Block name.
*
* @var string
*/
protected $block_name = 'mini-cart-checkout-button-block';
}

View File

@ -152,6 +152,8 @@ class MiniCartContents extends AbstractBlock {
$block_types[] = 'MiniCartItemsBlock';
$block_types[] = 'MiniCartProductsTableBlock';
$block_types[] = 'MiniCartShoppingButtonBlock';
$block_types[] = 'MiniCartCartButtonBlock';
$block_types[] = 'MiniCartCheckoutButtonBlock';
$block_types[] = 'MiniCartTitleBlock';
return $block_types;

View File

@ -16,6 +16,12 @@
<!-- /wp:woocommerce/mini-cart-items-block -->
<!-- wp:woocommerce/mini-cart-footer-block -->
<div class="wp-block-woocommerce-mini-cart-footer-block">
<!-- wp:woocommerce/mini-cart-cart-button-block -->
<div class="wp-block-woocommerce-mini-cart-cart-button-block"></div>
<!-- /wp:woocommerce/mini-cart-cart-button-block -->
<!-- wp:woocommerce/mini-cart-checkout-button-block -->
<div class="wp-block-woocommerce-mini-cart-checkout-button-block"></div>
<!-- /wp:woocommerce/mini-cart-checkout-button-block -->
</div>
<!-- /wp:woocommerce/mini-cart-footer-block -->
</div>