-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Block Editor: Ensure that blockType is defined when accessing apiVersion
#34346
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
Conversation
| 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
lightBlockWrapperhandling 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 setapiVersionto2in 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
|
gwwar
left a comment
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.jsfiles for terms that need renaming or removal).