Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Show Cart and Checkout blocks in Style Book (#8888)
Browse files Browse the repository at this point in the history
* Update forced layout

* Track isPreview in editor context

* Add preview to checkout

* Add preview to cart
  • Loading branch information
mikejolley authored Mar 30, 2023
1 parent d24aa8f commit 2caa016
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 95 deletions.
6 changes: 6 additions & 0 deletions assets/js/base/context/providers/editor-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ interface EditorContextType {

// Get data by name.
getPreviewData: ( name: string ) => Record< string, unknown >;

// Indicates whether in the preview context.
isPreview?: boolean;
}

const EditorContext = createContext( {
Expand All @@ -38,11 +41,13 @@ export const EditorProvider = ( {
currentPostId = 0,
previewData = {},
currentView = '',
isPreview = false,
}: {
children: React.ReactChildren;
currentPostId?: number | undefined;
previewData?: Record< string, unknown > | undefined;
currentView?: string | undefined;
isPreview?: boolean | undefined;
} ) => {
const editingPostId = useSelect(
( select ): number =>
Expand All @@ -68,6 +73,7 @@ export const EditorProvider = ( {
currentView,
previewData,
getPreviewData,
isPreview,
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,93 +7,14 @@ import {
createBlock,
getBlockType,
createBlocksFromInnerBlocksTemplate,
BlockInstance,
TemplateArray,
} from '@wordpress/blocks';
import type { Block, TemplateArray } from '@wordpress/blocks';
import type { MutableRefObject } from 'react';

interface LockableBlock extends Block {
attributes: {
lock?: {
type: 'object';
remove?: boolean;
move: boolean;
default?: {
remove?: boolean;
move?: boolean;
};
};
};
}
const isBlockLocked = ( {
attributes,
}: {
attributes: LockableBlock[ 'attributes' ];
} ) => Boolean( attributes.lock?.remove || attributes.lock?.default?.remove );
import { useEditorContext } from '@woocommerce/base-context';

/**
* This hook is used to determine which blocks are missing from a block. Given the list of inner blocks of a block, we
* can check for any registered blocks that:
* a) Are locked,
* b) Have the parent set as the current block, and
* c) Are not present in the list of inner blocks.
* Internal dependencies
*/
const getMissingBlocks = (
innerBlocks: BlockInstance[],
registeredBlockTypes: ( LockableBlock | undefined )[]
) => {
const lockedBlockTypes = registeredBlockTypes.filter(
( block: LockableBlock | undefined ) => block && isBlockLocked( block )
);
const missingBlocks: LockableBlock[] = [];
lockedBlockTypes.forEach( ( lockedBlock ) => {
if ( typeof lockedBlock === 'undefined' ) {
return;
}
const existingBlock = innerBlocks.find(
( block ) => block.name === lockedBlock.name
);

if ( ! existingBlock ) {
missingBlocks.push( lockedBlock );
}
} );
return missingBlocks;
};

/**
* This hook is used to determine the position that a missing block should be inserted at.
*
* @return The index to insert the missing block at.
*/
const findBlockPosition = ( {
defaultTemplatePosition,
innerBlocks,
currentDefaultTemplate,
}: {
defaultTemplatePosition: number;
innerBlocks: BlockInstance[];
currentDefaultTemplate: MutableRefObject< TemplateArray >;
} ) => {
switch ( defaultTemplatePosition ) {
case -1:
// The block is not part of the default template, so we append it to the current layout.
return innerBlocks.length;
// defaultTemplatePosition defaults to 0, so if this happens we can just return, this is because the block was
// the first block in the default layout, so we can prepend it to the current layout.
case 0:
return 0;
default:
// The new layout may have extra blocks compared to the default template, so rather than insert
// at the default position, we should append it after another default block.
const adjacentBlock =
currentDefaultTemplate.current[ defaultTemplatePosition - 1 ];
const position = innerBlocks.findIndex(
( { name: blockName } ) => blockName === adjacentBlock[ 0 ]
);
return position === -1 ? defaultTemplatePosition : position + 1;
}
};
import { getMissingBlocks, findBlockPosition } from './utils';

/**
* Hook to ensure FORCED blocks are rendered in the correct place.
Expand All @@ -112,11 +33,18 @@ export const useForcedLayout = ( {
} ) => {
const currentRegisteredBlocks = useRef( registeredBlocks );
const currentDefaultTemplate = useRef( defaultTemplate );

const registry = useRegistry();
const { isPreview } = useEditorContext();

useEffect( () => {
let templateSynced = false;

if ( isPreview ) {
return;
}

const { replaceInnerBlocks } = dispatch( 'core/block-editor' );

return registry.subscribe( () => {
const innerBlocks = registry
.select( 'core/block-editor' )
Expand Down Expand Up @@ -181,5 +109,5 @@ export const useForcedLayout = ( {
.insertBlocks( blockConfig, insertAtPosition, clientId );
} );
}, 'core/block-editor' );
}, [ clientId, registry ] );
}, [ clientId, isPreview, registry ] );
};
18 changes: 18 additions & 0 deletions assets/js/blocks/cart-checkout-shared/use-forced-layout/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* External dependencies
*/
import type { Block } from '@wordpress/blocks';

export interface LockableBlock extends Block {
attributes: {
lock?: {
type: 'object';
remove?: boolean;
move: boolean;
default?: {
remove?: boolean;
move?: boolean;
};
};
};
}
80 changes: 80 additions & 0 deletions assets/js/blocks/cart-checkout-shared/use-forced-layout/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* External dependencies
*/
import type { BlockInstance, TemplateArray } from '@wordpress/blocks';
import type { MutableRefObject } from 'react';

/**
* Internal dependencies
*/
import { LockableBlock } from './types';

export const isBlockLocked = ( {
attributes,
}: {
attributes: LockableBlock[ 'attributes' ];
} ) => Boolean( attributes.lock?.remove || attributes.lock?.default?.remove );

/**
* This hook is used to determine which blocks are missing from a block. Given the list of inner blocks of a block, we
* can check for any registered blocks that:
* a) Are locked,
* b) Have the parent set as the current block, and
* c) Are not present in the list of inner blocks.
*/
export const getMissingBlocks = (
innerBlocks: BlockInstance[],
registeredBlockTypes: ( LockableBlock | undefined )[]
) => {
const lockedBlockTypes = registeredBlockTypes.filter(
( block: LockableBlock | undefined ) => block && isBlockLocked( block )
);
const missingBlocks: LockableBlock[] = [];
lockedBlockTypes.forEach( ( lockedBlock ) => {
if ( typeof lockedBlock === 'undefined' ) {
return;
}
const existingBlock = innerBlocks.find(
( block ) => block.name === lockedBlock.name
);

if ( ! existingBlock ) {
missingBlocks.push( lockedBlock );
}
} );
return missingBlocks;
};

/**
* This hook is used to determine the position that a missing block should be inserted at.
*
* @return The index to insert the missing block at.
*/
export const findBlockPosition = ( {
defaultTemplatePosition,
innerBlocks,
currentDefaultTemplate,
}: {
defaultTemplatePosition: number;
innerBlocks: BlockInstance[];
currentDefaultTemplate: MutableRefObject< TemplateArray >;
} ) => {
switch ( defaultTemplatePosition ) {
case -1:
// The block is not part of the default template, so we append it to the current layout.
return innerBlocks.length;
// defaultTemplatePosition defaults to 0, so if this happens we can just return, this is because the block was
// the first block in the default layout, so we can prepend it to the current layout.
case 0:
return 0;
default:
// The new layout may have extra blocks compared to the default template, so rather than insert
// at the default position, we should append it after another default block.
const adjacentBlock =
currentDefaultTemplate.current[ defaultTemplatePosition - 1 ];
const position = innerBlocks.findIndex(
( { name: blockName } ) => blockName === adjacentBlock[ 0 ]
);
return position === -1 ? defaultTemplatePosition : position + 1;
}
};
15 changes: 5 additions & 10 deletions assets/js/blocks/cart/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import './editor.scss';
import {
addClassToBody,
useBlockPropsWithLocking,
useForcedLayout,
BlockSettings,
} from '../cart-checkout-shared';
import '../cart-checkout-shared/sidebar-notices';
Expand All @@ -38,22 +37,17 @@ const ALLOWED_BLOCKS = [
'woocommerce/empty-cart-block',
];

export const Edit = ( { className, attributes, setAttributes, clientId } ) => {
const { hasDarkControls, currentView } = attributes;
export const Edit = ( { className, attributes, setAttributes } ) => {
const { hasDarkControls, currentView, isPreview = false } = attributes;
const defaultTemplate = [
[ 'woocommerce/filled-cart-block', {}, [] ],
[ 'woocommerce/empty-cart-block', {}, [] ],
];
const blockProps = useBlockPropsWithLocking( {
className: classnames( className, 'wp-block-woocommerce-cart', {
'is-editor-preview': attributes.isPreview,
'is-editor-preview': isPreview,
} ),
} );
useForcedLayout( {
clientId,
registeredBlocks: ALLOWED_BLOCKS,
defaultTemplate,
} );

return (
<div { ...blockProps }>
Expand Down Expand Up @@ -81,6 +75,7 @@ export const Edit = ( { className, attributes, setAttributes, clientId } ) => {
<EditorProvider
previewData={ { previewCart } }
currentView={ currentView }
isPreview={ isPreview }
>
<CartBlockContext.Provider
value={ {
Expand All @@ -92,7 +87,7 @@ export const Edit = ( { className, attributes, setAttributes, clientId } ) => {
<InnerBlocks
allowedBlocks={ ALLOWED_BLOCKS }
template={ defaultTemplate }
templateLock={ false }
templateLock="insert"
/>
</CartProvider>
</SlotFillProvider>
Expand Down
6 changes: 6 additions & 0 deletions assets/js/blocks/cart/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ const settings = {
html: false,
multiple: false,
},
example: {
attributes: {
isPreview: true,
},
viewportWidth: 800,
},
attributes: blockAttributes,
edit: Edit,
save: Save,
Expand Down
6 changes: 6 additions & 0 deletions assets/js/blocks/checkout/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
"html": false,
"multiple": false
},
"example": {
"attributes": {
"isPreview": true
},
"viewportWidth": 800
},
"attributes": {
"isPreview": {
"type": "boolean",
Expand Down
2 changes: 2 additions & 0 deletions assets/js/blocks/checkout/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const Edit = ( {
showReturnToCart,
showRateAfterTaxName,
cartPageId,
isPreview = false,
} = attributes;

const defaultTemplate = [
Expand Down Expand Up @@ -146,6 +147,7 @@ export const Edit = ( {
/>
</InspectorControls>
<EditorProvider
isPreview={ isPreview }
previewData={ { previewCart, previewSavedPaymentMethods } }
>
<SlotFillProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const Edit = ( {
clientId: string;
attributes: {
className?: string;
isPreview?: boolean;
};
} ): JSX.Element => {
const blockProps = useBlockProps( {
Expand Down

0 comments on commit 2caa016

Please sign in to comment.