-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Block Editor: Ensure that blockType
is defined when accessing apiVersion
#34346
Conversation
@@ -111,11 +111,11 @@ function BlockListBlock( { | |||
|
|||
const blockType = getBlockType( name ); | |||
const lightBlockWrapper = | |||
blockType.apiVersion > 1 || | |||
blockType?.apiVersion > 1 || | |||
hasBlockSupport( blockType, 'lightBlockWrapper', false ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ellatrix, does lightBlockWrapper
handling exist only for backward compatibility? I don't see it used with core blocks anymore. I see it referenced in multiple places in the block editor package. Could we set apiVersion
to 2
in a hook during registration to simplify all those checks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's for backward compatibility.
Could we set apiVersion to 2 in a hook during registration to simplify all those checks?
What do you mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
registerBlockType
filter could detect if there is lightBlockWrapper
set in supports
and when it's true
then inject apiVersion
with the version 2
. Would it be compatible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, have we considered updating the selector for getBlockType
to return some default not found type, or do we depend on undefined
?
Normally we should return the core/missing
block type at this point in block list, but it is possible to unregister the block type. Presumably someone can also break it by updating setUnregisteredTypeHandlerName
to a nonsense value.
unregister.missing.mp4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, have we considered updating the selector for getBlockType to return some default not found type, or do we depend on undefined?
That would be a breaking change. In the current setup setUnregisteredTypeHandlerName
is optional for the blocks registry. In practice, it might unconsciously become mandatory. The challenge is that @wordpress/blocks
can't provide some fallback blocks like the default one, the grouping one, freeform content, and the unregistered type. Maybe that should be the responsibility of the @wordpress/block-editor
package to define some of those handlers so unregistering a given handler would rollback to the built-in one, or it wouldn't take an effect. We could alternatively refactor code to take into account that those handlers might get unset or the corresponding block type can be removed. @youknowriad and @mtias should know better what was the initial design here and what would be the best way to make it bugs free.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense to avoid a breaking change. If we want to make this a little nicer for callers, one alternative would be to return a default unknown type in the case that the unregistered type handler name is not configured (which supports no properties/has no attributes) in a new selector, say getBlockTypeOrUnknownBlock
. In its implementation might simply be something like: return getBlockType( type ) ?? UNKNOWN_BLOCK_TYPE
. It wouldn't actually be registered, since I think it's fair to add that level of customization.
I think the current design is fine too provided that all callers know that undefined
may be returned from getBlockType
. This is easy enough to search for in core usage. We're missing some other usages where we blindly pass blockType
to another selector, and that selector does not check for undefined. There's also a few spots in code where we also assume that 'core/missing' is registered.
We can of course guard these in follow up PRs:
gutenberg/packages/block-directory/src/plugins/get-install-missing/install-button.js
Lines 27 to 39 in 35e16bb
const blockType = getBlockType( block.name ); | |
const [ originalBlock ] = parse( | |
attributes.originalContent | |
); | |
if ( originalBlock ) { | |
replaceBlock( | |
clientId, | |
createBlock( | |
blockType.name, | |
originalBlock.attributes, | |
originalBlock.innerBlocks | |
) | |
); |
const blockType = getBlockType( block.name ); | |
const attributes = getBlockAttributes( | |
blockType, | |
html, | |
block.attributes | |
); | |
// If html is empty we reset the block to the default HTML and mark it as valid to avoid triggering an error | |
const content = html ? html : getSaveContent( blockType, attributes ); |
gutenberg/packages/block-editor/src/components/block-list/block.native.js
Lines 321 to 323 in 35e16bb
const blockType = getBlockType( name || 'core/missing' ); | |
const title = blockType.title; | |
const icon = blockType.icon; |
gutenberg/packages/block-editor/src/components/block-parent-selector/index.js
Lines 41 to 52 in 35e16bb
const _parentBlockType = getBlockType( parentBlockName ); | |
const settings = getSettings(); | |
return { | |
firstParentClientId: _firstParentClientId, | |
shouldHide: ! hasBlockSupport( | |
_parentBlockType, | |
'__experimentalParentSelector', | |
true | |
), | |
hasReducedUI: settings.hasReducedUI, | |
}; | |
}, |
gutenberg/packages/block-editor/src/components/block-settings-menu/block-mode-toggle.js
Line 28 in 35e16bb
! hasBlockSupport( blockType, 'html', true ) || |
gutenberg/packages/block-editor/src/components/block-switcher/block-styles-menu.js
Line 36 in 35e16bb
blockType.example |
blockTitle: getBlockType( firstBlockName ).title, |
const label = reusableBlockTitle || getBlockLabel( blockType, attributes ); |
gutenberg/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js
Lines 41 to 47 in 35e16bb
showParentSelector: | |
hasBlockSupport( | |
parentBlockType, | |
'__experimentalParentSelector', | |
true | |
) && selectedBlockClientIds.length <= 1, | |
}; |
gutenberg/packages/block-editor/src/components/block-tools/block-selection-button.js
Lines 79 to 85 in 35e16bb
const blockType = getBlockType( name ); | |
const label = getAccessibleBlockLabel( | |
blockType, | |
attributes, | |
index + 1, | |
orientation | |
); |
const { title } = getBlockType( getBlockName( clientId ) ); |
{ isReusable || hoveredItemBlockType.example ? ( |
gutenberg/packages/block-editor/src/components/inserter/hooks/use-clipboard-block.native.js
Line 29 in 35e16bb
const { icon, name } = getBlockType( clipboardBlock.name ); |
<BlockIcon icon={ blockType.icon } showColors /> |
: getBlockLabel( blockType, block.attributes ) } |
gutenberg/packages/block-editor/src/hooks/align.js
Lines 134 to 135 in 35e16bb
const blockType = getBlockType( props.name ); | |
const blockDefaultAlign = blockType.attributes?.align?.default; |
const attributeDefinition = selectedBlockType.attributes[ attributeKey ]; |
gutenberg/packages/blocks/src/api/factory.js
Lines 290 to 291 in 35e16bb
const blockType = getBlockType( sourceBlock.name ); | |
const transformsTo = getBlockTransforms( 'to', blockType.name ); |
https://github.com/WordPress/gutenberg/blob/trunk/packages/blocks/src/api/serializer.js#L346-L347
gutenberg/packages/blocks/src/api/utils.js
Lines 53 to 56 in 35e16bb
const blockType = getBlockType( defaultBlockName ); | |
return every( | |
blockType.attributes, |
gutenberg/packages/blocks/src/api/raw-handling/shortcode-converter.js
Lines 95 to 98 in 35e16bb
const transformationBlockType = { | |
...getBlockType( transformation.blockName ), | |
attributes: transformation.attributes, | |
}; |
gutenberg/packages/blocks/src/store/selectors.js
Lines 128 to 129 in 35e16bb
const blockType = getBlockType( state, blockName ); | |
const attributeKeys = Object.keys( blockType.attributes || {} ); |
<strong>{ blockType.title }: </strong> |
? getBlockLabel( getBlockType( block.name ), block.attributes ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does
lightBlockWrapper
handling exist only for backward compatibility? I don't see it used with core blocks anymore. I see it referenced in multiple places in the block editor package. Could we setapiVersion
to2
in a hook during registration to simplify all those checks?
I opened PR #34459 that removes usage of lightBlockWrapper
.
Size Change: +15 B (0%) Total Size: 1.04 MB
ℹ️ View Unchanged
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @gziolo. Going to approve this one since the guards here make sense 👍. There are other usages we should bulletproof for blockType (see https://github.com/WordPress/gutenberg/pull/34346/files#r699496884) though some of them will be harder to reach.
I think we should file an issue with a list and ask for help to address them eventually. One thing that would help is TypeScript validation, but I guess we are far from getting coverage for |
Description
Related to #32815.
This PR tries to minimize the chances to see the following error:
How has this been tested?
I'm not sure if it is possible to validate with manual testing.
Types of changes
Code quality.
Checklist:
*.native.js
files for terms that need renaming or removal).