diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/index.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/index.tsx index 4f7505b873e..bea902f0c8e 100644 --- a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/index.tsx +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/index.tsx @@ -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'; diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/attributes.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/attributes.tsx new file mode 100644 index 00000000000..0e2e88c3f1d --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/attributes.tsx @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import { defaultCartButtonLabel } from './constants'; + +export default { + cartButtonLabel: { + type: 'string', + default: defaultCartButtonLabel, + }, +}; diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/block.json b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/block.json new file mode 100644 index 00000000000..ad20847e016 --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/block.json @@ -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 +} diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/block.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/block.tsx new file mode 100644 index 00000000000..56ed78ce4a7 --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/block.tsx @@ -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 ( + + ); +}; + +export default Block; diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/constants.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/constants.tsx new file mode 100644 index 00000000000..073499a8d4c --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/constants.tsx @@ -0,0 +1,9 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; + +export const defaultCartButtonLabel = __( + 'View my cart', + 'woo-gutenberg-products-block' +); diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/edit.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/edit.tsx new file mode 100644 index 00000000000..271badd7df2 --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/edit.tsx @@ -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 ( + { + setAttributes( { + cartButtonLabel: content, + } ); + } } + /> + ); +}; + +export const Save = (): JSX.Element => { + return
; +}; diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/index.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/index.tsx new file mode 100644 index 00000000000..37b561d13f9 --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-cart-button-block/index.tsx @@ -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: ( + + ), + }, + attributes, + edit: Edit, + save: Save, +} ); diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/attributes.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/attributes.tsx new file mode 100644 index 00000000000..bc16a01bf9b --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/attributes.tsx @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import { defaultCheckoutButtonLabel } from './constants'; + +export default { + checkoutButtonLabel: { + type: 'string', + default: defaultCheckoutButtonLabel, + }, +}; diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/block.json b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/block.json new file mode 100644 index 00000000000..e630a23bd23 --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/block.json @@ -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 +} diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/block.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/block.tsx new file mode 100644 index 00000000000..35e05a6284c --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/block.tsx @@ -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 ( + + ); +}; + +export default Block; diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/constants.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/constants.tsx new file mode 100644 index 00000000000..65253b4d373 --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/constants.tsx @@ -0,0 +1,9 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; + +export const defaultCheckoutButtonLabel = __( + 'Go to checkout', + 'woo-gutenberg-products-block' +); diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/edit.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/edit.tsx new file mode 100644 index 00000000000..dff86ba1e4c --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/edit.tsx @@ -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 ( + { + setAttributes( { + checkoutButtonLabel: content, + } ); + } } + /> + ); +}; + +export const Save = (): JSX.Element => { + return
; +}; diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/index.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/index.tsx new file mode 100644 index 00000000000..54ba642f28c --- /dev/null +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-checkout-button-block/index.tsx @@ -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: ( + + ), + }, + attributes, + edit: Edit, + save: Save, +} ); diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/block.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/block.tsx index 5d4ddff405e..1ce199d41a3 100644 --- a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/block.tsx +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/block.tsx @@ -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 (
- { CART_URL && ( - - ) } - { CHECKOUT_URL && ( - + { hasButtons ? ( + children + ) : ( + <> + + + ) }
diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/constants.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/constants.ts similarity index 100% rename from assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/constants.tsx rename to assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/constants.ts diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/edit.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/edit.tsx index 2f914800272..cbf7f341413 100644 --- a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/edit.tsx +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/edit.tsx @@ -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 (
@@ -63,29 +49,7 @@ export const Edit = ( { 'woo-gutenberg-products-block' ) } /> -
- { - setAttributes( { - cartButtonLabel: content, - } ); - } } - /> - { - setAttributes( { - checkoutButtonLabel: content, - } ); - } } - /> -
+ @@ -95,5 +59,9 @@ export const Edit = ( { }; export const Save = (): JSX.Element => { - return
; + return ( +
+ +
+ ); }; diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/index.tsx b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/index.tsx index 81f6901cbbf..3fd3816264a 100644 --- a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/index.tsx +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/mini-cart-footer-block/index.tsx @@ -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
; + }, + }, + ], edit: Edit, save: Save, } ); diff --git a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/register-components.ts b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/register-components.ts index 79aa25254e3..5547bba5352 100644 --- a/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/register-components.ts +++ b/assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/register-components.ts @@ -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 @@ -88,3 +90,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' + ) + ), +} ); diff --git a/assets/js/blocks/mini-cart/style.scss b/assets/js/blocks/mini-cart/style.scss index ffa6a26c9f0..072172d9cc0 100644 --- a/assets/js/blocks/mini-cart/style.scss +++ b/assets/js/blocks/mini-cart/style.scss @@ -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 { diff --git a/packages/checkout/blocks-registry/types.ts b/packages/checkout/blocks-registry/types.ts index 6516621092e..3d0ab6f710a 100644 --- a/packages/checkout/blocks-registry/types.ts +++ b/packages/checkout/blocks-registry/types.ts @@ -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', } diff --git a/src/BlockTypes/MiniCart.php b/src/BlockTypes/MiniCart.php index 3b2bd18b38f..56ac7201d1c 100644 --- a/src/BlockTypes/MiniCart.php +++ b/src/BlockTypes/MiniCart.php @@ -280,6 +280,8 @@ public function print_lazy_load_scripts() { 'items-frontend', 'footer-frontend', 'products-table-frontend', + 'cart-button-frontend', + 'checkout-button-frontend', ); } foreach ( $inner_blocks_frontend_scripts as $inner_block_frontend_script ) { diff --git a/src/BlockTypes/MiniCartCartButtonBlock.php b/src/BlockTypes/MiniCartCartButtonBlock.php new file mode 100644 index 00000000000..2982e138005 --- /dev/null +++ b/src/BlockTypes/MiniCartCartButtonBlock.php @@ -0,0 +1,14 @@ +