* Create the counter block and the header pattern

* Change the pattern to an inner block

* Allow customizing item counter block

* Rename block

* Allow removing the items counter block

* Add padding controls

* Preload new blocks

* Add title block back from the inserter

* Move h2 tag to the parent title

* Align items at the bottom

* Unify styles

* Add title class in the editor

* Prevent removing title blocks

* Disallow unlocking and inserter

* Disallowing moving blocks
This commit is contained in:
Alba Rincón 2023-04-14 09:21:21 +02:00 committed by GitHub
parent ad2003117e
commit 709a7a9add
24 changed files with 444 additions and 42 deletions

View File

@ -42,7 +42,11 @@
h2.wc-block-mini-cart__title {
@include font-size(larger);
margin: $gap-largest $gap 0;
.block-editor-block-list__layout {
display: flex;
align-items: baseline;
}
}
table.wc-block-cart-items {

View File

@ -4,6 +4,8 @@
import './empty-mini-cart-contents-block';
import './filled-mini-cart-contents-block';
import './mini-cart-title-block';
import './mini-cart-title-items-counter-block';
import './mini-cart-title-label-block';
import './mini-cart-items-block';
import './mini-cart-products-table-block';
import './mini-cart-footer-block';

View File

@ -13,13 +13,13 @@ import { getIconsFromPaymentMethods } from '@woocommerce/base-utils';
import { getSetting } from '@woocommerce/settings';
import { PaymentEventsProvider } from '@woocommerce/base-context';
import classNames from 'classnames';
import { isObject } from '@woocommerce/types';
/**
* Internal dependencies
*/
import CartButton from '../mini-cart-cart-button-block/block';
import CheckoutButton from '../mini-cart-checkout-button-block/block';
import { hasChildren } from '../utils';
const PaymentMethodIconsElement = (): JSX.Element => {
const { paymentMethods } = usePaymentMethods();
@ -37,18 +37,6 @@ interface Props {
checkoutButtonLabel: string;
}
/**
* Checks if there are any children that are blocks.
*/
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,

View File

@ -1,34 +1,45 @@
/**
* External dependencies
*/
import { sprintf, _n, __ } from '@wordpress/i18n';
import { useStoreCart } from '@woocommerce/base-context/hooks';
import classNames from 'classnames';
/**
* Internal dependencies
*/
import TitleItemsCounter from '../mini-cart-title-items-counter-block/block';
import TitleYourCart from '../mini-cart-title-label-block/block';
import { hasChildren } from '../utils';
type MiniCartTitleBlockProps = {
className: string;
children: JSX.Element;
};
const Block = ( { className }: MiniCartTitleBlockProps ): JSX.Element => {
const { cartItemsCount, cartIsLoading } = useStoreCart();
const Block = ( {
children,
className,
}: MiniCartTitleBlockProps ): JSX.Element | null => {
const { cartIsLoading } = useStoreCart();
if ( cartIsLoading ) {
return null;
}
// The `Mini Cart Title` was converted to two inner blocks, but we still need to render the old title for
// themes that have the old `mini-cart.html` template. So we check if there are any inner blocks and if
// not, render the title blocks.
const hasTitleInnerBlocks = hasChildren( children );
return (
<h2 className={ classNames( className, 'wc-block-mini-cart__title' ) }>
{ cartIsLoading
? __( 'Your cart', 'woo-gutenberg-products-block' )
: sprintf(
/* translators: %d is the count of items in the cart. */
_n(
'Your cart (%d item)',
'Your cart (%d items)',
cartItemsCount,
'woo-gutenberg-products-block'
),
cartItemsCount
) }
{ hasTitleInnerBlocks ? (
children
) : (
<>
<TitleYourCart />
<TitleItemsCounter />
</>
) }
</h2>
);
};

View File

@ -1,23 +1,36 @@
/**
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import Block from './block';
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
export const Edit = (): JSX.Element => {
const blockProps = useBlockProps();
const blockProps = useBlockProps( {
className: 'wc-block-mini-cart__title',
} );
const TEMPLATE = [
[ 'woocommerce/mini-cart-title-label-block', {} ],
[ 'woocommerce/mini-cart-title-items-counter-block', {} ],
];
return (
<div { ...blockProps }>
<Block />
</div>
<h2 { ...blockProps }>
<InnerBlocks
allowedBlocks={ [
'woocommerce/mini-cart-title-label-block',
'woocommerce/mini-cart-title-items-counter-block',
] }
template={ TEMPLATE }
templateLock="all"
/>
</h2>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() }></div>;
return (
<div { ...useBlockProps.save() }>
<InnerBlocks.Content />
</div>
);
};

View File

@ -0,0 +1,29 @@
{
"name": "woocommerce/mini-cart-title-items-counter-block",
"version": "1.0.0",
"title": "Mini Cart Title Items Counter",
"description": "Block that displays the items counter part of the Mini Cart Title block.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false,
"color": {
"text": true,
"background": true
},
"typography": {
"fontSize": true
},
"spacing": {
"padding": true
}
},
"parent": [ "woocommerce/mini-cart-title-block" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2,
"$schema": "https://schemas.wp.org/trunk/block.json"
}

View File

@ -0,0 +1,50 @@
/**
* External dependencies
*/
import { useStoreCart } from '@woocommerce/base-context';
import classNames from 'classnames';
import { _n, sprintf } from '@wordpress/i18n';
import {
useColorProps,
useSpacingProps,
useTypographyProps,
} from '@woocommerce/base-hooks';
type Props = {
className?: string;
};
const Block = ( props: Props ): JSX.Element => {
const { cartItemsCount } = useStoreCart();
const colorProps = useColorProps( props );
const typographyProps = useTypographyProps( props );
const spacingProps = useSpacingProps( props );
return (
<span
className={ classNames(
props.className,
colorProps.className,
typographyProps.className
) }
style={ {
...colorProps.style,
...typographyProps.style,
...spacingProps.style,
} }
>
{ sprintf(
/* translators: %d is the count of items in the cart. */
_n(
'(%d item)',
'(%d items)',
cartItemsCount,
'woo-gutenberg-products-block'
),
cartItemsCount
) }
</span>
);
};
export default Block;

View File

@ -0,0 +1,30 @@
/**
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
import { _n, sprintf } from '@wordpress/i18n';
import { useStoreCart } from '@woocommerce/base-context';
export const Edit = (): JSX.Element => {
const blockProps = useBlockProps();
const { cartItemsCount } = useStoreCart();
return (
<span { ...blockProps }>
{ sprintf(
/* translators: %d is the count of items in the cart. */
_n(
'(%d item)',
'(%d items)',
cartItemsCount,
'woo-gutenberg-products-block'
),
cartItemsCount
) }
</span>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() }></div>;
};

View File

@ -0,0 +1,26 @@
/**
* External dependencies
*/
import { Icon, heading } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
// 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-title-items-counter-block', {
icon: {
src: (
<Icon
icon={ heading }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

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

View File

@ -0,0 +1,34 @@
{
"name": "woocommerce/mini-cart-title-label-block",
"version": "1.0.0",
"title": "Mini Cart Title Label",
"description": "Block that displays the 'Your cart' part of the Mini Cart Title block.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false,
"color": {
"text": true,
"background": true
},
"typography": {
"fontSize": true
},
"spacing": {
"padding": true
}
},
"attributes": {
"label": {
"type": "string"
}
},
"parent": [ "woocommerce/mini-cart-title-block" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2,
"$schema": "https://schemas.wp.org/trunk/block.json"
}

View File

@ -0,0 +1,44 @@
/**
* External dependencies
*/
import {
useColorProps,
useSpacingProps,
useTypographyProps,
} from '@woocommerce/base-hooks';
import classNames from 'classnames';
/**
* Internal dependencies
*/
import { defaultYourCartLabel } from './constants';
type Props = {
label?: string;
className?: string;
};
const Block = ( props: Props ): JSX.Element => {
const colorProps = useColorProps( props );
const typographyProps = useTypographyProps( props );
const spacingProps = useSpacingProps( props );
return (
<span
className={ classNames(
props.className,
colorProps.className,
typographyProps.className
) }
style={ {
...colorProps.style,
...typographyProps.style,
...spacingProps.style,
} }
>
{ props.label || defaultYourCartLabel }
</span>
);
};
export default Block;

View File

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

View File

@ -0,0 +1,34 @@
/**
* External dependencies
*/
import { RichText, useBlockProps } from '@wordpress/block-editor';
interface Attributes {
attributes: {
label: string;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
}
export const Edit = ( {
attributes: { label },
setAttributes,
}: Attributes ): JSX.Element => {
const blockProps = useBlockProps();
return (
<span { ...blockProps }>
<RichText
allowedFormats={ [] }
value={ label }
onChange={ ( newLabel ) =>
setAttributes( { label: newLabel } )
}
/>
</span>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() }></div>;
};

View File

@ -0,0 +1,28 @@
/**
* External dependencies
*/
import { Icon, heading } 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-title-label-block', {
icon: {
src: (
<Icon
icon={ heading }
className="wc-block-editor-components-block-icon"
/>
),
},
attributes,
edit: Edit,
save: Save,
} );

View File

@ -10,6 +10,8 @@ import { lazy } from '@wordpress/element';
import emptyMiniCartContentsMetadata from './empty-mini-cart-contents-block/block.json';
import filledMiniCartMetadata from './filled-mini-cart-contents-block/block.json';
import miniCartTitleMetadata from './mini-cart-title-block/block.json';
import miniCartTitleItemsCounterMetadata from './mini-cart-title-items-counter-block/block.json';
import miniCartTitleLabelBlockMetadata from './mini-cart-title-label-block/block.json';
import miniCartProductsTableMetadata from './mini-cart-products-table-block/block.json';
import miniCartFooterMetadata from './mini-cart-footer-block/block.json';
import miniCartItemsMetadata from './mini-cart-items-block/block.json';
@ -52,6 +54,28 @@ registerCheckoutBlock( {
),
} );
registerCheckoutBlock( {
metadata: miniCartTitleItemsCounterMetadata,
force: false,
component: lazy(
() =>
import(
/* webpackChunkName: "mini-cart-contents-block/title-items-counter" */ './mini-cart-title-items-counter-block/block'
)
),
} );
registerCheckoutBlock( {
metadata: miniCartTitleLabelBlockMetadata,
force: false,
component: lazy(
() =>
import(
/* webpackChunkName: "mini-cart-contents-block/title-label" */ './mini-cart-title-label-block/block'
)
),
} );
registerCheckoutBlock( {
metadata: miniCartItemsMetadata,
component: lazy(

View File

@ -1,3 +1,8 @@
/**
* External dependencies
*/
import { isObject } from 'lodash';
type Variant = 'text' | 'contained' | 'outlined';
export const getVariant = (
@ -14,3 +19,15 @@ export const getVariant = (
return defaultVariant;
};
/**
* Checks if there are any children that are blocks.
*/
export const hasChildren = ( children ): boolean => {
return children.some( ( child ) => {
if ( Array.isArray( child ) ) {
return hasChildren( child );
}
return isObject( child ) && child.key !== null;
} );
};

View File

@ -121,12 +121,18 @@
}
h2.wc-block-mini-cart__title {
@include font-size(larger);
display: flex;
align-items: baseline;
background: inherit;
margin: $gap $gap $gap * -2;
padding-bottom: $gap * 2;
mask-image: linear-gradient(#000 calc(100% - #{$gap * 1.5}), transparent);
z-index: 1;
margin: $gap $gap $gap * -2;
@include font-size(larger);
span:first-child {
margin-right: $gap-smaller;
}
}
.wc-block-mini-cart__items {

View File

@ -25,6 +25,7 @@ export enum innerBlockAreas {
MINI_CART = 'woocommerce/mini-cart-contents',
EMPTY_MINI_CART = 'woocommerce/empty-mini-cart-contents-block',
FILLED_MINI_CART = 'woocommerce/filled-mini-cart-contents-block',
MINI_CART_TITLE = 'woocommerce/mini-cart-title-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',

View File

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

View File

@ -155,6 +155,8 @@ class MiniCartContents extends AbstractBlock {
$block_types[] = 'MiniCartCartButtonBlock';
$block_types[] = 'MiniCartCheckoutButtonBlock';
$block_types[] = 'MiniCartTitleBlock';
$block_types[] = 'MiniCartTitleItemsCounterBlock';
$block_types[] = 'MiniCartTitleLabelBlock';
return $block_types;
}

View File

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

View File

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

View File

@ -4,6 +4,15 @@
<div class="wp-block-woocommerce-filled-mini-cart-contents-block">
<!-- wp:woocommerce/mini-cart-title-block -->
<div class="wp-block-woocommerce-mini-cart-title-block">
<!-- wp:woocommerce/mini-cart-title-label-block -->
<div class="wp-block-woocommerce-mini-cart-title-label-block">
</div>
<!-- /wp:woocommerce/mini-cart-title-label-block -->
<!-- wp:woocommerce/mini-cart-title-items-counter-block -->
<div class="wp-block-woocommerce-mini-cart-title-items-counter-block">
</div>
<!-- /wp:woocommerce/mini-cart-title-items-counter-block -->
</div>
<!-- /wp:woocommerce/mini-cart-title-block -->
<!-- wp:woocommerce/mini-cart-items-block -->