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

Block API: Support optional block category #22192

Closed
wants to merge 5 commits into from

Conversation

aduth
Copy link
Member

@aduth aduth commented May 7, 2020

Related (partially extracted from): #19279

This pull request seeks to allow for the omission of a category for a registered block type. The effort at #19279 has demonstrated a need to be able to anticipate cases where block types may be registering to categories which do not exist. The requirement of a block category can be seen as unnecessarily restrictive, especially in contrast with many other block settings which are not required (icon, etc), and in observance of the many unit tests which were difficult to implement due to the need to satisfy this arbitrary requirement.

Note: This is being submitted as "In Progress" in order to invite early feedback. It is largely functionally complete, but implementation details may be subject to change.

I considered that there are many ways that this could be approached:

  • "Uncategorized" could be assigned at the time of block registration, so that a block type would always have a category assigned.
    • Downside: There may be value in being able to identify blocks which have no category assigned in differentiating on the value type of the category (i.e. undefined vs. string), which would not be possible if we forcibly assign a category.
    • Downside: Technically, the set of block categories can change over time. Assigning a default category during registration would not be durable to these sorts of changes, as it would only remain valid as to the default category at the time of registration. By contrast, with the current proposed implementation, combining the absence of a category and the availability of a dedicated "default category" state allows for more robustness to this scenario.
  • "Uncategorized" could at least be merged into block type as part of a getBlockType selector.
    • This is slightly better, but still suffers mostly from the problem of not being able to differentiate category assignment by type.
  • As categories are already objects (slug, title), a "default" category could be assigned by a separate property isDefault, so that there wouldn't need to be separate state between categories and defaultCategory.
    • Downside: In considering strong typing, ideally isDefault would always be assigned as a boolean, which becomes cumbersome to either normalize or require from a consumer to always pass isDefault: false for every non-default category.
    • Downside: There's already strong precedent for this sort of separation within the same store. See also: defaultBlockName.
  • "Uncategorized" may not need to be an actual block category, but instead handled in user interfaces where absence of category is handled.
    • This may actually be good! I'm only now considering it as I write the pull request comment. I do foresee a couple minor downsides.
    • Downside: It becomes a risk for duplication and syncing issues if each handling of the "Uncategorized" behavior needs to behave consistently. For example, both the Block Inserter and Block Manager would separately need to implement a (hopefully consistent) "Uncategorized" label. "Hoping" is always a risk, and we could avoid it by treating the default category as a specially-handled case.

Implementation Notes:

Currently, the proposed implementation is as follows:

  • "Uncategorized" is added as a new block type category.
  • "Default category" becomes a new state of core/blocks, and can be selected by getDefaultCategory
  • Block types can be registered without a category, and are left as category: undefined
  • It's up to the UI implementations to handle category: undefined as intending to map to the default category

@aduth aduth added [Feature] Block API API that allows to express the block paradigm. [Status] In Progress Tracking issues with work in progress labels May 7, 2020
Comment on lines +216 to +220
itemsPerCategory[
category === defaultCategory
? undefined
: category.slug
];
Copy link
Member Author

Choose a reason for hiding this comment

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

This works, but it's a bit strange, because it's assuming there's a string key undefined in itemsPerCategory, i.e.

itemsPerCategory = {
   // ...
   undefined: [ /* ... */ ],
};

- `Array<Object>`: Block categories.
- `Array<WPBlockTypeCategory>`: Block categories.

<a name="getCategory" href="#getCategory">#</a> **getCategory**
Copy link
Member Author

Choose a reason for hiding this comment

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

Should probably be consistent here with getDefaultCategory, if it's kept in the implementation. There was some prior concern about including this at all, since these singleton methods are discouraged. In any case, they should either both be included, or both be omitted, depending if the current implementation stays.

Comment on lines 207 to 208
'category' in settings &&
! some( select( 'core/blocks' ).getCategories(), {
Copy link
Member Author

Choose a reason for hiding this comment

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

I guess to support the use-case described at #19279 (comment), we probably want to make sure that if a block type attempts to register with a category which doesn't actually exist, it should be treated the same as if it were not assigned a category at all. Rather than as it exists here currently, where an error will occur.

@github-actions
Copy link

github-actions bot commented May 7, 2020

Size Change: +152 B (0%)

Total Size: 824 kB

Filename Size Change
build/block-editor/index.js 102 kB +26 B (0%)
build/blocks/index.js 48.2 kB +93 B (0%)
build/edit-post/index.js 28.1 kB +33 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.02 kB 0 B
build/annotations/index.js 3.62 kB 0 B
build/api-fetch/index.js 4.08 kB 0 B
build/autop/index.js 2.82 kB 0 B
build/blob/index.js 620 B 0 B
build/block-directory/index.js 6.61 kB 0 B
build/block-directory/style-rtl.css 760 B 0 B
build/block-directory/style.css 761 B 0 B
build/block-editor/style-rtl.css 10.3 kB 0 B
build/block-editor/style.css 10.3 kB 0 B
build/block-library/editor-rtl.css 7.08 kB 0 B
build/block-library/editor.css 7.08 kB 0 B
build/block-library/index.js 115 kB 0 B
build/block-library/style-rtl.css 7.28 kB 0 B
build/block-library/style.css 7.29 kB 0 B
build/block-library/theme-rtl.css 683 B 0 B
build/block-library/theme.css 685 B 0 B
build/block-serialization-default-parser/index.js 1.88 kB 0 B
build/block-serialization-spec-parser/index.js 3.1 kB 0 B
build/components/index.js 180 kB 0 B
build/components/style-rtl.css 17 kB 0 B
build/components/style.css 16.9 kB 0 B
build/compose/index.js 6.66 kB 0 B
build/core-data/index.js 11.4 kB 0 B
build/data-controls/index.js 1.29 kB 0 B
build/data/index.js 8.44 kB 0 B
build/date/index.js 5.47 kB 0 B
build/deprecated/index.js 772 B 0 B
build/dom-ready/index.js 568 B 0 B
build/dom/index.js 3.1 kB 0 B
build/edit-navigation/index.js 4.4 kB 0 B
build/edit-navigation/style-rtl.css 618 B 0 B
build/edit-navigation/style.css 617 B 0 B
build/edit-post/style-rtl.css 12.2 kB 0 B
build/edit-post/style.css 12.2 kB 0 B
build/edit-site/index.js 12.2 kB 0 B
build/edit-site/style-rtl.css 5.21 kB 0 B
build/edit-site/style.css 5.21 kB 0 B
build/edit-widgets/index.js 8.37 kB 0 B
build/edit-widgets/style-rtl.css 4.69 kB 0 B
build/edit-widgets/style.css 4.69 kB 0 B
build/editor/editor-styles-rtl.css 425 B 0 B
build/editor/editor-styles.css 428 B 0 B
build/editor/index.js 44.3 kB 0 B
build/editor/style-rtl.css 5.07 kB 0 B
build/editor/style.css 5.08 kB 0 B
build/element/index.js 4.65 kB 0 B
build/escape-html/index.js 734 B 0 B
build/format-library/index.js 7.63 kB 0 B
build/format-library/style-rtl.css 502 B 0 B
build/format-library/style.css 502 B 0 B
build/hooks/index.js 2.14 kB 0 B
build/html-entities/index.js 622 B 0 B
build/i18n/index.js 3.56 kB 0 B
build/is-shallow-equal/index.js 710 B 0 B
build/keyboard-shortcuts/index.js 2.51 kB 0 B
build/keycodes/index.js 1.94 kB 0 B
build/list-reusable-blocks/index.js 3.12 kB 0 B
build/list-reusable-blocks/style-rtl.css 226 B 0 B
build/list-reusable-blocks/style.css 226 B 0 B
build/media-utils/index.js 5.29 kB 0 B
build/notices/index.js 1.79 kB 0 B
build/nux/index.js 3.4 kB 0 B
build/nux/style-rtl.css 616 B 0 B
build/nux/style.css 613 B 0 B
build/plugins/index.js 2.56 kB 0 B
build/primitives/index.js 1.5 kB 0 B
build/priority-queue/index.js 789 B 0 B
build/redux-routine/index.js 2.85 kB 0 B
build/rich-text/index.js 14.8 kB 0 B
build/server-side-render/index.js 2.67 kB 0 B
build/shortcode/index.js 1.7 kB 0 B
build/token-list/index.js 1.28 kB 0 B
build/url/index.js 4.02 kB 0 B
build/viewport/index.js 1.84 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.18 kB 0 B

compressed-size-action

@@ -20,7 +20,26 @@ import { combineReducers } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

/**
* Module Constants
* @typedef WPBlockTypeCategory
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm on the fence about whether to call this WPBlockTypeCategory or WPBlockCategory. I think technically WPBlockTypeCategory is the more accurate name†, but (a) it's verbose and (b) we're already pretty consistent elsewhere in this module with calling things "block something" where we actually mean "block type something"
("block collections", "block names", "block styles", etc).

See also: #19279 (comment)

† Accuracy as in: Consider that we don't call labels of register_post_type as "post labels". It would be strange to think about it this way, as it would be very easy to confuse this as "the labels of a post", which is meaningless. That's why the function is called get_post_type_labels, and not get_post_labels.

Comment on lines -80 to +88
blockTypes={ filter( blockTypes, {
category: category.slug,
} ) }
blockTypes={ filter(
blockTypes,
( blockType ) =>
blockType.category ===
( category === defaultCategory
? undefined
: category.slug )
) }
Copy link
Member Author

Choose a reason for hiding this comment

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

Fun fact: This shorthand object predicate form for Lodash methods does not allow you to filter for undefined values, unless the property actually exists and is undefined. The function form works either way.

const blockTypes = [ { name: 'example' } ];
lodash.filter( blockTypes, { category: undefined } );
// [] 😕

const blockTypes = [ { name: 'example', category: undefined } ];
lodash.filter( blockTypes, { category: undefined } );
// [ {...} ] 🤷‍♂️ (we can't rely on it)

'title' => __( 'Uncategorized' ),
);

return $categories;
Copy link
Contributor

@youknowriad youknowriad May 8, 2020

Choose a reason for hiding this comment

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

What's the reasoning for actually registering a default category instead of just having a nullable category and just group blocks with a "null" or inexistant category together in the inserter? It might be simpler and avoids having to define a "default category"?

Copy link
Member Author

Choose a reason for hiding this comment

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

What's the reasoning for actually registering a default category instead of just having a nullable category and just group blocks with a "null" or inexistant category together in the inserter? It might be simpler and avoids having to define a "default category"?

This is mentioned in the original comment:

  • "Uncategorized" may not need to be an actual block category, but instead handled in user interfaces where absence of category is handled.
    • This may actually be good! I'm only now considering it as I write the pull request comment. I do foresee a couple minor downsides.
    • Downside: It becomes a risk for duplication and syncing issues if each handling of the "Uncategorized" behavior needs to behave consistently. For example, both the Block Inserter and Block Manager would separately need to implement a (hopefully consistent) "Uncategorized" label. "Hoping" is always a risk, and we could avoid it by treating the default category as a specially-handled case.

It may also require a bit more "explicit" handling in the components where the groupings are arranged, since it wouldn't be able to be treated just the same as any other category.

In the end though, it still may be the simplest option here 🤷 Worth exploring at least.

Copy link
Member Author

Choose a reason for hiding this comment

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

What's the reasoning for actually registering a default category instead of just having a nullable category and just group blocks with a "null" or inexistant category together in the inserter? It might be simpler and avoids having to define a "default category"?

Still in progress, but mostly completed, and seems quite a bit simpler indeed: #22280

@aduth
Copy link
Member Author

aduth commented May 13, 2020

Superseded by #22280

@aduth aduth closed this May 13, 2020
@aduth aduth deleted the update/optional-block-category branch May 13, 2020 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Block API API that allows to express the block paradigm. [Status] In Progress Tracking issues with work in progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants