-
Notifications
You must be signed in to change notification settings - Fork 382
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
Add AMP validation checking for Gutenberg blocks #1019
Changes from 8 commits
8413457
07aeee2
01691e5
27e8bec
590cd76
8218919
9c3568d
ab82e8d
ea1c37a
96297fc
ed4b03b
561af32
f64ab2e
b9bd206
bcdff5f
049e482
fe34e8e
49b66ae
743cca4
4676777
b9aa16c
e2fc9a7
6cd996a
339f69f
a02d029
15f0abe
2b03c6a
d366da3
80d32de
16388c2
f9d8575
9aa6704
dcb1e77
eb00bcf
9ecc24c
354fdcd
0bada73
34024cc
8e78904
277da63
ca3ff60
01a21a8
3e032d4
6a4cbcc
4d7dc19
21e60f0
ca6c96c
7a60509
5bf12da
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,185 @@ | ||
/*jshint esversion: 6 */ | ||
/*global _, wp:true */ | ||
/** | ||
* AMP Gutenberg integration. | ||
* | ||
* On editing a block, this checks that the content is AMP-compatible. | ||
* And it displays a notice if it's not. | ||
*/ | ||
|
||
/* exported ampBlockValidation */ | ||
var ampBlockValidation = ( function( $ ) { | ||
var module = { | ||
|
||
/** | ||
* Holds data. | ||
*/ | ||
data: {}, | ||
|
||
/** | ||
* Boot module. | ||
* | ||
* @param {Object} data Object data. | ||
* @return {void} | ||
*/ | ||
boot: function( data ) { | ||
module.data = data; | ||
$( document ).ready( function() { | ||
if ( 'undefined' !== typeof wp.blocks ) { | ||
module.processBlocks(); | ||
} | ||
} ); | ||
}, | ||
|
||
/** | ||
* Gets all of the registered blocks, and overwrites their edit() functions. | ||
* | ||
* The new edit() functions will check if the content is AMP-compliant. | ||
* If not, it will display a notice. | ||
* | ||
* @returns {void} | ||
*/ | ||
processBlocks: function() { | ||
var blocks = wp.blocks.getBlockTypes(); | ||
blocks.forEach( function( block ) { | ||
if ( block.hasOwnProperty( 'name' ) ) { | ||
module.overwriteEdit( block.name ); | ||
} | ||
} ); | ||
}, | ||
|
||
/** | ||
* Overwrites the edit() function of a block. | ||
* | ||
* Outputs the original edit function, stored in OriginalBlockEdit. | ||
* This also appends a notice to the block. | ||
* It only displays if the block's content isn't valid AMP, | ||
* | ||
* @see https://riad.blog/2017/10/16/one-thousand-and-one-way-to-extend-gutenberg-today/ | ||
* @param {string} blockType the type of the block, like 'core/paragraph'. | ||
* @returns {void} | ||
*/ | ||
overwriteEdit: function( blockType ) { | ||
let block = wp.blocks.unregisterBlockType( blockType ); | ||
let OriginalBlockEdit = block.edit; | ||
|
||
block.edit = class AMPNotice extends wp.element.Component { | ||
|
||
/** | ||
* The AMPNotice constructor. | ||
* | ||
* @param {object} props The component properties. | ||
* @returns {void} | ||
*/ | ||
constructor( props ) { | ||
props.attributes.pendingValidation = false; | ||
super( props ); | ||
this.validateAMP = _.throttle( this.validateAMP, 5000 ); | ||
this.state = { isInvalidAMP: false }; | ||
} | ||
|
||
/** | ||
* Outputs the existing block, with a Notice element below. | ||
* | ||
* The Notice only appears if the state of isInvalidAMP is false. | ||
* It also displays the name of the block. | ||
* | ||
* @returns {array} The elements. | ||
*/ | ||
render() { | ||
let originalEdit; | ||
let result; | ||
|
||
result = []; | ||
originalEdit = wp.element.createElement( OriginalBlockEdit, this.props ); | ||
if ( originalEdit ) { | ||
result.push( originalEdit ); | ||
} | ||
if ( this.state.isInvalidAMP && wp.components.hasOwnProperty( 'Notice' ) ) { | ||
result.push( wp.element.createElement( | ||
wp.components.Notice, | ||
{ | ||
status: 'warning', | ||
content: module.data.i18n.notice.replace( '%s', this.props.name ), | ||
isDismissible: false | ||
} | ||
) ); | ||
} | ||
|
||
this.props.attributes.pendingValidation = false; | ||
return result; | ||
} | ||
|
||
/** | ||
* Handler for after the component mounting. | ||
* | ||
* If validateAMP() changes the isInvalidAMP state, it will result in this method being called again. | ||
* There's no need to check if the state is valid again. | ||
* So this skips the check if pendingValidation is true. | ||
* | ||
* @returns {void} | ||
*/ | ||
componentDidMount() { | ||
if ( ! this.props.attributes.pendingValidation ) { | ||
let content = this.props.attributes.content; | ||
if ( 'string' !== typeof content ) { | ||
content = wp.element.renderToString( content ); | ||
} | ||
if ( content.length > 0 ) { | ||
this.validateAMP( this.props.attributes.content ); | ||
} | ||
} | ||
this.props.attributes.pendingValidation = false; | ||
} | ||
|
||
/** | ||
* Validates the content for AMP compliance, and sets the state of the Notice. | ||
* | ||
* Depending on the results of the validation, | ||
* it sets the Notice component's isInvalidAMP state. | ||
* This will cause the notice to either display or be hidden. | ||
* | ||
* @param {string} content The block content, from calling save(). | ||
* @returns {void} | ||
*/ | ||
validateAMP( content ) { | ||
this.setState( function() { | ||
|
||
// Changing the state can cause componentDidMount() to be called, so prevent it from calling validateAMP() again. | ||
component.props.attributes.pendingValidation = true; | ||
return { isInvalidAMP: ( Math.random() > 0.5 ) }; | ||
} ); | ||
|
||
let component = this; | ||
$.post( | ||
module.data.endpoint, | ||
{ | ||
markup: content | ||
} | ||
).done( function( data ) { | ||
if ( data.hasOwnProperty( 'removed_elements' ) && ( 0 === data.removed_elements.length ) && ( 0 === data.removed_attributes.length ) ) { | ||
component.setState( function() { | ||
|
||
// Changing the state can cause componentDidMount() to be called, so prevent it from calling validateAMP() again. | ||
component.props.attributes.pendingValidation = true; | ||
return { isInvalidAMP: false }; | ||
} ); | ||
} else { | ||
component.setState( function() { | ||
component.props.attributes.pendingValidation = true; | ||
return { isInvalidAMP: true }; | ||
} ); | ||
} | ||
} ); | ||
} | ||
|
||
}; | ||
|
||
wp.blocks.registerBlockType( blockType, block ); | ||
} | ||
|
||
}; | ||
|
||
return module; | ||
|
||
} )( window.jQuery ); | ||
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. We should eliminate jQuery as being a dependency for this script. Extending Gutenberg should be jQuery-free. 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, @westonruter. This commit removes the |
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.
ES6 classes aren't going to work in IE11.
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.
Thanks, @westonruter. This commit removes the ES6 class.