Bug Scrub Schedule for 5.4

Now that 5.4 has been officially kicked off, bug scrubs will happen weekly all the way up to the final release. Keep an eye on this schedule – it will change often to reflect the latest information.

  1. 1/21/2020 19:00 UTC
  2. 1/29/2020 23:00 UTC
  3. 2/7/2020 05:00 UTC (APAC-Friendly)
  4. 2/10/2020 16:00 UTC
  5. 2/18/2020 20:00 UTC
  6. 2/27/2020 23:00 UTC
  7. 3/2/2020 18:00 UTC
  8. 3/11/2020 TBD (If Necessary)
  9. 3/17/2020 TBD (If Necessary)
  10. 3/27/2020 TBD (If Necessary)
  11. 3/30/2020 TBD (If Necessary)

These scrubs are separate and in addition to the normal scrubbing and triage by individual components. Some of those sessions include:

Design Triage: Every Monday 17:30 UTC at #design
Gutenberg Design Triage: Every Tuesday 17:00 UTC at #design
Accessibility Scrub: Every Friday 14:00 UTC at #accessibility

Also, the ongoing APAC-friendly #core bug scrub session every Thursday at 06:00 UTC will continue during the cycle, alternating focus between core and editor.

Next, the Accessibility team has announced a few extra scrubs for the 5.4 cycle. You can read about those here.

Finally, a reminder that anyone — Yes, you! — can host a bug scrub at anytime. You can work through any tickets you’re comfortable with. In fact, if you plan one that’s 5.4-focused, we’ll add it to the schedule here along with your name. Finally, you’ll get well deserved props in the weekly Dev Chat, as well as in the #props Slack channel!

All open tickets for 5.4, in order of priority, can be found here. Tickets that haven’t seen any love in a while are in particular need. Those can be found in this query.

If you’d like to lead a bug scrub or have any questions or concerns about the schedule, please leave a comment or reach out to me directly.

#5-4, #bug-scrub

XML Sitemaps Meeting: March 3rd, 2020

Another week passed by with quite a productive meeting for the XML Sitemaps feature project. Here’s a short summary, as well as the agenda for today’s meeting.

Meeting Recap: February 25th

In case you missed it, I recommend checking out last week’s post with everything that happened so far:

As planned, we went over some of the existing issues, but we also discussed some things that came up on short notice. Here’s the gist:

  • We reiterated on the idea to remove the lastmod field. @swissspidy offered to start a PR that explores this so it can actually be tested in the wild. @joemcgill offered to post some stats about the performance of this last modified date calculation.
  • There was a discussion, also after the meeting, about changing URLs to have a /wp- prefix and whether that prefix should be filterable. The consensus was that a filter is unnecessary. A new PR was added to implement this.
    @kraftbj offered his help to implement automatic redirects from /sitemap.xml to /wp-sitemap.xml for improved discoverability.
  • Next up was the SimpleXML dependency and how the plugin should behave when that PHP extension is missing.
    We tend towards just disabling sitemaps if that’s the case, but perhaps provide some messaging about it.
    @kraftbj offered to try to get some stats about the availability of SimpleXML via Jetpack, as well as to help with a PR.
    @pbiron reached out on the hosting community channel, and is looking for specific questions that we could ask in a make/hosting post.
  • Last but not least, there was an open question about leveraging the REST API for sitemaps. It was not fully clear though how that would be beneficial. As of now, there are no plans to explore this.

Agenda: March 3rd

The next meeting will be held on Tuesday, March 3 at 16.00 CET

This meeting is held in the #core-sitemaps channel , to join the meeting, you’ll need an account on the Making WordPress Slack.

#agenda, #feature-plugins, #feature-projects, #xml-sitemaps

Editor Chat Agenda: 4th March, 2020

Note taker: @jorgefilipecosta

This is the agenda for the weekly editor chat scheduled for 2020-03-04 14:00 UTC. This meeting is held in the #core-editor WordPress Slack channel.

  • WordPress 5.4 Upcoming Release
  • Weekly Priorities
  • Task Coordination
  • Open Floor

If you have anything to share for the Task Coordination section, please leave it as a comment on this post. If you have anything to propose for the agenda or other specific items related to those listed above, please leave a comment below.

#agenda#core-editor#editor-chat

New gradient theme APIs

WordPress 5.4 lets your theme use gradients as backgrounds in Cover and Buttons blocks, courtesy of some new theme APIs. You’ll notice they parallel the approaches of the colors and fonts API.

Configure the predefined set of gradients

Start by configuring a predefined set of gradients. Do that with the theme-support option editor-gradient-presets, then pass an array that represents the gradient set:

add_theme_support(
    'editor-gradient-presets',
    array(
        array(
            'name'     => __( 'Vivid cyan blue to vivid purple', 'themeLangDomain' ),
            'gradient' => 'linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)',
            'slug'     => 'vivid-cyan-blue-to-vivid-purple'
        ),
        array(
            'name'     => __( 'Vivid green cyan to vivid cyan blue', 'themeLangDomain' ),
            'gradient' => 'linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)',
            'slug'     =>  'vivid-green-cyan-to-vivid-cyan-blue',
        ),
        array(
            'name'     => __( 'Light green cyan to vivid green cyan', 'themeLangDomain' ),
            'gradient' => 'linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%)',
            'slug'     => 'light-green-cyan-to-vivid-green-cyan',
        ),
        array(
            'name'     => __( 'Luminous vivid amber to luminous vivid orange', 'themeLangDomain' ),
            'gradient' => 'linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%)',
            'slug'     => 'luminous-vivid-amber-to-luminous-vivid-orange',
        ),
        array(
            'name'     => __( 'Luminous vivid orange to vivid red', 'themeLangDomain' ),
            'gradient' => 'linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%)',
            'slug'     => 'luminous-vivid-orange-to-vivid-red',
        ),
    )
);

For more, see https://developer.wordpress.org/block-editor/developers/themes/theme-support/#block-gradient-presets.

Disable custom gradients

You can also disable custom gradients:

add_theme_support( 'disable-custom-gradients' );

This call limits your users to the default gradients you defined in the block editor or that you added to the editor-gradient-presets theme support setting.

For more, see https://developer.wordpress.org/block-editor/developers/themes/theme-support/#disabling-custom-gradients.

Disable gradient functionality

You can also use these APIs to shut down gradient functionality altogether. Just combine these two calls:

add_theme_support( 'disable-custom-gradients' );
add_theme_support( 'editor-gradient-presets', array() );

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

Markup and style-related changes

WordPress 5.4 makes several DOM structure changes on the block editor. Ideally, your code doesn’t depend on WordPress Core class names or a specific DOM, since class names and the DOM structure are not part of the WordPress public API.

Remove legacy “editor-” class name compatibility

WordPress 5.2 updated the CSS class names of a lot of the components in the block editor, changing editor- prefixes on those class names to block-editor-. Where the old class names still existed, largely in component references, the conventional wisdom urged developers to avoid those references or update them accordingly.

In WordPress 5.4, these old editor--prefixed class names are gone from components in the block-editor scripts. If you’re still referencing the editor-prefixed CSS class name of a component, you’ll need to update to the block-editor- equivalent.

Div with class edit-post-layout__content removed

The `edit-post-layout__content` div is gone from the DOM of the block editor. Check your custom styles and plugin files to make sure you’re not targeting it fir styling.

Blocks and rich text components lose redundant wrappers

Blocks and RichText have been refactored to simplify and lighten the React and DOM tree.

The key to this refactor: moving controls out of the elements and into adjacent popovers, which made four div wrappers redundant—so now they’re gone.

The result: significant performance improvement—and the editor-content DOM looks a lot more like the front end. So styling blocks is now a lot easier for both block and theme authors. Eventually, the plan is to let blocks render the very same tree in the editor as on the front end.

Here’s how those changes have affected the block-editor DOM:

The block-editor-rich-text class is now part of the editable element, unless you’re still using the wrapperClasses prop (which you should really stop using). If you really need a wrapper around the editable element, you’re better off creating your own.

The data-block attribute, containing the block ID, has relocated to the outer block wrapper element. The block-editor-block-list__block-edit class is gone completely; so is the div element. Selectors like .wp-block > .block-editor-block-list__block-edit > [data-block] won’t work anymore. Use .wp-block or just the [data-type] attribute instead.

Simpler block margins

 17877 changes the way the block editor lays out blocks. Before now, every block came with built-in padding, left and right, and negative margins to compensate.

Well, in a bid to radically simplify the CSS you need to style blocks, to develop blocks, to build themes and style the editor in your themes, the built-in padding and negative margins are refactored out of existence—they’re GONE.

Now, if your current block or editor styles have been compensating for those previous margins/paddings, they might look a little off in version 5.4. If so, try getting rid of the styles you wrote to compensate.

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

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

Privacy Updates in 5.4

WordPress 5.4 brings several improvements to the privacy tools to improve the user experience and expand upon the data provided in personal data exports.

Personal Data Export now includes Session Tokens, Community Events Location and Custom User Meta.

In WordPress 5.4 the Personal Data exports were expanded upon to ensure the Personally Identifiable Information (PII) present in Session Tokens (#45889) and the Community Events Location (#43921) user data were made available to the exporting user. This data is made available in the export as custom group sections.

Session Tokens grouping
Community Events Location grouping

Along with the new groupings which will automatically be included in the export if the relevant data is available, developers can now expand upon the User profile data grouping through the use of the new wp_privacy_additional_user_data filter.

// Privacy Filter for adding additional user meta to personal data exports.
function my_custom_additional_user_profile_data( $additional_profile_data, $user, $reserved_names ) {
	return array(
		array(
			'name'  => __( 'Data one', 'a-plugin' ),
			'value' => 'one',
		),
		array(
			'name'  => __( 'Data two', 'a-plugin' ),
			'value' => 'two',
		),
		array(
			'name'  => __( 'Data three', 'a-plugin' ),
			'value' => 'three',
		),
		array(
			'name'  => __( 'Data four', 'a-plugin' ),
			'value' => 'four',
		),
	);
}
add_filter( 'wp_privacy_additional_user_profile_data', 'my_custom_additional_user_profile_data', 10, 3 );

Note: The $reserved_names array is supplied to the filter to assist developers in avoiding using these names in their array of additional data. This is due to any additional data matching these names will be suppressed from the export to avoid a conflict with the existing user profile data to be exported.

See #47509

Personal Data Exports now include a JSON file and a Table of Contents

Along with including additional data in the Personal Data Exports the export zip will now contain a JSON file (#49029) of the data for better portability. This JSON file will contain all of the data present in the HTML file with the exception of the table of contents.

The HTML export file has been updated to include a Table of Contents (#46894) for easier navigation of large data exports.

Visual Improvements to the Privacy Tools tables

In WordPress 5.4 the Privacy Tools tables have been updated to give progress indicators (#44264) for both the export and erasure processes. As well as switched the ‘Next Steps’ buttons to links (#49323) for a cleaner interface.

New filters for the headers of all Privacy-related emails

In WordPress 5.4 developers are now able to filter the email headers on privacy related emails. For example, this will enable developers to change the “From” email address. These headers and an example are listed below;

  • wp_privacy_personal_data_email_headers
  • user_request_action_email_headers
  • user_request_confirmed_email_headers
  • user_erasure_complete_email_headers
// Privacy filter for setting the From name/email on privacy emails.
function my_privacy_mail_headers( $headers, $subject, $content, $request_id, $email_data ) {
	$headers = array(
		'From: My Name <myname@example.com>',
	);
	return $headers;
}
add_filter( 'wp_privacy_personal_data_email_headers', 'my_privacy_mail_headers', 10, 5 );
add_filter( 'user_request_action_email_headers', 'my_privacy_mail_headers', 10, 5 );
add_filter( 'user_request_confirmed_email_headers', 'my_privacy_mail_headers', 10, 5 );
add_filter( 'user_erasure_complete_email_headers', 'my_privacy_mail_headers', 10, 5 );

See #44501

Renamed Function for Clarity

In WordPress 5.4 the wp_get_user_request_data function was renamed to wp_get_user_request for function clarity. This is due to the function returning the actual WP_User_Request object and not the data parameter which is part of the request object. The old function signature will now produce a _doing_it_wrong warning message.

See #46302

#5-4, #core-privacy, #dev-notes, #privacy

REST API Changes in 5.4

Taxonomy “OR” Relation Now Supported in Posts Controller

Querying for /wp/v2/posts?tags=1&categories=2 returns all posts assigned the tag with ID 1, AND assigned the category with ID 2. This AND relationship, where multiple taxonomies’ term relationships must all be satisfied, has been the only supported behavior in these collection endpoints since WordPress 4.7.

The REST API /wp/v2/posts endpoint, as well as custom post type endpoints extending from WP_REST_Posts_Controller (including custom post types specifying "show_in_rest" => true), now supports a new tax_relation parameter which can be used to return posts matching either taxonomy filter, rather than both.

As an example, in WordPress 5.4, the posts endpoint query

/wp/v2/posts?tags=1&categories=2&tax_relation=OR

will now return posts in either the tag ID 1 or the category with ID 2.

Selective Link Embedding

The REST API now supports returning a limited set of embedded objects using the _embed parameter. As an example, in WordPress 5.4, the following query only embeds the author information instead of including all the comments, media, etc…

/wp/v2/posts/?_embed=author

All embeds will be returned if a value for the _embed parameter is omitted, or set to true or 1.

WP_REST_Server method changes

WordPress 5.4 changes the signature of two methods in the WP_REST_Server class. Developers who are extending WP_REST_Server and overriding these methods should update their code to match the new signatures to avoid PHP warnings.

  1. The signature of WP_REST_Server::embed_links() is now embed_links( $data, $embed = true ). The new $embed paramter accepts an array of link relations (such as array( 'author', 'wp:term' )) and limits the embedded links in the response to those relations. The default of true maintains the previous behavior of embedding all links in the response. For more details, see #39696.
  2. The signature of WP_REST_Server::get_routes() is now get_routes( $namespace = '' ). The new $namespace parameter accepts a string and limits the returned routes to those whose namespace matches the string. Internally, WP_REST_Server uses this new parameter to improve the performance of WP_REST_Server::dispatch() by reducing the number of regex checks necessary to match a request to a registered route. For more details, see #48530.

For performance reasons, WP_REST_Server::embed_links() also now caches response data in memory. This cache is managed by WP_REST_Server::response_to_data(). Code calling the protected embed_links method being called directly may need to be updated to ensure stale data is not returned.

(Thank you to @dlh for authoring this section)

See the full list of REST API changes on Trac.

#5-4, #dev-notes, #rest-api

New @wordpress/create-block package for block scaffolding

Block scaffolding is a shorthand term that describes the supporting directory structure you need for WordPress to recognize a block. Typically that directory includes files like index.php, index.js, style.css and perhaps others—which in turn hold calls like register_block_type.

You’ve likely noticed a lot of tools that aim to automate the scaffolding build—or at least make it easier to build your own blocks. They’re nothing new in the WordPress ecosystem. And there’s even one in WP-CLI: the wp scaffold block command.

You’ve probably also noticed that some of those third-party tools get you up and running with just one npx command, while WP-CLI still needs a full WordPress installation to run. And WP-CLI doesn’t play well with advanced JavaScript tooling like code transpiling, linting or formatting.

Until now.

WordPress 5.4 brings you a brand-new, officially supported npm-based scaffolding package.

Create Block generates PHP, JS and CSS code—and everything else you need to start building your WordPress plugin. Its quick-start mode lets you pass it a slug that then becomes both the target location for the generated files AND the internal block name:

$ npm init @wordpress/block todo-list
wordpress-create-block

You don’t need to install or configure tools like webpack, Babel or ESLint yourself. They’re preconfigured and out of sight. So you can focus on the code.

Create Block also has an interactive mode that lets you customize several essential block options before code generation:

$ npm init @wordpress/block

Finally, despite its support for modern JavaScript tooling, Create Block also lets you to pick an ES5 template—and skip the transpiling tools that trigger ESNext and JSX support.

Note: If you’re concerned about backwards compatibility, the new tool comes straight from the existing WP-CLI implementation—and in particular, the optional ES5 template uses the existing ES5 code.

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

WP Notify weekly meeting agenda for Monday 2 March 2020

This is the agenda for the next WP Notify feature project meeting, to be held today, Monday, March 2, 2020, 14:00 UTC.

Agenda

  • Opening and welcome
  • Reviewing work done on Current Status with a view to mark it complete
  • Reviewing and updating the requirements document: Project Goal
  • Reviewing and updating the requirements document: Overall Description
  • Open floor

If you have anything else to propose for the agenda or specific items related to those listed above, please leave a comment below.

This meeting is held in the #feature-notifications channel , to join the meeting, you’ll need an account on the Making WordPress Slack.

#agenda, #feature-notifications

Block Collections

Collections allow specific block types to be grouped together for added visibility in the editor’s Inserter menu, regardless of their categories. For instance, a plugin such as CoBlocks may register a Map block of category Widgets and a Post Carousel block of category Layout. By registering a collection for the coblocks namespace, these blocks will appear in the Inserter under their respective categories but also grouped under a CoBlocks collection.

The Block Collections API thus aims to improve the relationship of block discovery between users and plugin authors without compromising the semantics of block types, notably around categories (e.g. formatting, layout, widget, embed).

registerBlockCollection( 'coblocks', {
	title: 'CoBlocks',
	icon: brandAssets.categoryIcon,
} );

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