General Block Editor API Updates

Meta attribute sources deprecated in 5.4

WordPress 5.4 deprecates meta attribute sources.

Your existing code that uses these attributes should still work, but there’s a new way to get where you want to go.

Instead of meta attributes, use EntityProvider and related hooks APIs. EntityProvider and related hooks APIs provide a more flexible and powerful way to build blocks that source data from a variety of properties of WordPress entities and data.

Here’s how your block’s objects can permit reading and writing to the meta of a post:

const [meta, setMeta] = useEntityProp('postType', 'post', 'meta')

Shortcode transforms: Support isMatch predicate

To bring shortcodes in line with other types of block transformations, you can add an optional isMatch function to see if a given shortcode should transform into a specific block.

For instance, this hypothetical Antarctica Weather block only cares about [weather] shortcodes for Antarctica:

transforms: {
    from: [
        {
            type: 'shortcode',
            tag: 'weather',
            isMatch( { named: { latitude } } ) {
                return parseInt( latitude, 10 ) < -70;
            },
            attributes: {
                latitude: {
                    type: 'number',
                    shortcode: ( { named: { latitude } } ) =>
                        parseInt( latitude, 10 ),
                },
                longitude: {
                    type: 'number',
                    shortcode: ( { named: { longitude } } ) =>
                        parseInt( longitude, 10 ),
                },
            },
        },
    ],
},

If isMatch returns false, the shortcode won’t become an Antarctica Weather block. At that point, another block type can pick it up (presumably, one that matches the [weather] shortcode), or it can stay a shortcode and get encapsulated in a Shortcode block.

New AsyncModeProvider API

Because nobody wants laggy typing in the Editor, the BlockEditor uses an Async Rendering Mode: The selected block gets rerendered synchronously on each change—while the unselected blocks only refresh when the browser goes idle (i.e., while it’s not actively doing some task).

That behavior comes courtesy of the Async Mode, implemented in the wordpress/data package.

In WordPress 5.4, you can use that same sort of asynchronous behavior to speed things up in your own React state trees—as long as they rely on the data module.

That’s because version 5.4 puts the AsyncModeProvider component where you can find it and use it — or, conversely, not use it, since you can also use it to opt out of the block async rendering mode.

import { AsyncModeProvider } from '@wordpress/data';
const MyComponent = () => {
  return (
    <>
        The following component updates synchronously on data store changes
        <MySyncComponent />
        <AsyncModeProvider value={ true }>
            The following component updates asynchronously on data store changes
            <MyAsyncComponent />
        </AsyncModeProvider>
    </>
  );
}

For more about the AsyncMode, you can check this blog post.

A custom media upload handler in a block editor. In a SETTING!

Did you know? You can use the wordpress/block-editor package by itself, to add block-editor pages just about anywhere. Use it for custom WPAdmin pages or even in a completely WordPress-agnostic context.

Here’s an example from the Gutenberg Playground. In a situation like this, WordPress 5.4 lets you add a custom media-upload handler to the block editor—as a setting! (One of your users probably wants this right now.)

import { BlockEditorProvider } from '@wordpress/block-editor';

/**
 * Media Upload Handler
 *
 * @param   {Object}   $0                   Parameters object passed to the function.
 * @param   {?Object}  $0.additionalData    Additional data to include in the request.
 * @param   {string}   $0.allowedTypes      Array with the types of media that can be uploaded, if unset all types are allowed.
 * @param   {Array}    $0.filesList         List of files.
 * @param   {?number}  $0.maxUploadFileSize Maximum upload size in bytes allowed for the site.
 * @param   {Function} $0.onError           Function called when an error happens.
 * @param   {Function} $0.onFileChange      Function called each time a file or a temporary representation of the file is available.
 */
const myMediaUploadHandler = ( settings ) => {
   const mediaObject = {
      id, alt, caption, title, url,
   };    

    settings.onFileChange([mediaObject]);
}

const MyCustomEditor = () => {
  return (
        <BlockEditorProvider settings={ { mediaUpload: myMediaUploadHandler  } }>
            <MyEditorLayout />
        </BlockEditorProvider>
  );
}

Now, realize this: if you leave the mediaUpload handler out of your BlockEditor instance, your editor won’t support media upload at all. Or, it might not let the current user upload media with their current permissions.

You can also give Media Blocks access to this setting in their edit functions, so they can support uploads.

const MyBlockEdit = () => {
   const mediaUpload = useSelect( ( select ) => {
      return select( 'core/block-editor' ).getSettings().mediaUpload;
   }, [] );

   return (
      <>
           { !! mediaUpload && <MyMediaUploadComponent onUpload={ mediaUpload } /> }
       </>
   );
   <
}

Easier drag-and-drop

Do you get complaints about drag-and-drop being more like swing-and-miss? In WordPress 5.4, you can listen to sweet, sweet silence.

That’s because the positioning classes that rendered in the DropZone component (is-close-to-topis-close-to-bottomis-close-to-left and is-close-to-right) are GONE.

In fact, the drop zone is gone. So users have a much bigger target to grab in the blocks they want to move. Easier grab, easier drag, happier users. And you’re the hero.

And with the exit of the drop zone, the editor.BlockDropZone component filter is also gone. That filter was originally supposed to filter media uploads that happened by drag-and-drop— but it didn’t seem to be doing the job well.

If you’d been using that filter, and its removal is a problem, please leave a comment on this note. If there’s enough demand, it’s possible a filter like it, focused on a broader media-upload use case, could emerge from the discussion.

RichText: don’t set focus when applying format

When you’re formatting text in a rich-text instance, does it annoy you to see the focus go back automatically to the editable element? Do your users complain about that behavior?

Well, get happy! In support of more complex UI, to give your users better control of rich-text instances, (e.g. a color control: who wants to have to click back into the color UI for every color change?) the focus will stay where you (or your users) are working until all the color changes, or whatever, are DONE.

Of course, that means that if you do want to keep setting the focus back, say, after a button click, you’ll have to actively make that happen when you’re registering the format type.

You’ll want to do that with the new onFocus function for the edit component.

For example, here’s how that works with the bold format button: https://github.com/WordPress/gutenberg/blob/c5eb9626dc1c73ad9bc543a5d171e9ab4a840996/packages/format-library/src/bold/index.js#L21-L53

New Guide component

A new Guide component was added to the @wordpress/components package (wp.components.Guide). Guide allows developers to easily create a step-by-step guide to display to users. The block editor uses Guide to implement a new welcome modal which appears on first launch.

Guide is a React component that renders a user guide in a modal. The guide consists of several GuidePage components which the user can step through one by one. The guide is finished when the modal is closed or when the user clicks Finish on the last page of the guide.

function MyTutorial() {
    const [ isOpen, setIsOpen ] = useState( true );

    if ( ! isOpen ) {
        return null;
    }

    <Guide onFinish={ () => setIsOpen( false ) }>
        <GuidePage>
            <p>Welcome to the ACME Store! Select a category to begin browsing our wares.</p>
        </GuidePage>
        <GuidePage>
            <p>When you find something you love, click <i>Add to Cart</i> to add the product to your shopping cart.</p>
        </GuidePage>
    </Guide>
}

Deprecation of wordpress/nux

The @wordpress/nux package (wp.nux) has been deprecated along with the DotTip component that it contained. Importing the package will display a warning in the browser console. It is recommended that plugins using wp.nux and DotTip migrate to Guide instead.

#5-4, #block-editor, #dev-notes