-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Validate block settings for edit() and save() #508
Conversation
Fixes #362. Validates that a block has a `save()` and `edit()`. This is probably a temporary solution. The API should probably be cleaned up a bit so everything is not quite as coupled and tests can be done differently.
'Block settings must specify a edit() method for each block.' | ||
); | ||
return false; | ||
} |
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.
Since we're adding some validation, should we validate all the mandatory attributes we have? I'm thinking of icon
, title
and category
. Maybe we should also validate that the category is a known category.
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 this is fine for now, this sets our interface as requiring save()
and edit()
.
If there is buy-in to do more thorough validation, then I would probably redo this entire approach to become a bit more programmatic. The more and more validation we add will make things more and more sprawly, so we will probably eventually want to introduce a schema. A schema based validation could double serve to help validate incoming attributes for an actual block as well.
@@ -43,6 +43,13 @@ export function registerBlock( slug, settings ) { | |||
); | |||
return; | |||
} | |||
if ( true !== validateBlockSettings( settings ) ) { |
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.
There was a similar discussion in the original validation introduced about how to name this function based on its behavior. Eventually it appears we inlined the validation. A few questions then:
- Should we inline block settings validation for consistency with the slug validation?
- Should we expect the
validate
to do something, likethrow
, or perhaps prefixing withis
to reflect its boolean return value is more accurate, likeisValidBlockSettings
- If we know the function returns a boolean, do we need to be so explicit with the comparison, or can we simplify to
! isValidBlockSettings
?
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.
Should we inline block settings validation for consistency with the slug validation?
Most likely I am going to make another PR and totally redo the approach, to have the validation be all in one function rather than the sort of procedural thing we have developing.
Should we expect the validate to do something, like throw, or perhaps prefixing with is to reflect its boolean return value is more accurate, like isValidBlockSettings
Prefixing sounds good. We can throw as well.
If we know the function returns a boolean, do we need to be so explicit with the comparison, or can we simplify to ! isValidBlockSettings ?
I am usually a fan of the strict checks, but can definitely change it.
'Block not registered.' | ||
); | ||
// Return the block even though it was not registered. | ||
return Object.assign( { slug }, settings ); |
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.
Thinking since we already assign this as block
below that we could move the condition after the variable assignment and simply return the variable instead of duplicating the logic (especially in case we change the merge logic down the road).
const block = Object.assign( { slug }, settings );
if ( ! isValidBlockSettings( settings ) ) {
console.error(
'Block not registered.'
);
// Return the block even though it was not registered.
return block;
}
Or perhaps we reflect the intent that the global blocks
object should only assign the new block if it's valid, but allow the function to continue to the final return
:
const block = Object.assign( { slug }, settings );
if ( isValidBlockSettings( settings ) ) {
blocks[ slug ] = block;
} else {
console.error(
'Block not registered.'
);
}
return block;
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.
Yup, I think the second one looks better to me. Something I like to do in my apps is have a processing chain of sorts that has consistent input output behavior. It works similar to Promise
s.
This could just become a pipe ( flow in lowdash ) of blockSettings
-> isValid
-> addBlock
-> return settings. That way, if done properly, you can interchange the processing for whatever needs may come and in the future stick something new in there pretty painlessly.
@@ -0,0 +1,13 @@ | |||
/** |
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.
Being that this file is placed in the test
directory, Mocha will try to read and interpret it as a test suite, which it's not. Perhaps we could either inline these functions where they're used, or create a nested fixtures
directory?
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.
Sure. I didn't see the harm in Mocha running it. It pretty much just skips over it.
fixtures
directory sounds good. I usually name it test-data
. is that good?
* | ||
* These are methods that should be assigned to a valid block. | ||
*/ | ||
export const edit = function edit() { |
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 we could choose one or the other of the named variable or function:
export function edit() {
export const edit = function() {
// OR:
// export const edit = () => 'tacos';
The one-liner arrow function could work well as an inline function in each test, even if duplicated. Else I tend to prefer named functions with the function keyword.
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.
Yup, sounds good.
@@ -99,15 +106,21 @@ describe( 'block parser', () => { | |||
} ); | |||
|
|||
it( 'should create the requested block with no attributes if it exists', () => { | |||
registerBlock( 'core/test-block', {} ); | |||
registerBlock( 'core/test-block', { | |||
edit: edit, |
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.
Minor: We could take advantage of ES2015 object property shorthand to simplify this:
registerBlock( 'core/test-block', { edit, save } );
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.
Yeah, I wasn't sure what style should be done for that. In some projects people shy away from that as it can be confusing as to how that works.
Rather than revising this right now, I think I may try a separate PR that demonstrates another way to handle this, if that approach seems more appealing, then we can go that way. If we poo poo it, then we can come back to this one and I will revise. |
Closing, will circle back to this at another point in the future. |
Can this branch be deleted after #1345? |
Fixes #362. Validates that a block has a
save()
andedit()
. This isprobably a temporary solution. The API should probably be cleaned up a
bit so everything is not quite as coupled and tests can be done
differently.