-
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
Block API: Introduce block definitions and implementations #6733
Changes from all commits
036e05a
a691fc7
feb0600
3d1fd0c
44ab993
c55f8d9
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 |
---|---|---|
|
@@ -43,6 +43,28 @@ export function blockTypes( state = {}, action ) { | |
return state; | ||
} | ||
|
||
/** | ||
* Reducer managing the block type implementations | ||
* | ||
* @param {Object} state Current state. | ||
* @param {Object} action Dispatched action. | ||
* | ||
* @return {Object} Updated state. | ||
*/ | ||
export function implementations( state = {}, action ) { | ||
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 don't necessarily oppose, but to me it was unexpected to see this as a separate branch on the client. Does the client really care about which bits of information are part of the definition and which of the implementation? As long as we have distinct actions, we can treat the bits differently as they come in if need be, but then we store everything in a common place for a single block type. |
||
switch ( action.type ) { | ||
case 'IMPLEMENT_BLOCK_TYPES': | ||
return { | ||
...state, | ||
...keyBy( action.implementations, 'name' ), | ||
}; | ||
case 'REMOVE_BLOCK_TYPES': | ||
return omit( state, action.names ); | ||
} | ||
|
||
return state; | ||
} | ||
|
||
/** | ||
* Higher-order Reducer creating a reducer keeping track of given block name. | ||
* | ||
|
@@ -91,6 +113,7 @@ export function categories( state = DEFAULT_CATEGORIES, action ) { | |
|
||
export default combineReducers( { | ||
blockTypes, | ||
implementations, | ||
defaultBlockName, | ||
fallbackBlockName, | ||
categories, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
* External dependencies | ||
*/ | ||
import createSelector from 'rememo'; | ||
import { filter, includes, map } from 'lodash'; | ||
import { filter, includes, map, mapValues, compact, get } from 'lodash'; | ||
|
||
/** | ||
* Returns all the available block types. | ||
|
@@ -12,9 +12,10 @@ import { filter, includes, map } from 'lodash'; | |
* @return {Array} Block Types. | ||
*/ | ||
export const getBlockTypes = createSelector( | ||
( state ) => Object.values( state.blockTypes ), | ||
( state ) => compact( Object.values( state.blockTypes ).map( ( { name } ) => getBlockType( state, name ) ) ), | ||
( state ) => [ | ||
state.blockTypes, | ||
state.implementations, | ||
] | ||
); | ||
|
||
|
@@ -26,9 +27,32 @@ export const getBlockTypes = createSelector( | |
* | ||
* @return {Object?} Block Type. | ||
*/ | ||
export function getBlockType( state, name ) { | ||
return state.blockTypes[ name ]; | ||
} | ||
export const getBlockType = createSelector( | ||
( state, name ) => { | ||
const blockTypeDefinition = state.blockTypes[ name ]; | ||
const blockTypeImplementation = state.implementations[ name ]; | ||
|
||
if ( ! blockTypeDefinition || ! blockTypeImplementation ) { | ||
return null; | ||
} | ||
|
||
return { | ||
...blockTypeDefinition, | ||
...blockTypeImplementation, | ||
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 makes sense to me that the implementation can override attributes that are in the definition.
We could 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.
It's not quite as obvious to me. What's the use case? |
||
attributes: mapValues( blockTypeDefinition.attributes, ( attribute, key ) => { | ||
const implementationAttribute = get( blockTypeImplementation.attributes, [ key ], {} ); | ||
return { | ||
...attribute, | ||
...implementationAttribute, | ||
}; | ||
} ), | ||
}; | ||
}, | ||
( state ) => [ | ||
state.blockTypes, | ||
state.implementations, | ||
] | ||
); | ||
|
||
/** | ||
* Returns all the available categories. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ import { | |
setDefaultBlockName, | ||
setUnknownTypeHandlerName, | ||
} from '@wordpress/blocks'; | ||
import { dispatch } from '@wordpress/data'; | ||
|
||
/** | ||
* Internal dependencies | ||
|
@@ -77,11 +78,15 @@ export const registerCoreBlocks = () => { | |
table, | ||
textColumns, | ||
verse, | ||
video, | ||
].forEach( ( { name, settings } ) => { | ||
registerBlockType( name, settings ); | ||
} ); | ||
|
||
[ video ].forEach( ( { name, definition, implementation } ) => { | ||
dispatch( 'core/blocks' ).addBlockTypes( { name, ...definition } ); | ||
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 think this is not acceptable solution because it will break all plugins that depend on the following filter: settings = applyFilters( 'blocks.registerBlockType', settings, name ); 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 addition this code bypass all validations we have 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. Good points.
How to approach this? should we just leave the video block as is for now and refactor all the blocks at once once we create the alternative filters and move things to the server? I'm trying to avoid telling people to rewrite their code twice and only introduce deprecations once we have everything.
I guess we should split validation between the two actions. I'll give it a shot 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. Two times yes :) It was good for illustration how this can work, but we still need those wrapping API functions to make sure everything is valid. |
||
dispatch( 'core/blocks' ).implementBlockTypes( { name, ...implementation } ); | ||
} ); | ||
|
||
setDefaultBlockName( paragraph.name ); | ||
setUnknownTypeHandlerName( freeform.name ); | ||
}; |
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.
deprecated
is missing in here. Is it intentional?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.
Actually, it's not. I intentionally avoided any deprecations for now. The idea is to do it only once, when we have all the bits in (the server-side part essentially)
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 mean, it doesn't matter in this PR, but definitely something, we should keep in mind. Should we add todo 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.
I think it's not necessary because that's the first thing we should do when we move registration to the server. Tell people to register their blocks on the server.