Update Cart & Checkout i2 temporarily locking to be on the parent block (https://github.com/woocommerce/woocommerce-blocks/pull/4837)

* update locking to be on parent, not block

* include cart as well
This commit is contained in:
Seghir Nadir 2021-09-24 14:44:05 +01:00 committed by GitHub
parent a2f39d07f1
commit f9f70893e0
13 changed files with 146 additions and 128 deletions

View File

@ -28,7 +28,7 @@ import { Icon, filledCart, removeCart } from '@woocommerce/icons';
* Internal dependencies
*/
import './editor.scss';
import { addClassToBody } from './hacks';
import { addClassToBody, useBlockPropsWithLocking } from './hacks';
import { useViewSwitcher } from './use-view-switcher';
import type { Attributes } from './types';
import { CartBlockControlsContext } from './context';
@ -184,12 +184,14 @@ export const Edit = ( {
],
[ 'woocommerce/empty-cart-block', {}, [] ],
];
return (
<div
className={ classnames( className, 'wp-block-woocommerce-cart', {
const blockProps = useBlockPropsWithLocking( {
className: classnames( className, 'wp-block-woocommerce-cart', {
'is-editor-preview': attributes.isPreview,
} ) }
>
} ),
} );
return (
<div { ...blockProps }>
<BlockErrorBoundary
header={ __(
'Cart Block Error',

View File

@ -17,11 +17,12 @@ import {
store as blockEditorStore,
} from '@wordpress/block-editor';
import { isTextField } from '@wordpress/dom';
import { useSelect, subscribe, select as _select } from '@wordpress/data';
import { subscribe, select as _select } from '@wordpress/data';
import { useEffect, useRef } from '@wordpress/element';
import { MutableRefObject } from 'react';
import { BACKSPACE, DELETE } from '@wordpress/keycodes';
import { hasFilter } from '@wordpress/hooks';
import { getBlockType } from '@wordpress/blocks';
/**
* Toggle class on body.
*
@ -73,64 +74,91 @@ export const addClassToBody = (): void => {
}
};
const isBlockLocked = ( clientId: string ): boolean => {
if ( ! clientId ) {
return false;
}
const { getBlock } = _select( blockEditorStore );
const block = getBlock( clientId );
// If lock.remove is defined at the block instance (not using the default value)
// Then we use it.
if ( typeof block?.attributes?.lock?.remove === 'boolean' ) {
return block.attributes.lock.remove;
}
// If we don't have lock on the block instance, we check the type
const blockType = getBlockType( block.name );
if ( typeof blockType?.attributes?.lock?.default?.remove === 'boolean' ) {
return blockType?.attributes?.lock?.default?.remove;
}
// If nothing is defined, return false
return false;
};
/**
* This is a hook we use in conjunction with useBlockProps. Its goal is to check if a block is locked (move or remove)
* and will stop the keydown event from propagating to stop it from being deleted via the keyboard.
* This is a hook we use in conjunction with useBlockProps. Its goal is to check if of the block's children is locked and being deleted.
* It will stop the keydown event from propagating to stop it from being deleted via the keyboard.
*
* @todo Disable custom locking support if native support is detected.
*/
const useLockBlock = ( {
clientId,
const useLockedChildren = ( {
ref,
attributes,
}: {
clientId: string;
ref: MutableRefObject< Element | undefined >;
attributes: Record< string, unknown >;
ref: MutableRefObject< HTMLElement | undefined >;
} ): void => {
const lockInCore = hasFilter(
'blocks.registerBlockType',
'core/lock/addAttribute'
);
const { isSelected } = useSelect(
( select ) => {
return {
isSelected: select( blockEditorStore ).isBlockSelected(
clientId
),
};
},
[ clientId ]
);
const node = ref.current;
return useEffect( () => {
if ( ! isSelected || ! node || lockInCore ) {
if ( ! node || lockInCore ) {
return;
}
function onKeyDown( event: KeyboardEvent ) {
const { keyCode, target } = event;
if ( ! ( target instanceof HTMLElement ) ) {
return;
}
// We're not trying to delete something here.
if ( keyCode !== BACKSPACE && keyCode !== DELETE ) {
return;
}
if ( target !== node || isTextField( target ) ) {
// We're in a field, so we should let text be deleted.
if ( isTextField( target ) ) {
return;
}
// Typecast to fix issue with isTextField.
const targetNode = target as HTMLElement;
// Our target isn't a block.
if ( targetNode.dataset.block === undefined ) {
return;
}
const clientId = targetNode.dataset.block;
const isLocked = isBlockLocked( clientId );
// Prevent the keyboard event from propogating if it supports locking.
if ( attributes?.lock?.remove ) {
if ( isLocked ) {
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
}
}
node.addEventListener( 'keydown', onKeyDown, true );
node.addEventListener( 'keydown', onKeyDown, {
capture: true,
passive: false,
} );
return () => {
node.removeEventListener( 'keydown', onKeyDown, true );
node.removeEventListener( 'keydown', onKeyDown, {
capture: true,
} );
};
}, [ node, isSelected, lockInCore, attributes ] );
}, [ node, lockInCore ] );
};
/**
@ -139,13 +167,10 @@ const useLockBlock = ( {
export const useBlockPropsWithLocking = (
props: Record< string, unknown > = {}
): Record< string, unknown > => {
const ref = useRef< Element >();
const { attributes } = props;
const ref = useRef< HTMLElement >();
const blockProps = useBlockProps( { ref, ...props } );
useLockBlock( {
useLockedChildren( {
ref,
attributes,
clientId: blockProps[ 'data-block' ],
} );
return blockProps;
};

View File

@ -14,7 +14,6 @@ import classnames from 'classnames';
*/
import Block from './block';
import './editor.scss';
import { useBlockPropsWithLocking } from '../../hacks';
/**
* Renders a placeholder in the editor.
@ -48,23 +47,13 @@ const NoExpressPaymentMethodsPlaceholder = () => {
);
};
export const Edit = ( {
attributes,
}: {
attributes: {
lock: {
move: boolean;
remove: boolean;
};
};
} ): JSX.Element | null => {
export const Edit = (): JSX.Element | null => {
const { paymentMethods, isInitialized } = useExpressPaymentMethods();
const hasExpressPaymentMethods = Object.keys( paymentMethods ).length > 0;
const blockProps = useBlockPropsWithLocking( {
const blockProps = useBlockProps( {
className: classnames( {
'wp-block-woocommerce-cart-express-payment-block--has-express-payment-methods': hasExpressPaymentMethods,
} ),
attributes,
} );
if ( ! isInitialized ) {

View File

@ -6,21 +6,10 @@ import { useBlockProps } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import { useBlockPropsWithLocking } from '../../hacks';
import Block from './block';
export const Edit = ( {
attributes,
}: {
attributes: {
lock: {
move: boolean;
remove: boolean;
};
};
} ): JSX.Element => {
const blockProps = useBlockPropsWithLocking( { attributes } );
export const Edit = (): JSX.Element => {
const blockProps = useBlockProps();
return (
<div { ...blockProps }>

View File

@ -10,7 +10,6 @@ import { getSetting } from '@woocommerce/settings';
* Internal dependencies
*/
import Block from './block';
import { useBlockPropsWithLocking } from '../../hacks';
export const Edit = ( {
attributes,
@ -27,7 +26,7 @@ export const Edit = ( {
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const { showRateAfterTaxName, isShippingCalculatorEnabled } = attributes;
const blockProps = useBlockPropsWithLocking( { attributes } );
const blockProps = useBlockProps();
const taxesEnabled = getSetting( 'taxesEnabled' ) as boolean;
const displayItemizedTaxes = getSetting(
'displayItemizedTaxes',

View File

@ -12,7 +12,6 @@ import { CART_PAGE_ID } from '@woocommerce/block-settings';
* Internal dependencies
*/
import Block from './block';
import { useBlockPropsWithLocking } from '../../hacks';
export const Edit = ( {
attributes,
setAttributes,
@ -22,7 +21,7 @@ export const Edit = ( {
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const blockProps = useBlockPropsWithLocking( { attributes } );
const blockProps = useBlockProps();
const { checkoutPageId = 0 } = attributes;
const { current: savedCheckoutPageId } = useRef( checkoutPageId );
const currentPostId = useSelect(

View File

@ -35,7 +35,7 @@ import { CartCheckoutCompatibilityNotice } from '@woocommerce/editor-components/
*/
import './styles/editor.scss';
import { Columns } from './columns';
import { addClassToBody } from './hacks';
import { addClassToBody, useBlockPropsWithLocking } from './hacks';
import { CheckoutBlockContext, CheckoutBlockControlsContext } from './context';
import type { Attributes } from './types';
@ -260,9 +260,9 @@ export const Edit = ( {
</PanelBody>
</InspectorControls>
);
const blockProps = useBlockPropsWithLocking();
return (
<>
<div { ...blockProps }>
<EditorProvider
previewData={ { previewCart, previewSavedPaymentMethods } }
>
@ -305,7 +305,7 @@ export const Edit = ( {
</CheckoutProvider>
</EditorProvider>
<CartCheckoutCompatibilityNotice blockName="checkout" />
</>
</div>
);
};

View File

@ -3,14 +3,17 @@
*/
import { __ } from '@wordpress/i18n';
import classnames from 'classnames';
import { PlainText, InspectorControls } from '@wordpress/block-editor';
import {
PlainText,
InspectorControls,
useBlockProps,
} from '@wordpress/block-editor';
import { PanelBody, ToggleControl } from '@wordpress/components';
/**
* Internal dependencies
*/
import FormStepHeading from './form-step-heading';
import { useBlockPropsWithLocking } from '../hacks';
export interface FormStepBlockProps {
attributes: { title: string; description: string; showStepNumber: boolean };
setAttributes: ( attributes: Record< string, unknown > ) => void;
@ -29,11 +32,10 @@ export const FormStepBlock = ( {
children,
}: FormStepBlockProps ): JSX.Element => {
const { title = '', description = '', showStepNumber = true } = attributes;
const blockProps = useBlockPropsWithLocking( {
const blockProps = useBlockProps( {
className: classnames( 'wc-block-components-checkout-step', className, {
'wc-block-components-checkout-step--with-step-number': showStepNumber,
} ),
attributes,
} );
return (
<div { ...blockProps }>

View File

@ -17,11 +17,12 @@ import {
store as blockEditorStore,
} from '@wordpress/block-editor';
import { isTextField } from '@wordpress/dom';
import { useSelect, subscribe, select as _select } from '@wordpress/data';
import { subscribe, select as _select } from '@wordpress/data';
import { useEffect, useRef } from '@wordpress/element';
import { MutableRefObject } from 'react';
import { BACKSPACE, DELETE } from '@wordpress/keycodes';
import { hasFilter } from '@wordpress/hooks';
import { getBlockType } from '@wordpress/blocks';
/**
* Toggle class on body.
*
@ -73,64 +74,91 @@ export const addClassToBody = (): void => {
}
};
const isBlockLocked = ( clientId: string ): boolean => {
if ( ! clientId ) {
return false;
}
const { getBlock } = _select( blockEditorStore );
const block = getBlock( clientId );
// If lock.remove is defined at the block instance (not using the default value)
// Then we use it.
if ( typeof block?.attributes?.lock?.remove === 'boolean' ) {
return block.attributes.lock.remove;
}
// If we don't have lock on the block instance, we check the type
const blockType = getBlockType( block.name );
if ( typeof blockType?.attributes?.lock?.default?.remove === 'boolean' ) {
return blockType?.attributes?.lock?.default?.remove;
}
// If nothing is defined, return false
return false;
};
/**
* This is a hook we use in conjunction with useBlockProps. Its goal is to check if a block is locked (move or remove)
* and will stop the keydown event from propagating to stop it from being deleted via the keyboard.
* This is a hook we use in conjunction with useBlockProps. Its goal is to check if of the block's children is locked and being deleted.
* It will stop the keydown event from propagating to stop it from being deleted via the keyboard.
*
* @todo Disable custom locking support if native support is detected.
*/
const useLockBlock = ( {
clientId,
const useLockedChildren = ( {
ref,
attributes,
}: {
clientId: string;
ref: MutableRefObject< Element | undefined >;
attributes: Record< string, unknown >;
ref: MutableRefObject< HTMLElement | undefined >;
} ): void => {
const lockInCore = hasFilter(
'blocks.registerBlockType',
'core/lock/addAttribute'
);
const { isSelected } = useSelect(
( select ) => {
return {
isSelected: select( blockEditorStore ).isBlockSelected(
clientId
),
};
},
[ clientId ]
);
const node = ref.current;
return useEffect( () => {
if ( ! isSelected || ! node || lockInCore ) {
if ( ! node || lockInCore ) {
return;
}
function onKeyDown( event: KeyboardEvent ) {
const { keyCode, target } = event;
if ( ! ( target instanceof HTMLElement ) ) {
return;
}
// We're not trying to delete something here.
if ( keyCode !== BACKSPACE && keyCode !== DELETE ) {
return;
}
if ( target !== node || isTextField( target ) ) {
// We're in a field, so we should let text be deleted.
if ( isTextField( target ) ) {
return;
}
// Typecast to fix issue with isTextField.
const targetNode = target as HTMLElement;
// Our target isn't a block.
if ( targetNode.dataset.block === undefined ) {
return;
}
const clientId = targetNode.dataset.block;
const isLocked = isBlockLocked( clientId );
// Prevent the keyboard event from propogating if it supports locking.
if ( attributes?.lock?.remove ) {
if ( isLocked ) {
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
}
}
node.addEventListener( 'keydown', onKeyDown, true );
node.addEventListener( 'keydown', onKeyDown, {
capture: true,
passive: false,
} );
return () => {
node.removeEventListener( 'keydown', onKeyDown, true );
node.removeEventListener( 'keydown', onKeyDown, {
capture: true,
} );
};
}, [ node, isSelected, lockInCore, attributes ] );
}, [ node, lockInCore ] );
};
/**
@ -139,13 +167,10 @@ const useLockBlock = ( {
export const useBlockPropsWithLocking = (
props: Record< string, unknown > = {}
): Record< string, unknown > => {
const ref = useRef< Element >();
const { attributes } = props;
const ref = useRef< HTMLElement >();
const blockProps = useBlockProps( { ref, ...props } );
useLockBlock( {
useLockedChildren( {
ref,
attributes,
clientId: blockProps[ 'data-block' ],
} );
return blockProps;
};

View File

@ -12,7 +12,7 @@ import { CHECKOUT_PAGE_ID } from '@woocommerce/block-settings';
* Internal dependencies
*/
import Block from './block';
import { useBlockPropsWithLocking } from '../../hacks';
export const Edit = ( {
attributes,
setAttributes,
@ -23,7 +23,7 @@ export const Edit = ( {
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const blockProps = useBlockPropsWithLocking( { attributes } );
const blockProps = useBlockProps();
const { cartPageId = 0, showReturnToCart = true } = attributes;
const { current: savedCartPageId } = useRef( cartPageId );
const currentPostId = useSelect(

View File

@ -14,7 +14,6 @@ import classnames from 'classnames';
*/
import Block from './block';
import './editor.scss';
import { useBlockPropsWithLocking } from '../../hacks';
/**
* Renders a placeholder in the editor.
@ -60,7 +59,7 @@ export const Edit = ( {
} ): JSX.Element | null => {
const { paymentMethods, isInitialized } = useExpressPaymentMethods();
const hasExpressPaymentMethods = Object.keys( paymentMethods ).length > 0;
const blockProps = useBlockPropsWithLocking( {
const blockProps = useBlockProps( {
className: classnames( {
'wp-block-woocommerce-checkout-express-payment-block--has-express-payment-methods': hasExpressPaymentMethods,
} ),

View File

@ -9,19 +9,9 @@ import { Disabled } from '@wordpress/components';
*/
import Block from './block';
import './editor.scss';
import { useBlockPropsWithLocking } from '../../hacks';
export const Edit = ( {
attributes,
}: {
attributes: {
lock: {
move: boolean;
remove: boolean;
};
};
} ): JSX.Element => {
const blockProps = useBlockPropsWithLocking( { attributes } );
export const Edit = (): JSX.Element => {
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<Disabled>

View File

@ -10,7 +10,6 @@ import { getSetting } from '@woocommerce/settings';
* Internal dependencies
*/
import Block from './block';
import { useBlockPropsWithLocking } from '../../hacks';
export const Edit = ( {
attributes,
@ -25,7 +24,7 @@ export const Edit = ( {
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const blockProps = useBlockPropsWithLocking( { attributes } );
const blockProps = useBlockProps();
const taxesEnabled = getSetting( 'taxesEnabled' ) as boolean;
const displayItemizedTaxes = getSetting(
'displayItemizedTaxes',