-
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
Changes from all commits
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,13 @@ export function registerBlock( slug, settings ) { | |
); | ||
return; | ||
} | ||
if ( true !== validateBlockSettings( settings ) ) { | ||
console.error( | ||
'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 commentThe reason will be displayed to describe this comment to others. Learn more. Thinking since we already assign this as 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 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 commentThe 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 This could just become a pipe ( flow in lowdash ) of |
||
} | ||
const block = Object.assign( { slug }, settings ); | ||
blocks[ slug ] = block; | ||
return block; | ||
|
@@ -104,3 +111,34 @@ export function getBlockSettings( slug ) { | |
export function getBlocks() { | ||
return Object.values( blocks ); | ||
} | ||
|
||
/** | ||
* Validates the block settings. | ||
* | ||
* @param {Object} settings Block settings. | ||
* @return {Boolean} Whether the block settings are valid. | ||
*/ | ||
export function validateBlockSettings( settings ) { | ||
if ( ! settings ) { | ||
console.error( | ||
'Block settings must specify a save() and edit() method for each block.' | ||
); | ||
return false; | ||
} | ||
|
||
if ( ! settings.save || 'function' !== typeof settings.save ) { | ||
console.error( | ||
'Block settings must specify a save() method for each block.' | ||
); | ||
return false; | ||
} | ||
|
||
if ( ! settings.edit || 'function' !== typeof settings.edit ) { | ||
console.error( | ||
'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 commentThe 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 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 fine for now, this sets our interface as requiring 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. |
||
|
||
return true; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/** | ||
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. Being that this file is placed in 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. Sure. I didn't see the harm in Mocha running it. It pretty much just skips over it.
|
||
* Function references used so that Chai doesn't complain about functions | ||
* not being equal. | ||
* | ||
* 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. Yup, sounds good. |
||
'tacos'; | ||
}; | ||
|
||
export const save = function save() { | ||
'delicious'; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,10 @@ import { | |
getBlocks, | ||
setUnknownTypeHandler, | ||
} from '../registration'; | ||
import { | ||
edit, | ||
save | ||
} from './function-refs.js'; | ||
|
||
describe( 'block parser', () => { | ||
afterEach( () => { | ||
|
@@ -87,7 +91,10 @@ describe( 'block parser', () => { | |
|
||
describe( 'createBlockWithFallback', () => { | ||
it( 'should create the requested block if it exists', () => { | ||
registerBlock( 'core/test-block', {} ); | ||
registerBlock( 'core/test-block', { | ||
edit: edit, | ||
save: save | ||
} ); | ||
|
||
const block = createBlockWithFallback( | ||
'core/test-block', | ||
|
@@ -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 commentThe 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 commentThe 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. |
||
save: save | ||
} ); | ||
|
||
const block = createBlockWithFallback( 'core/test-block', 'content' ); | ||
expect( block.blockType ).to.eql( 'core/test-block' ); | ||
expect( block.attributes ).to.eql( {} ); | ||
} ); | ||
|
||
it( 'should fall back to the unknown type handler for unknown blocks if present', () => { | ||
registerBlock( 'core/unknown-block', {} ); | ||
registerBlock( 'core/unknown-block', { | ||
edit: edit, | ||
save: save | ||
} ); | ||
setUnknownTypeHandler( 'core/unknown-block' ); | ||
|
||
const block = createBlockWithFallback( | ||
|
@@ -120,7 +133,10 @@ describe( 'block parser', () => { | |
} ); | ||
|
||
it( 'should fall back to the unknown type handler if block type not specified', () => { | ||
registerBlock( 'core/unknown-block', {} ); | ||
registerBlock( 'core/unknown-block', { | ||
edit: edit, | ||
save: save | ||
} ); | ||
setUnknownTypeHandler( 'core/unknown-block' ); | ||
|
||
const block = createBlockWithFallback( null, 'content' ); | ||
|
@@ -142,7 +158,9 @@ describe( 'block parser', () => { | |
return { | ||
content: rawContent, | ||
}; | ||
} | ||
}, | ||
edit: edit, | ||
save: save | ||
} ); | ||
|
||
const parsed = parse( | ||
|
@@ -166,7 +184,9 @@ describe( 'block parser', () => { | |
return { | ||
content: rawContent + ' & Chicken' | ||
}; | ||
} | ||
}, | ||
edit: edit, | ||
save: save | ||
} ); | ||
|
||
const parsed = parse( | ||
|
@@ -184,8 +204,14 @@ describe( 'block parser', () => { | |
} ); | ||
|
||
it( 'should parse the post content, using unknown block handler', () => { | ||
registerBlock( 'core/test-block', {} ); | ||
registerBlock( 'core/unknown-block', {} ); | ||
registerBlock( 'core/test-block', { | ||
edit: edit, | ||
save: save | ||
} ); | ||
registerBlock( 'core/unknown-block', { | ||
edit: edit, | ||
save: save | ||
} ); | ||
|
||
setUnknownTypeHandler( 'core/unknown-block' ); | ||
|
||
|
@@ -204,14 +230,19 @@ describe( 'block parser', () => { | |
} ); | ||
|
||
it( 'should parse the post content, including raw HTML at each end', () => { | ||
registerBlock( 'core/test-block', {} ); | ||
registerBlock( 'core/test-block', { | ||
edit: edit, | ||
save: save | ||
} ); | ||
registerBlock( 'core/unknown-block', { | ||
// Currently this is the only way to test block content parsing? | ||
attributes: function( rawContent ) { | ||
return { | ||
content: rawContent, | ||
}; | ||
} | ||
}, | ||
edit: edit, | ||
save: save | ||
} ); | ||
|
||
setUnknownTypeHandler( 'core/unknown-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.
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:
validate
to do something, likethrow
, or perhaps prefixing withis
to reflect its boolean return value is more accurate, likeisValidBlockSettings
! 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.
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.
Prefixing sounds good. We can throw as well.
I am usually a fan of the strict checks, but can definitely change it.