-
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
Blocks: Introduce registerBlockTypeFromMetadata API #30293
Changes from all commits
fb21722
2dfdbdd
80c27f2
304eba2
e236e1e
e89312e
f437262
f371ba8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"title": "block title", | ||
"description": "block description", | ||
"keywords": [ "block keyword" ], | ||
"styles": [ | ||
{ | ||
"label": "block style label" | ||
} | ||
], | ||
"variations": [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nosolosw and @jorgefilipecosta – this i18n schema format is very flexible, it works with quite complex structures like the one for block variations. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's good news :) |
||
{ | ||
"title": "block variation title", | ||
"description": "block variation description", | ||
"keywords": [ "block variation keyword" ] | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,9 +5,13 @@ | |||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||
camelCase, | ||||||||||||||||||||||||||||||||
isArray, | ||||||||||||||||||||||||||||||||
isEmpty, | ||||||||||||||||||||||||||||||||
isFunction, | ||||||||||||||||||||||||||||||||
isNil, | ||||||||||||||||||||||||||||||||
isObject, | ||||||||||||||||||||||||||||||||
isPlainObject, | ||||||||||||||||||||||||||||||||
isString, | ||||||||||||||||||||||||||||||||
mapKeys, | ||||||||||||||||||||||||||||||||
omit, | ||||||||||||||||||||||||||||||||
pick, | ||||||||||||||||||||||||||||||||
|
@@ -20,11 +24,13 @@ import { | |||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||
import { applyFilters } from '@wordpress/hooks'; | ||||||||||||||||||||||||||||||||
import { select, dispatch } from '@wordpress/data'; | ||||||||||||||||||||||||||||||||
import { _x } from '@wordpress/i18n'; | ||||||||||||||||||||||||||||||||
import { blockDefault } from '@wordpress/icons'; | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||
* Internal dependencies | ||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||
import i18nBlockSchema from './i18n-block.json'; | ||||||||||||||||||||||||||||||||
import { isValidIcon, normalizeIconObject } from './utils'; | ||||||||||||||||||||||||||||||||
import { DEPRECATED_ENTRY_KEYS } from './constants'; | ||||||||||||||||||||||||||||||||
import { store as blocksStore } from '../store'; | ||||||||||||||||||||||||||||||||
|
@@ -310,6 +316,115 @@ export function registerBlockType( name, settings ) { | |||||||||||||||||||||||||||||||
return settings; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||
* Translates block settings provided with metadata using the i18n schema. | ||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||
* @param {string|string[]|Object[]} i18nSchema I18n schema for the block setting. | ||||||||||||||||||||||||||||||||
* @param {string|string[]|Object[]} settingValue Value for the block setting. | ||||||||||||||||||||||||||||||||
* @param {string} textdomain Textdomain to use with translations. | ||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||
* @return {string|string[]|Object[]} Translated setting. | ||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||
function translateBlockSettingUsingI18nSchema( | ||||||||||||||||||||||||||||||||
i18nSchema, | ||||||||||||||||||||||||||||||||
settingValue, | ||||||||||||||||||||||||||||||||
textdomain | ||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||
if ( isString( i18nSchema ) && isString( settingValue ) ) { | ||||||||||||||||||||||||||||||||
// eslint-disable-next-line @wordpress/i18n-no-variables, @wordpress/i18n-text-domain | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @swissspidy, do we need to do something special to ensure this particular translation call is ignored during WP-CLI processing? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. String extraction will skip anything that‘s not a string literal, so no. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gziolo How are the translations supposed to be in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the context of WordPress core, they are always registered on the server and translated there before passed to the client. At the moment, there is a temporary JS method In practice, this implementation is a fallback for everything else that depends only on JavaScript:
|
||||||||||||||||||||||||||||||||
return _x( settingValue, i18nSchema, textdomain ); | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aren't we going to cause a string extraction because of the usage of _x function? Maybe because we are using variables the extraction does not happen? On the PHP side, we use an internal function to avoid this issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comment from @swissspidy in #30293 (comment):
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Things like the block description currently don't have a context and now are going to have one. I guess their current translation will be invalidated, should we make some dev note or something equivalent to dev note for translators? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I understand it will be automatically handled during the WordPress release cycle, and it's the same process that happens for every newly added or updated translation. We did a batch updated of UI labels in the block editor in the past, and I don't remember any dev notes related to that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the branch of PR #31120, I made a simple test and replaced "return _x( settingValue, i18nSchema, textdomain );" with "return 'ok';". Nothing changed. With this change shouldn't all the block titles and descriptions appear as "ok"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might require adding a fallback to read the value from the client when it isn't provided from the server. It would be useful for WordPress 5.7 when using with the Gutenberg plugin. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I investigate it further and it's a different use case than gutenberg/packages/blocks/src/api/registration.js Lines 167 to 181 in c047e27
We register all blocks on the server and we set now all translatable fields there and they are exposed to the client. The values from the server take precedence to respect all PHP filters that could get applied to the metadata. It's the same data after all, so there is no reason to override it with the same metadata read on the client. To test it, you could change the method I embedded above and enforce that those fields get overridden with the values loaded on the client. Maybe you could also tweak There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for the clarification 👍 |
||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
if ( | ||||||||||||||||||||||||||||||||
isArray( i18nSchema ) && | ||||||||||||||||||||||||||||||||
! isEmpty( i18nSchema ) && | ||||||||||||||||||||||||||||||||
isArray( settingValue ) | ||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||
return settingValue.map( ( value ) => | ||||||||||||||||||||||||||||||||
translateBlockSettingUsingI18nSchema( | ||||||||||||||||||||||||||||||||
i18nSchema[ 0 ], | ||||||||||||||||||||||||||||||||
value, | ||||||||||||||||||||||||||||||||
textdomain | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
if ( | ||||||||||||||||||||||||||||||||
isObject( i18nSchema ) && | ||||||||||||||||||||||||||||||||
! isEmpty( i18nSchema ) && | ||||||||||||||||||||||||||||||||
isObject( settingValue ) | ||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||
return Object.keys( settingValue ).reduce( ( accumulator, key ) => { | ||||||||||||||||||||||||||||||||
if ( ! i18nSchema[ key ] ) { | ||||||||||||||||||||||||||||||||
accumulator[ key ] = settingValue[ key ]; | ||||||||||||||||||||||||||||||||
return accumulator; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
accumulator[ key ] = translateBlockSettingUsingI18nSchema( | ||||||||||||||||||||||||||||||||
i18nSchema[ key ], | ||||||||||||||||||||||||||||||||
settingValue[ key ], | ||||||||||||||||||||||||||||||||
textdomain | ||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||
return accumulator; | ||||||||||||||||||||||||||||||||
}, {} ); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
return settingValue; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||
* Registers a new block provided from metadata stored in `block.json` file. | ||||||||||||||||||||||||||||||||
* It uses `registerBlockType` internally. | ||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||
* @see registerBlockType | ||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||
* @param {Object} metadata Block metadata loaded from `block.json`. | ||||||||||||||||||||||||||||||||
* @param {string} metadata.name Block name. | ||||||||||||||||||||||||||||||||
* @param {string} metadata.textdomain Textdomain to use with translations. | ||||||||||||||||||||||||||||||||
* @param {Object} additionalSettings Additional block settings. | ||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||
* @return {?WPBlock} The block, if it has been successfully registered; | ||||||||||||||||||||||||||||||||
* otherwise `undefined`. | ||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||
export function registerBlockTypeFromMetadata( | ||||||||||||||||||||||||||||||||
{ name, textdomain, ...metadata }, | ||||||||||||||||||||||||||||||||
additionalSettings | ||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||
const allowedFields = [ | ||||||||||||||||||||||||||||||||
'apiVersion', | ||||||||||||||||||||||||||||||||
'title', | ||||||||||||||||||||||||||||||||
'category', | ||||||||||||||||||||||||||||||||
'parent', | ||||||||||||||||||||||||||||||||
'icon', | ||||||||||||||||||||||||||||||||
'description', | ||||||||||||||||||||||||||||||||
'keywords', | ||||||||||||||||||||||||||||||||
'attributes', | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does i18n support get added for string default values? For example let's say we have
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, we should rather avoid it because it won't produce predictable content. Depending on the language picked in the admin UI, you could get a different output for the same block. We need to remember also that, the value of an attribute when strictly equal to the default value it doesn't get stored in HTML comment of the block definition. The other challenge is, how would you decide which There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🤔 good to highlight this case.
If we wanted to support it, we could add an additional attribute. I found that I did need it in one of the blocks, but the behavior can also be simulated with a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When using a side effect as part of the React component lifecycle, you are updating an attribute that might create an undo level so you need to use a special version of the store action that mitigates that. Another approach is to use a block variation that gets applied as the default one in the inserter. The benefit of that is that it sets attributes dynamically when inserting the block. |
||||||||||||||||||||||||||||||||
'providesContext', | ||||||||||||||||||||||||||||||||
'usesContext', | ||||||||||||||||||||||||||||||||
'supports', | ||||||||||||||||||||||||||||||||
'styles', | ||||||||||||||||||||||||||||||||
'example', | ||||||||||||||||||||||||||||||||
'variations', | ||||||||||||||||||||||||||||||||
]; | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
const settings = pick( metadata, allowedFields ); | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
if ( textdomain ) { | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I checked, in the native version we set the locale data without defining a text domain, not sure if this could be a problem if in the future we move some of the fields to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||
Object.keys( i18nBlockSchema ).forEach( ( key ) => { | ||||||||||||||||||||||||||||||||
if ( ! settings[ key ] ) { | ||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
settings[ key ] = translateBlockSettingUsingI18nSchema( | ||||||||||||||||||||||||||||||||
i18nBlockSchema[ key ], | ||||||||||||||||||||||||||||||||
settings[ key ], | ||||||||||||||||||||||||||||||||
textdomain | ||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||
} ); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
unstable__bootstrapServerSideBlockDefinitions( { | ||||||||||||||||||||||||||||||||
[ name ]: settings, | ||||||||||||||||||||||||||||||||
} ); | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
return registerBlockType( name, additionalSettings ); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||
* Registers a new block collection to group blocks in the same namespace in the inserter. | ||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||
|
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.
@gwwar, should we put here
variations
as well which we plan to integrate withblock.json
soon?There is one more place where we would need to put it on the allow list.
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.
I think it'd be worth a try, will it affect the needed PR order?
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.
Let me think about what we need to change to enable
variations
inblock.json
on the PHP side. As far as I can tell, it's mostly the same set of changes like here now thatWP_Block_Type
and the REST API endpoint support block variations. Given that you won't be able to register block variations on the server until WP 5.8 is out the order shouldn't matter at all. We also have a mechanism in place that favors definitions from the server but fallback to the client when not set there. I think it's safe to change here at the same time 👍🏻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.
Added in b851c2f. Every change to docs get synced with https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/ so I will open another PR that covers the part that describes
variations
in theblock.json
file. We should plan to merge it around WordPress 5.8 RC.1 when all Dev Notes get published.