Skip to content

Instantly share code, notes, and snippets.

@igorbenic
Last active June 16, 2022 00:44
Show Gist options
  • Save igorbenic/2f92aa2bd5512a6eb49f93fd9e8e9481 to your computer and use it in GitHub Desktop.
Save igorbenic/2f92aa2bd5512a6eb49f93fd9e8e9481 to your computer and use it in GitHub Desktop.
How to create a Gutenberg Block for Displaying a Post | https://www.ibenic.com/create-gutenberg-block-displaying-post/
registerBlockType( 'cgb/block-guten-load-post', {
// ...
keywords: [
__( 'my-block — CGB Block' ),
__( 'CGB Example' ),
__( 'create-guten-block' ),
],
attributes: {
content: {
type: 'array',
source: 'children',
selector: 'p',
},
title: {
type: 'string',
selector: 'h2'
},
link: {
type: 'string',
selector: 'a'
},
selectedPost: {
type: 'number',
default: 0,
},
},
// ...
}
// ...
const { __ } = wp.i18n; // Import __() from wp.i18n
const { registerBlockType, InspectorControls } = wp.blocks; // Import registerBlockType() from wp.blocks
const { SelectControl } = wp.components;
const { Component } = wp.element;
class mySelectPosts extends Component {
render() {
return ( 'Load Post Placeholder' )
}
}
registerBlockType( 'cgb/block-guten-load-post', {
// ...
// The "edit" property must be a valid function.
edit: mySelectPosts,
// ...
} );
class mySelectPosts extends Component {
// Method for setting the initial state.
static getInitialState( selectedPost ) {
return {
posts: [],
selectedPost: selectedPost,
post: {},
};
}
// Constructing our component. With super() we are setting everything to 'this'.
// Now we can access the attributes with this.props.attributes
constructor() {
super( ...arguments );
// Maybe we have a previously selected post. Try to load it.
this.state = this.constructor.getInitialState( this.props.attributes.selectedPost );
}
render() {
// Options to hold all loaded posts. For now, just the default.
let options = [ { value: 0, label: __( 'Select a Post' ) } ];
return [
// If we are focused on this block, create the inspector controls.
!! this.props.isSelected && ( <InspectorControls key='inspector'>
<SelectControl
// Selected value.
value={ this.props.attributes.selectedPost }
label={ __( 'Select a Post' ) }
options={ options } />
</InspectorControls>
),
'Load Post Placeholder'
]
}
}
class mySelectPosts extends Component {
// ...
render() {
let options = [ { value: 0, label: __( 'Select a Post' ) } ];
let output = __( 'Loading Posts' );
if( this.state.posts.length > 0 ) {
const loading = __( 'We have %d posts. Choose one.' );
output = loading.replace( '%d', this.state.posts.length );
this.state.posts.forEach((post) => {
options.push({value:post.id, label:post.title.rendered});
});
} else {
output = __( 'No posts found. Please create some first.' );
}
return [
!! this.props.isSelected && ( <InspectorControls key='inspector'>
<SelectControl value={ this.props.attributes.selectedPost } label={ __( 'Select a Post' ) } options={ options } />
</InspectorControls>
),
output
]
}
}
class mySelectPosts extends Component {
// ...
constructor() {
super( ...arguments );
this.state = this.constructor.getInitialState( this.props.attributes.selectedPost );
// Bind so we can use 'this' inside the method.
this.getOptions = this.getOptions.bind(this);
// Load posts.
this.getOptions();
}
/**
* Loading Posts
*/
getOptions() {
return ( new wp.api.collections.Posts() ).fetch().then( ( posts ) => {
if( posts && 0 !== this.state.selectedPost ) {
// If we have a selected Post, find that post and add it.
const post = posts.find( ( item ) => { return item.id == this.state.selectedPost } );
// This is the same as { post: post, posts: posts }
this.setState( { post, posts } );
} else {
this.setState({ posts });
}
});
}
// ...
}
class mySelectPosts extends Component {
// ...
constructor() {
// ...
// Bind it.
this.onChangeSelectPost = this.onChangeSelectPost.bind(this);
}
onChangeSelectPost( value ) {
// Find the post
const post = this.state.posts.find( ( item ) => { return item.id == parseInt( value ) } );
// Set the state
this.setState( { selectedPost: parseInt( value ), post } );
// Set the attributes
this.props.setAttributes( {
selectedPost: parseInt( value ),
title: post.title.rendered,
content: post.excerpt.rendered,
link: post.link,
});
}
render() {
// ...
return [
!! this.props.focus && ( <InspectorControls key='inspector'>
// Adding onChange method.
<SelectControl onChange={this.onChangeSelectPost} value={ this.props.attributes.selectedPost } label={ __( 'Select a Post' ) } options={ options } />
</InspectorControls>
),
output
]
}
}
class mySelectPosts extends Component {
render() {
let options = [ { value: 0, label: __( 'Select a Post' ) } ];
let output = __( 'Loading Posts' );
this.props.className += ' loading';
if( this.state.posts.length > 0 ) {
// ...
} else {
output = __( 'No posts found. Please create some first.' );
}
// Checking if we have anything in the object
if( this.state.post.hasOwnProperty('title') ) {
output = <div className="post">
<a href={ this.state.post.link }><h2 dangerouslySetInnerHTML={ { __html: this.state.post.title.rendered } }></h2></a>
<p dangerouslySetInnerHTML={ { __html: this.state.post.excerpt.rendered } }></p>
</div>;
this.props.className += ' has-post';
} else {
this.props.className += ' no-post';
}
return [
!! this.props.isSelected && ( <InspectorControls key='inspector'>
<SelectControl onChange={this.onChangeSelectPost} value={ this.props.attributes.selectedPost } label={ __( 'Select a Post' ) } options={ options } />
</InspectorControls>
),
<div className={this.props.className}>{output}</div>
]
}
}
registerBlockType( 'cgb/block-guten-load-post', {
// ...
// The "save" property must be specified and must be a valid function.
save: function( props ) {
return (
<div className={ props.className }>
<div className="post">
<a href={ props.attributes.link }><h2 dangerouslySetInnerHTML={ { __html: props.attributes.title } }></h2></a>
<p dangerouslySetInnerHTML={ { __html: props.attributes.content } }></p>
</div>
</div>
);
},
} );
registerBlockType( 'cgb/block-guten-load-post', {
// Block name. Block names must be string that contains a namespace prefix. Example: my-plugin/my-custom-block.
title: __( 'Load a Post' ), // Block title.
icon: 'shield', // Block icon from Dashicons → https://developer.wordpress.org/resource/dashicons/.
category: 'common', // Block category — Group blocks together based on common traits E.g. common, formatting, layout widgets, embed.
keywords: [
__( 'load' ),
__( 'Load Post' ),
__( 'guten-load-post' ),
],
// ...
}
@keyframes pulse {
0% {
background-color: #ffffff;
}
50% {
background-color: #cccccc;
}
100% {
background-color: #ffffff;
}
}
.wp-block-cgb-block-guten-load-post {
padding: 0.5em;
border: 1px dashed rgba(0, 0, 0, 0.5);
&.no-post {
background: yellow;
animation: none;
}
&.loading {
animation: pulse 3s infinite;
}
&.has-post {
animation: none;
}
}
@jnz31
Copy link

jnz31 commented Jun 21, 2021

InspectorControls has been moved from wp.blocks to wp.blockEditor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment