Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InnerBlocks: introduce prop to specify render callback for each block. #24232

Merged
merged 13 commits into from
Aug 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ function BlockListBlock( {
toggleSelection,
index,
enableAnimation,
__experimentalRenderCallback: renderCallback,
} ) {
// In addition to withSelect, we should favor using useSelect in this
// component going forward to avoid leaking new props to the public API
Expand Down Expand Up @@ -241,6 +242,10 @@ function BlockListBlock( {
block = <Block.div { ...wrapperProps }>{ blockEdit }</Block.div>;
}

if ( renderCallback ) {
block = renderCallback( block );
}

return (
<BlockListBlockContext.Provider value={ memoizedValue }>
<BlockCrashBoundary onError={ onBlockError }>
Expand Down
4 changes: 4 additions & 0 deletions packages/block-editor/src/components/block-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function BlockList(
className,
rootClientId,
renderAppender,
__experimentalItemCallback,
__experimentalTagName = 'div',
__experimentalAppenderTagName,
__experimentalPassedProps = {},
Expand Down Expand Up @@ -110,6 +111,9 @@ function BlockList(
isDropTarget &&
orientation === 'horizontal',
} ) }
__experimentalRenderCallback={
__experimentalItemCallback
}
/>
</AsyncModeProvider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ export class BlockList extends Component {
parentWidth,
marginVertical = styles.defaultBlock.marginTop,
marginHorizontal = styles.defaultBlock.marginLeft,
__experimentalItemCallback,
} = this.props;
return (
<BlockListItem
Expand All @@ -279,6 +280,7 @@ export class BlockList extends Component {
onCaretVerticalPositionChange={
this.onCaretVerticalPositionChange
}
__experimentalRenderCallback={ __experimentalItemCallback }
/>
);
}
Expand Down
8 changes: 7 additions & 1 deletion packages/block-editor/src/components/inner-blocks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function UncontrolledInnerBlocks( props ) {
const {
clientId,
allowedBlocks,
__experimentalItemCallback: itemCallback,
template,
templateLock,
forwardedRef,
Expand Down Expand Up @@ -93,6 +94,7 @@ function UncontrolledInnerBlocks( props ) {
{ ...props }
ref={ forwardedRef }
rootClientId={ clientId }
__experimentalItemCallback={ itemCallback }
className={ classes }
/>
);
Expand Down Expand Up @@ -157,7 +159,11 @@ ForwardedInnerBlocks.DefaultBlockAppender = DefaultBlockAppender;
ForwardedInnerBlocks.ButtonBlockAppender = ButtonBlockAppender;

ForwardedInnerBlocks.Content = withBlockContentContext(
( { BlockContent } ) => <BlockContent />
( { BlockContent, __experimentalItemCallback } ) => (
<BlockContent
__experimentalItemCallback={ __experimentalItemCallback }
/>
)
);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function UncontrolledInnerBlocks( props ) {
marginHorizontal,
horizontalAlignment,
filterInnerBlocks,
__experimentalItemCallback,
} = props;

const block = useSelect(
Expand Down Expand Up @@ -82,6 +83,7 @@ function UncontrolledInnerBlocks( props ) {
onAddBlock={ onAddBlock }
onDeleteBlock={ onDeleteBlock }
filterInnerBlocks={ filterInnerBlocks }
__experimentalItemCallback={ __experimentalItemCallback }
/>
);

Expand Down
21 changes: 15 additions & 6 deletions packages/block-editor/src/components/use-block-drop-zone/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import { difference } from 'lodash';

/**
* WordPress dependencies
*/
Expand Down Expand Up @@ -50,11 +55,6 @@ export function getNearestBlockIndex( elements, position, orientation ) {
let candidateDistance;

elements.forEach( ( element, index ) => {
// Ensure the element is a block. It should have the `wp-block` class.
if ( ! element.classList.contains( 'wp-block' ) ) {
return;
}

const rect = element.getBoundingClientRect();
const cursorLateralPosition = isHorizontal ? y : x;
const cursorForwardPosition = isHorizontal ? x : y;
Expand Down Expand Up @@ -326,7 +326,16 @@ export default function useBlockDropZone( {

useEffect( () => {
if ( position ) {
const blockElements = Array.from( element.current.children );
// Get the root elements of blocks inside the element, ignoring
// InnerBlocks item wrappers and the children of the blocks.
const blockElements = difference(
Array.from( element.current.querySelectorAll( '.wp-block' ) ),
Array.from(
element.current.querySelectorAll(
':scope .wp-block .wp-block'
)
)
);
Comment on lines +329 to +338
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting that this may not be the fastest way to do this. Here's an alternative implementation that I've considered, but I have no idea if it would be faster or slower:

function getBlockElements( node ) {
	const list = [];

	for ( const childNode of node ) {
		if ( childNode.classList.contains( 'wp-block' ) ) {
			list.push( childNode );
		} else if ( childNode.children.length > 0 ) {
			const grandchildren = getBlockElements( childNode );
			if ( grandchildren.length > 0 ) {
				list.push( ...grandchildren );
			}
		}
	}

	return list;
}

const blockElements = getBlockElements( element.current );

Copy link
Member

@noisysocks noisysocks Aug 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably they're quite similar. When does this code run? Unless it's a very critical path (e.g. every keystroke, every block render) it's probably not worth worrying about too much.

You could always measure it using console.time().

console.time( 'getBlockElements' );
// Code goes here
console.endTime( 'getBlockElements' );


const targetIndex = getNearestBlockIndex(
blockElements,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,22 +83,6 @@ describe( 'getNearestBlockIndex', () => {
expect( result ).toBeUndefined();
} );

it( 'returns `undefined` if the elements do not have the `wp-block` class', () => {
const nonBlockElements = [
{ classList: createMockClassList( 'some-other-class' ) },
];
const position = { x: 0, y: 0 };
const orientation = 'horizontal';

const result = getNearestBlockIndex(
nonBlockElements,
position,
orientation
);

expect( result ).toBeUndefined();
} );

describe( 'Vertical block lists', () => {
const orientation = 'vertical';

Expand Down
46 changes: 39 additions & 7 deletions packages/blocks/src/api/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { isEmpty, reduce, isObject, castArray, startsWith } from 'lodash';
/**
* WordPress dependencies
*/
import { Component, cloneElement, renderToString } from '@wordpress/element';
import {
Component,
RawHTML,
cloneElement,
renderToString,
} from '@wordpress/element';
import { hasFilter, applyFilters } from '@wordpress/hooks';
import isShallowEqual from '@wordpress/is-shallow-equal';

Expand All @@ -21,10 +26,16 @@ import {
import { normalizeBlockType } from './utils';
import BlockContentProvider from '../block-content-provider';

/** @typedef {import('@wordpress/element').WPElement} WPElement */

/**
* @typedef {Object} WPBlockSerializationOptions Serialization Options.
*
* @property {boolean} isInnerBlocks Whether we are serializing inner blocks.
* @property {boolean} isInnerBlocks
* Whether we are serializing inner blocks.
* @property {WPElement} [__experimentalRenderCallback]
* Callback to define HTML surrounding block, outside of the comment
* delimiters. Used by InnerBlocks API.
*/

/**
Expand Down Expand Up @@ -307,20 +318,41 @@ export function getCommentDelimitedContent(
*
* @return {string} Serialized block.
*/
export function serializeBlock( block, { isInnerBlocks = false } = {} ) {
export function serializeBlock(
block,
{ isInnerBlocks = false, __experimentalRenderCallback: renderCallback } = {}
) {
const blockName = block.name;
const saveContent = getBlockContent( block );

// Serialized block content before wrapping it with an InnerBlocks item
// wrapper.
let unwrappedContent;

if (
blockName === getUnregisteredTypeHandlerName() ||
( ! isInnerBlocks && blockName === getFreeformContentHandlerName() )
) {
return saveContent;
unwrappedContent = saveContent;
} else {
const blockType = getBlockType( blockName );
const saveAttributes = getCommentAttributes(
blockType,
block.attributes
);
unwrappedContent = getCommentDelimitedContent(
blockName,
saveAttributes,
saveContent
);
}

const blockType = getBlockType( blockName );
const saveAttributes = getCommentAttributes( blockType, block.attributes );
return getCommentDelimitedContent( blockName, saveAttributes, saveContent );
if ( renderCallback ) {
return renderToString(
renderCallback( <RawHTML>{ unwrappedContent }</RawHTML> )
);
}
return unwrappedContent;
}

/**
Expand Down
7 changes: 5 additions & 2 deletions packages/blocks/src/block-content-provider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ const { Consumer, Provider } = createContext( () => {} );
* @return {WPComponent} Element with BlockContent injected via context.
*/
const BlockContentProvider = ( { children, innerBlocks } ) => {
const BlockContent = () => {
const BlockContent = ( { __experimentalItemCallback } ) => {
// Value is an array of blocks, so defer to block serializer
const html = serialize( innerBlocks, { isInnerBlocks: true } );
const html = serialize( innerBlocks, {
isInnerBlocks: true,
__experimentalRenderCallback: __experimentalItemCallback,
} );

// Use special-cased raw HTML tag to avoid default escaping
return <RawHTML>{ html }</RawHTML>;
Expand Down