I have started using Gutenberg custom blocks, and I have built custom blocks successfully!
But I have an issue, I need to see the preview of the block when I am not editing it on the Admin side, I don't think Gutenberg provides it, because I searched the documentation, searched the internet but nothing found regarding preview of the block when not in edit state.
I have used Block Lab Plugin and it do have a preview, if Gutenberg don't provide it, how can I have a preview? Any Trick or Hack?
This is my block.js:
(function(blocks, components, element) {
var el = element.createElement;
var registerBlockType = blocks.registerBlockType;
var TextControl = components.TextControl;
registerBlockType("blocks/test-block", {
title: "Test Block",
description: "A custom block.",
icon: "businessman",
category: "common",
attributes: {
headline: {
type: "string"
}
},
edit: function(props) {
var headline = props.attributes.headline;
var onHeadlineChange = function(val) {
props.setAttributes({
headline: val
});
};
return el(TextControl, {
value: headline,
label: "Headline",
onChange: onHeadlineChange
});
},
save: function(props) {
return el(
"h3",
{
className: "headline"
},
props.attributes.headline
);
}
});
})(window.wp.blocks, window.wp.components, window.wp.element);
To display your custom block with a preview, just add example: () => {}, to section, in the same way as save and edit.
https://prnt.sc/rov8qo
//Custom Gutenberg block
(function(components, element) {
//Default
const { registerBlockType } = wp.blocks;
//Block registration
registerBlockType('', {
title: '',
description: '',
icon: '',
category: '',
keywords: [''],
attributes: {},
//Example function
example: () => {}, //Add this to get block preview works
//Edit function
edit: props => {},
//Save function
save: props => {}
});
})(window.wp.components, window.wp.element);
UPDATE 1
In order to make a preview of the block (how it will appear in the front), you need to add styles to this block, the same as on the front, and track props.isSelected this block in editor.
Depending on whether the block is selected or not, show different content.
//Custom Gutenberg block
(function(blocks, element, blockEditor) {
//Default variable
const { registerBlockType } = blocks;
const { createElement: el } = element;
const { RichText } = blockEditor;
//Block registration
registerBlockType("blocks/test-block", {
title: 'Test Block',
description: 'A custom block.',
icon: 'businessman',
category: 'common',
attributes: {
title: {
type: 'string',
source: 'html',
selector: '.headline'
}
},
//Example function
example: () => {},
//Edit function
edit: props => {
const attributes = props.attributes;
return (
el( 'div', { className: props.className },
//View input field
props.isSelected && el(RichText, {
tagName: 'h3',
placeholder: 'Headline...',
keepPlaceholderOnFocus: true,
value: attributes.title,
allowedFormats: [],
onChange: title => props.setAttributes({ title })
}),
//View frontend preview
!props.isSelected && el( 'div', { className: 'block__headline' },
el( 'div', { className: 'block__headline-title' }, attributes.title ? attributes.title : 'Entry headline...')
)
)
);
},
//Save function
save: props => {
return el( RichText.Content, {
tagName: 'h3',
className: 'headline',
value: props.attributes.title
});
}
});
})(window.wp.blocks, window.wp.element, window.wp.blockEditor);
Css the same as on your frontend.
.block__headline {
padding: 20px 15px 30px;
background: #fafafa;
text-align: center;
}
.block__headline-title {
font-family: 'Montserrat';
font-size: 30px;
font-weight: bold;
position: relative;
}
.block__headline-title:after {
content: '';
display: block;
width: 40px;
height: 2px;
background: #333;
margin: 0 auto;
}
Related
In the following code, I am trying to dynamically update a template with attribute data that is not available until the first useEffect call. In the code below, the first render of course has emoty attributes and displays defaults. I can trace the code and see the template get updated, but after the useInnerBlocksProps is called with the revised template, the data is still not rendered. what am I missing?
export default function Edit(props) {
const { PanelBody, PanelRow } = wp.components;
const { attributes, setAttributes } = props;
useEffect(() => {
setAttributes({'name': 'Edward Alan Thompson'});
setAttributes({'birth_date': '1 jan 1900'});
setAttributes({'death_date': '31 Dec 1990'});
setAttributes({'imageUrl': 'http://wordpressdevel.local/wp-content/uploads/2022/07/1.png'});
}, []);
const blockProps = useBlockProps( {
style:{minHeight: 40, width: '100%', border: "1px dotted red"}
});
const innerBlocksProps = useInnerBlocksProps(
{orientation: "horizontal"},
{template: [
[ 'core/columns', { columns: 2 }, [
[ 'core/column', {width: "20%"}, [
[ 'core/image', { url: attributes.imageUrl??''} ]
] ],
[ 'core/column', {}, [
[ 'core/heading', { content: attributes.name??''} ],
[ 'core/paragraph', { content: 'b. ' + attributes.birth_date??''} ],
[ 'core/paragraph', { content: 'd. ' + attributes.death_date??''} ]
] ],
]
]
]}
);
return (
<div >
<InspectorControls>
<PanelBody title="General" initialOpen={true}>
<PanelRow>
</PanelRow>
</PanelBody>
</InspectorControls>
<div {...blockProps}>
<div {...innerBlocksProps}></div></div>
</div>
);
}
I verified that the last render call does have the proper template definition, its just not what is displayed on the screen.
Looking at your code, the issue seems to be with how useInnerBlocksProps() is called: the first parameter expects blockProps followed by the innerBlocks content, eg:
edit.js
import { useEffect } from '#wordpress/element';
import { useInnerBlocksProps, useBlockProps } from '#wordpress/block-editor';
export default function Edit({ attributes, setAttributes }) {
// Nb. Removed code not related to issue for clarity
useEffect(() => {
// Call setAttributes once
setAttributes({
name: "Edward Alan Thompson",
birth_date: "1 jan 1900",
death_date: "31 Dec 1990",
image_url: "http://wordpressdevel.local/wp-content/uploads/2022/07/1.png",
});
}, []);
const blockProps = useBlockProps({
style: { minHeight: 40, width: '100%', border: "1px dotted red" }
});
const TEMPLATE = [
['core/columns', {}, [ // Number of columns not needed
['core/column', { width: '20%' }, [
['core/image', { url: attributes.image_url ?? '' }]],
],
['core/column', {}, [
['core/heading', { content: attributes.name ?? '' }],
['core/paragraph', { content: 'b. ' + attributes.birth_date ?? '' }],
['core/paragraph', { content: 'd. ' + attributes.death_date ?? '' }]
]]
]]]
const { children, ...innerBlocksProps } = useInnerBlocksProps(blockProps,
{
orientation: "horizontal",
template: TEMPLATE
}
);
return (
<div {...innerBlocksProps}>
{children}
</div>
);
}
Ref: Inner Blocks: Using a react hook
I came to this conclusion after testing your template definition by itself using <InnerBlocks .../> then checked what useInnerBlocksProps() expected.
I am working on creating a Gutenberg block that will have a dropdown of posts and the user will pick one.
It saves the selection, but if I refresh the page the selection is not there anymore, switches back to 'ALL'.
It also displays fine in the front end, however, if I try to edit the page, the selections are gone and the dropdown switch back to all.
Any ideas on this one?
const { RichText, InspectorControls, ColorPalette, MediaUpload } = wp.blockEditor;
const { PanelBody, IconButton, RangeControl, TextControl, SelectControl } = wp.components;
wp.data.select('core').getEntityRecords('postType', 'beers');
registerBlockType('tg/product-block', {
title: 'TG Product Block',
description: 'Showcasing Products in a block',
parent: ['tg/products-block'],
atrributes: {
beerName: {
type:'string'
},
beerId: {
type:'string'
}
},
edit: ({attributes, setAttributes}) => {
const {
beerName,
beerId,
} = attributes;
function getBeers() {
let options = [{ value: 0, label: 'All' }];
let beers = wp.data.select('core').getEntityRecords('postType', 'beers');
beers.forEach((beer) => {
options.push({ value: beer.id, label: beer.title.rendered });
});
return options;
}
function onDropdownChangeOne (newBeerId) {
console.log('newBeerId', newBeerId)
setAttributes({ beerId: newBeerId })
fetch(window.location.protocol + '//' + window.location.hostname + ':10016/wp-json/wp/v2/beers/' + newBeerId)
.then(response => response.json())
.then(data => {
console.log(data)
setAttributes({beerName: data.title.rendered})
})
}
return([
<InspectorControls style={{ marginBottom: '40px' }}>
<PanelBody title={'Choose Category For The First Box'}>
</PanelBody>
</InspectorControls>,
<div className="col-md-4">
<SelectControl
value={beerId}
label={'Select a Post'}
options={getBeers()}
onChange={onDropdownChangeOne}
/>
<RichText.Content tagName="h2"
value={beerName}
/>
</div>
])
},
save: ({attributes}) => {
const {
beerName,
beerId,
} = attributes;
return(
<div className="col-md-4">
<RichText.Content tagName="h2"
value={beerName}
/>
</div>
)
}
})```
As the title states I am teaching myself how to create a custom Gutenburg Block for wordpress development and I have written the following code. It functions correctly when you save, but when you reload the saved page you get console errors and it shows
This block contains unexpected or invalid content.
When you click resolve it shows the following:
// Import CSS.
import './editor.scss';
import './style.scss';
const { __ } = wp.i18n; // Import __() from wp.i18n
const { registerBlockType } = wp.blocks; // Import registerBlockType() from wp.blocks
const { RichText } = wp.blockEditor
const { withColors } = wp.blockEditor
const { PanelColorSettings} = wp.blockEditor;
const { InspectorControls } = wp.blockEditor;
const { PanelBody } = wp.components;
registerBlockType( 'cgb/block-my-block', {
title: __( 'my-block - CGB Block' ),
icon: 'shield',
category: 'common',
keywords: [
__( 'my-block — CGB Block' ),
__( 'CGB Example' ),
__( 'create-guten-block' ),
],
attributes: {
align: {
type: 'string',
default: 'full',
},
link_text: {
selector: 'a',
source: 'children',
},
link_url: {
selector: 'a',
source: 'attribute',
attribute: 'href',
},
txtColor: {
type: 'string'
},
bgcolor: {
type: 'string'
},
},
supports: {
align:true,
//align: ['wide','full'], // limit only to these
},
getEditWrapperProps() {
return {
'data-align': 'full',
};
},
edit: ( props ) => {
let link_text = props.attributes.link_text
let link_url = props.attributes.link_url
let txtColor = props.attributes.txtColor
let bgColor = props.attributes.bgColor
function onChangeContentURL ( content ) {
props.setAttributes({link_url: content})
}
function onChangeContentName ( content ) {
props.setAttributes({link_text: content})
}
function onChangeBGColor ( content ) {
props.setAttributes({bgColor: content})
}
function onChangeColor ( content ) {
props.setAttributes({txtColor: content})
}
return (
<div className={ props.className } style={{ backgroundColor:bgColor, color: txtColor }}>
<InspectorControls key= { 'inspector' } >
<PanelBody>
<PanelColorSettings
title={ __('Title Color', 'tar') }
colorSettings= { [
{
value: txtColor,
onChange: (colorValue) => onChangeColor ( colorValue ),
label: __('Color', 'tar'),
},
] }
/>
<PanelColorSettings
title={ __('Background Color', 'tar') }
colorSettings= { [
{
value: bgColor,
onChange: (colorValue) => onChangeBGColor ( colorValue ),
label: __('Color', 'tar'),
},
] }
/>
</PanelBody>
</InspectorControls>
<p>Sample Link Block</p>
<label>Name:</label>
<RichText
className={props.className} // Automatic class: gutenberg-blocks-sample-block-editable
onChange={onChangeContentName} // onChange event callback
value={link_text} // Binding
placeholder="Name of the link"
/>
<label>URL:</label>
<RichText
format="string" // Default is 'element'. Wouldn't work for a tag attribute
className={props.className} // Automatic class: gutenberg-blocks-sample-block-editable
onChange={onChangeContentURL} // onChange event callback
value={link_url} // Binding
placeholder="URL of the link"
/>
<p>— Hello from the backend.!!</p>
</div>
);
},
/**
* The save function defines the way in which the different attributes should be combined
* into the final markup, which is then serialized by Gutenberg into post_content.
*
* The "save" property must be specified and must be a valid function.
*
* #link https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/
*
* #param {Object} props Props.
* #returns {Mixed} JSX Frontend HTML.
*/
save: ( props ) => {
let txtColor = props.attributes.txtColor
let bgColor = props.attributes.bgColor
return (
<div className={ props.className } style={{ backgroundColor:bgColor, color:txtColor }} >
<p>— Hello from the frontend.</p>
<a href={props.attributes.link_url}>{props.attributes.link_text}</a>
</div>
);
},
} );
The console error looks like the POST and the SAVE data are different causing the error.
The message is:
Block validation: Block validation failed for `cgb/block-my-block` ({name: "cgb/block-my-block", icon: {…}, attributes: {…}, keywords: Array(3), save: ƒ, …}).
Content generated by `save` function:
<div class="wp-block-cgb-block-my-block alignfull" style="color:#000000"><p>— Hello from the frontend.</p><a></a></div>
Content retrieved from post body:
<div class="wp-block-cgb-block-my-block alignfull" style="background-color:#cd2653;color:#000000"><p>— Hello from the frontend.</p><a></a></div>
So it looks to me as if the problem is with the Save function style tag's on the root element.
<div className={ props.className } style={{ backgroundColor:bgColor, color:txtColor }} >
I have removed one style only leaving the other and it works. Putting the other back in and it breaks. Have I included this multiple style wrong? If so what is the convention for adding multiple styles that save on the root element? Also I am new and I am learning from tutorials and reading the Gutenburg github docs. If there is something rudimentary I am doing wrong please let me know.
The block validation issue is caused by a small typo where your attribute bgcolor (case sensitive) is called as bgColor in edit() and save().
Your code shows you are on the right path with creating your own custom Gutenberg block, so I'd like to share a suggestion to use array destructuring with props to make your code much easier to read and maintain. Your custom onChange functions which just call setAttributes()can also be removed in favor of calling setAttributes directly, this reduces how much code you need to write and reduces the chance of typos too..
Eg:
// Import CSS.
import './editor.scss';
import './style.scss';
const { __ } = wp.i18n; // Import __() from wp.i18n
const { registerBlockType } = wp.blocks; // Import registerBlockType() from wp.blocks
const { RichText } = wp.blockEditor
const { withColors } = wp.blockEditor
const { PanelColorSettings } = wp.blockEditor;
const { InspectorControls } = wp.blockEditor;
const { PanelBody } = wp.components;
registerBlockType('cgb/block-my-block', {
title: __('my-block - CGB Block'),
icon: 'shield',
category: 'common',
keywords: [
__('my-block — CGB Block'),
__('CGB Example'),
__('create-guten-block'),
],
attributes: {
align: {
type: 'string',
default: 'full',
},
link_text: {
selector: 'a',
source: 'children',
},
link_url: {
selector: 'a',
source: 'attribute',
attribute: 'href',
},
txtColor: {
type: 'string',
},
bgColor: {
type: 'string',
},
},
supports: {
align: true,
//align: ['wide','full'], // limit only to these
},
getEditWrapperProps() {
return {
'data-align': 'full',
};
},
// Use array destructuring of props
edit: ({ attributes, className, setAttributes } = props) => {
// Use array destructuring of the attributes
const { link_text, link_url, txtColor, bgColor } = attributes;
// Removed custom onChange functions that just call setAttributes
return (
<div className={className} style={{ backgroundColor: bgColor, color: txtColor }}>
<InspectorControls key={'inspector'} >
<PanelBody>
<PanelColorSettings
title={__('Title Color', 'tar')}
colorSettings={[
{
value: txtColor,
onChange: (colorValue) => setAttributes({ txtColor: colorValue }),
label: __('Color', 'tar'),
},
]}
/>
<PanelColorSettings
title={__('Background Color', 'tar')}
colorSettings={[
{
value: bgColor,
onChange: (colorValue) => setAttributes({ bgColor: colorValue }),
label: __('Color', 'tar'),
},
]}
/>
</PanelBody>
</InspectorControls>
<p>Sample Link Block</p>
<label>Name:</label>
<RichText
className={className} // Automatic class: gutenberg-blocks-sample-block-editable
onChange={(content) => setAttributes({ link_text: content })} // onChange event callback
value={link_text} // Binding
placeholder="Name of the link"
/>
<label>URL:</label>
<RichText
format="string" // Default is 'element'. Wouldn't work for a tag attribute
className={className} // Automatic class: gutenberg-blocks-sample-block-editable
onChange={(content) => setAttributes({ link_url: content })} // onChange event callback
value={link_url} // Binding
placeholder="URL of the link"
/>
<p>— Hello from the backend.!!</p>
</div>
);
},
/**
* The save function defines the way in which the different attributes should be combined
* into the final markup, which is then serialized by Gutenberg into post_content.
*
* The "save" property must be specified and must be a valid function.
*
* #link https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/
*
* #param {Object} props Props.
* #returns {Mixed} JSX Frontend HTML.
*/
// Use array destructuring of props
save: ({ attributes, className } = props) => {
// Use array destructuring of the attributes
const { txtColor, bgColor, link_url, link_text } = attributes;
return (
<div className={className} style={{ backgroundColor: bgColor, color: txtColor }}>
<p>— Hello from the frontend.</p>
<a href={link_url}>{link_text}</a>
</div >
);
},
});
I've been searching for a few days now and can't find anything. I'm using material-table in React but can't figure how to add css in headers (columns of the table) and the content [like changing font size, width and making table rows of striped background].
Can anyone tell me how I can do it?
For reference, here's the current Material table I have and I want to make the rows striped and change the look of headers (column names)
<MaterialTable
title="Title"
columns={this.state.columns}
data={newDataTable}
options={{
selection: true
}}
options={{
search: false,
sorting: true
}}
actions={[
{
icon: () => <Checkbox />,
tooltip: 'checkbox'
},
{
icon: () => <InfoIcon />,
tooltip: 'info',
onClick: (event, item) => {
this.setState({
isOpen: true,
selectedItem: item
});
}
}
]}
/>
</div>
Edit: Also, can't figure out how to change the content of the rows. Like for example I want to add an icon in front of every item corresponding to its data. Currently, I'm just using a js array to display the static data but I can't edit it.
Just define the styles within the columns property:
this.setState({
columns: {[
{
title: 'Name', field: 'name',
cellStyle: {
backgroundColor: '#039be5',
color: '#FFF'
},
headerStyle: {
backgroundColor: '#039be5',
}
},
// Other columns
]}
});
See https://material-table.com/#/docs/features/styling
if you want to add an icon inside your data( rows/cells), you can use built-in render callback in columns definition like this :
const handleTableColumns = (cols) => {
return cols.map((col) => ({
...col,
render: (rowData) => {
return (
<span style = {{display: 'flex'}} >
<KeyboardArrowRightIcon />
{ rowData[col.id]}
</span>
);
};
}))
};
this will insert the icon <KeyboardArrowRightIcon /> in front of every cell of each row, so in materialTable component you have to use handleTableColumns as columns :
<MaterialTable
style={{ padding: '0 8px' }}
columns={this.handleTableColumns(this.state.columns)}
data={data}
...
...
/>
Options can be passed with a key rowstyle. Where you can configure the background colour.
< MaterialTable title = "Title"
columns = {
this.state.columns
}
data = {
newDataTable
}
options = {
{
selection: true,
rowStyle: (row) => {
const rowStyling = {
fontSize: "14px",
fontFamily: "latoregular"
};
if (row.sl % 2) {
rowStyling.backgroundColor = "#f2f2f2";
}
return rowStyling;
},
}
}
options = {
{
search: false,
sorting: true,
}
}
actions = {
[{
icon: () =>
<
Checkbox / > ,
tooltip: "checkbox",
}, {
icon: () =>
<
InfoIcon / > ,
tooltip: "info",
onClick: (event, item) => {
this.setState({
isOpen: true,
selectedItem: item,
});
},
}, ]
}
/>;
I am trying to add a padding Inspector Control to all core block of the new Gutenberg Wordpress Editor. I have created the Control on the Editor and now I am trying to apply this style to the block element its self.
But I keep getting This block contains unexpected or invalid content. Error on the blocks. Can anyone help me out here on what exactly I am not doing?
var paddingEditor = wp.compose.createHigherOrderComponent(function(
BlockEdit
) {
return function(props) {
var padding = props.attributes.padding || 0;
handleChange = name => newValue => {
if (props) {
props.setAttributes({ [name]: newValue });
}
};
return el(
Fragment,
{},
el(BlockEdit, props),
el(
editor.InspectorControls,
{},
el(
components.PanelBody,
{
title: "Padding",
className: "",
initialOpen: false
},
el("p", {}, "Padding"),
el(components.TextControl, {
value: padding,
onChange: this.handleChange("padding")
})
)
)
);
};
},
"paddingStyle");
wp.hooks.addFilter(
"editor.BlockEdit",
"my-plugin/padding-style",
paddingStyle
);
function AddPaddingAttribute(element, blockType, attributes) {
Object.assign(blockType.attributes, {
padding: {
type: "string"
}
});
return element;
}
wp.hooks.addFilter(
"blocks.getSaveElement",
"my-plugin/add-padding-attr",
AddPaddingAttribute
);
function AddPaddingStyle(props, blockType, attributes) {
if (attributes.padding) {
props.style = lodash.assign(props.style, { padding: attributes.padding });
}
return props;
}
wp.hooks.addFilter(
"blocks.getSaveContent.extraProps",
"my-plugin/add-background-color-style",
AddPaddingStyle
);
PHP
function register_block_extensions(){
wp_register_script(
'extend-blocks', // Handle.
get_template_directory_uri() . '/js/extend-blocks.js',
array( 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-editor' )
);
wp_enqueue_script( 'extend-blocks' );
}
add_action('enqueue_block_editor_assets', 'register_block_extensions');
Your editor.BlockEdit looks right, but it's hard for me to parse the older syntax. Assuming it is correct, you'll need to replace blocks.getSaveElement with this:
function AddPaddingAttribute(props) {
if (props.attributes) { // Some modules don't have attributes
props.attributes = Object.assign(
props.attributes,
{
padding: {}
}
);
}
return props;
}
wp.hooks.addFilter(
'blocks.registerBlockType',
'my-plugin/add-padding-attr',
AddPaddingAttribute
);
And modify blocks.getSaveContent.extraProps to this:
function AddPaddingStyle(props, blockType, attributes) {
return Object.assign(
props,
{
style: {
padding: attributes.padding
}
}
);
}
wp.hooks.addFilter(
"blocks.getSaveContent.extraProps",
"my-plugin/add-background-color-style",
AddPaddingStyle
);