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

Display children of inner block controllers in the block navigator #24083

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,15 @@ then the nested template part's child blocks will not be returned. This way,
the template block itself is considered part of the parent, but the children
are not.

You can override this behavior with the includeControlledInnerBlocks setting.
So if you call `getBlock( TP, { WPGetBlockSettings: true } )`, it will return
all nested blocks, including all child inner block controllers and their children.

_Parameters_

- _state_ `Object`: Editor state.
- _clientId_ `string`: Block client ID.
- _settings_ `?WPGetBlockSettings`: A settings object.

_Returns_

Expand Down Expand Up @@ -277,7 +282,8 @@ _Returns_

Returns all block objects for the current post being edited as an array in
the order they appear in the post. Note that this will exclude child blocks
of nested inner block controllers.
of nested inner block controllers unless the `includeControlledInnerBlocks`
setting is set to true.

Note: It's important to memoize this selector to avoid return a new instance
on each call. We use the block cache state for each top-level block of the
Expand All @@ -289,6 +295,7 @@ _Parameters_

- _state_ `Object`: Editor state.
- _rootClientId_ `?string`: Optional root client ID of block list.
- _settings_ `?WPGetBlockSettings`: A settings object.

_Returns_

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,11 @@ export default compose(
} = select( 'core/block-editor' );
const selectedBlockClientId = getSelectedBlockClientId();
return {
rootBlocks: getBlocks(),
rootBlocks: getBlocks( '', { includeControlledInnerBlocks: true } ),
Copy link
Contributor

Choose a reason for hiding this comment

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

Calling selectors with inline objects is never a good idea in terms of performance, not sure it's the source of the issue but it's definitely something to avoid in general (breaks memoization)

rootBlock: selectedBlockClientId
? getBlock(
getBlockHierarchyRootClientId( selectedBlockClientId )
getBlockHierarchyRootClientId( selectedBlockClientId ),
{ includeControlledInnerBlocks: true }
)
: null,
selectedBlockClientId,
Expand Down
43 changes: 32 additions & 11 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ import { Platform } from '@wordpress/element';
* text value. See `wp.richText.create`.
*/

/**
* Settings which can be passed to the getBlock or getBlocks selectors.
*
* @typedef {Object} WPGetBlockSettings
* @property {boolean} includeControlledInnerBlocks If true, include nested child
* blocks of inner block controllers.
* The default of false excludes
* nested blocks of inner block controllers.
*/

// Module constants
const MILLISECONDS_PER_HOUR = 3600 * 1000;
const MILLISECONDS_PER_DAY = 24 * 3600 * 1000;
Expand Down Expand Up @@ -133,13 +143,18 @@ export function getBlockAttributes( state, clientId ) {
* the template block itself is considered part of the parent, but the children
* are not.
*
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* You can override this behavior with the includeControlledInnerBlocks setting.
* So if you call `getBlock( TP, { WPGetBlockSettings: true } )`, it will return
* all nested blocks, including all child inner block controllers and their children.
*
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* @param {?WPGetBlockSettings} settings A settings object.
*
* @return {Object} Parsed block object.
*/
export const getBlock = createSelector(
( state, clientId ) => {
( state, clientId, { includeControlledInnerBlocks = false } = {} ) => {
const block = state.blocks.byClientId[ clientId ];
if ( ! block ) {
return null;
Expand All @@ -148,9 +163,13 @@ export const getBlock = createSelector(
return {
...block,
attributes: getBlockAttributes( state, clientId ),
innerBlocks: areInnerBlocksControlled( state, clientId )
? EMPTY_ARRAY
: getBlocks( state, clientId ),
innerBlocks:
! includeControlledInnerBlocks &&
areInnerBlocksControlled( state, clientId )
? EMPTY_ARRAY
noahtallen marked this conversation as resolved.
Show resolved Hide resolved
: getBlocks( state, clientId, {
includeControlledInnerBlocks,
} ),
Copy link
Contributor

Choose a reason for hiding this comment

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

This call also breaks memoization as it will recompute getBlocks on each call since we're passing and inline object.

};
},
( state, clientId ) => [
Expand Down Expand Up @@ -184,23 +203,25 @@ export const __unstableGetBlockWithoutInnerBlocks = createSelector(
/**
* Returns all block objects for the current post being edited as an array in
* the order they appear in the post. Note that this will exclude child blocks
* of nested inner block controllers.
* of nested inner block controllers unless the `includeControlledInnerBlocks`
* setting is set to true.
*
* Note: It's important to memoize this selector to avoid return a new instance
* on each call. We use the block cache state for each top-level block of the
* given clientID. This way, the selector only refreshes on changes to blocks
* associated with the given entity, and does not refresh when changes are made
* to blocks which are part of different inner block controllers.
*
* @param {Object} state Editor state.
* @param {?string} rootClientId Optional root client ID of block list.
* @param {Object} state Editor state.
* @param {?string} rootClientId Optional root client ID of block list.
* @param {?WPGetBlockSettings} settings A settings object.
*
* @return {Object[]} Post blocks.
*/
export const getBlocks = createSelector(
( state, rootClientId ) => {
( state, rootClientId, { includeControlledInnerBlocks = false } = {} ) => {
return map( getBlockOrder( state, rootClientId ), ( clientId ) =>
getBlock( state, clientId )
getBlock( state, clientId, { includeControlledInnerBlocks } )
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here.

);
},
( state, rootClientId ) =>
Expand Down