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>
)
}
})```
Related
import { HTMLSelect } from '#blueprintjs/core';
let value = '';
const dropdownPanelSelector = () => {
const options = [
{ label: 'Map', value: '' },
{ label: 'Camera', value: 'Camera' },
];
const onChange = (e) => {
console.log(e, e.target.value);
value = e.target.value;
};
return (
<HTMLSelect
className={css.dropdownPanel}
options={options}
selected={value}
onChange={onChange}
/>
);
};
I would like to change the selected box's text and none of the others.
.dropdown select (unsure here){
font-size:2em:
}
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 have been tasked with preventing addFilter() from running on certain custom post types using the new Gutenberg API and not any WP PHP. It's currently fed into the editor.PostFeaturedImage hook, meaning it fires every time the Gutenberg editor loads the Featured Image box.
The Filter calls a function that adds a dropdown menu underneath the featured image box to allow users to pick a contributor (which are themselves custom post types) to credit the image to.
The filter should not run on the contributor custom post type, but should run on other custom post types. There should still be a featured image box for the contributor custom post type, but no dropdown.
Letting the hook fire and then canceling within the function works but the function itself is resource heavy and the directive is to prevent the function from firing at all. The idea being that the built-in hook/function would fire instead.
Borrowing from this ticket, I attempted to place the main function setFeaturedImageArtist within an anonymous function that also printed the post type to console in addFilter(). I was able to get the post type to print, but calling setFeaturedImageArtist function didn't work as expected.
wp.hooks.addFilter( 'editor.PostFeaturedImage', 'blocks/featured-image-artist', function() {
console.log(wp.data.select("core/editor").getCurrentPostType())
return setFeaturedImageArtist()
});
Placing setFeaturedImageArtistin a wrapper function like so didn't work either. I'm assuming it's because it's the same thing.
function checkPostType() {
console.log(wp.data.select("core/editor").getCurrentPostType())
return setFeaturedImageArtist()
}
wp.hooks.addFilter( 'editor.PostFeaturedImage', 'blocks/featured-image-artist', checkPostType);
Here is the component the filter is triggering:
function setFeaturedImageArtist( OriginalComponent ) {
return ( props ) => {
const artistSelect = compose.compose(
withDispatch( function( dispatch, props ) {
return {
setMetaValue: function( metaValue ) {
dispatch( 'core/editor' ).editPost(
{ meta: { 'featured-image-artist': metaValue } }
);
}
}
} ),
withSelect( function( select, props ) {
let query = {
per_page : 20,
metaKey : '_author_type',
metaValue : 'artist'
};
let postType = select("core/editor").getCurrentPostType();
if ( postType === 'contributor' ) {
return null
}
// Please see below
// if ( postType === 'contributor' ) {
// return {
// postType
// }
// }
return {
posts: select( 'core' ).getEntityRecords( 'postType', 'contributor', query ),
metaValue: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ 'featured-image-artist' ],
}
} ) )( function( props ) {
var options = [];
// This works in removing the dropdown for authors/artists
// if (props.postType === 'contributor'){
// return null
// }
if( props.posts ) {
options.push( { value: 0, label: __( 'Select an artist', 'blocks' ) } );
props.posts.forEach((post) => {
options.push({value:post.id, label:post.title.rendered});
});
} else {
options.push( { value: 0, label: __( 'Loading artists...', 'blocks' ) } )
}
return el( SelectControl,
{
label: __( 'Art Credit:', 'blocks' ),
options : options,
onChange: ( content ) => {
props.setMetaValue( content );
},
value: props.metaValue,
}
);
}
);
return (
el( 'div', { }, [
el( OriginalComponent, props ),
el( artistSelect )
] )
);
}
}
wp.hooks.addFilter( 'editor.PostFeaturedImage', 'blocks/featured-image-artist', setFeaturedImageArtist );
Here is the redacted component the filter is triggering:
function setFeaturedImageArtist( OriginalComponent ) {
return ( props ) => {
const artistSelect = compose.compose(
...
)( function( props ) {
... // Cancelling out here works, but resources are loaded by this point.
});
return (
el( 'div', { }, [
el( OriginalComponent, props ),
el( artistSelect )
])
);
}
}
wp.hooks.addFilter( 'editor.PostFeaturedImage', 'blocks/featured-image-artist', setFeaturedImageArtist );
This is the React error being received:
Element type is invalid: expected a string (for built-in components) or
a class/function (for composite components) but got: undefined.
I'm not sure what the best approach to this would be, and the documentation is scant/barely applicable. Is creating a custom hook that mimics editor.PostFeaturedImage but only fires on certain custom post types a possibility? Or is there some way to call a function like setFeaturedImageArtist within a wrapper that checks the post type?
Wordpress Hooks on Github
Hooks in Wordpress Handbook
I tried to recreate the script and fixed some issues:
const { createElement: el } = wp.element;
const { compose } = wp.compose;
const { withSelect, withDispatch } = wp.data;
const { SelectControl } = wp.components;
const { __ } = wp.i18n;
const ArtistSelect = compose(
withDispatch(function(dispatch, props) {
return {
setMetaValue: function(metaValue) {
dispatch("core/editor").editPost({
meta: { "featured-image-artist": metaValue }
});
}
};
}),
withSelect(function(select, props) {
let query = {
per_page: 20,
metaKey: "_author_type",
metaValue: "artist"
};
return {
postType: select("core/editor").getCurrentPostType(),
posts: select("core").getEntityRecords("postType", "contributor", query),
metaValue: select("core/editor").getEditedPostAttribute("meta")[
"featured-image-artist"
]
};
})
)(function(props) {
var options = [];
// This works in removing the dropdown for authors/artists
if (props.postType === "contributor") {
return null;
}
if (props.posts) {
options.push({ value: 0, label: __("Select an artist", "blocks") });
props.posts.forEach(post => {
options.push({ value: post.id, label: post.title.rendered });
});
} else {
options.push({ value: 0, label: __("Loading artists...", "blocks") });
}
return el(SelectControl, {
label: __("Art Credit:", "blocks"),
options: options,
onChange: content => {
props.setMetaValue(content);
},
value: props.metaValue
});
});
function setFeaturedImageArtist(OriginalComponent) {
return props => {
return el("div", {}, [el(OriginalComponent, props), el(ArtistSelect)]);
};
}
wp.hooks.addFilter(
"editor.PostFeaturedImage",
"blocks/featured-image-artist",
setFeaturedImageArtist
);
ArtistSelect is a component so we take it outside of setFeaturedImageArtist function. withSelect had a check for the postType that made it return null. Instead of that we pass that variable and then return null in the components render. An alternative would be to check inside setFeaturedImageArtist. This is a fixed version using JSX. Hope its clear:
const { compose } = wp.compose;
const { withSelect, withDispatch, select } = wp.data;
const { SelectControl } = wp.components;
const { __ } = wp.i18n;
const { addFilter } = wp.hooks;
const ArtistSelect = compose(
withDispatch(dispatch => {
return {
setMetaValue: metaValue => {
dispatch("core/editor").editPost({
meta: { "featured-image-artist": metaValue }
});
}
};
}),
withSelect(select => {
const query = {
per_page: 20,
metaKey: "_author_type",
metaValue: "artist"
};
return {
posts: select("core").getEntityRecords("postType", "contributor", query),
metaValue: select("core/editor").getEditedPostAttribute("meta")[
"featured-image-artist"
]
};
})
)(props => {
const { posts, setMetaValue, metaValue } = props;
const options = [];
if (posts) {
options.push({ value: 0, label: __("Select an artist", "blocks") });
posts.forEach(post => {
options.push({ value: post.id, label: post.title.rendered });
});
} else {
options.push({ value: 0, label: __("Loading artists...", "blocks") });
}
return (
<SelectControl
label={__("Art Credit:", "blocks")}
options={options}
onChange={content => setMetaValue(content)}
value={metaValue}
/>
);
});
const setFeaturedImageArtist = OriginalComponent => {
return props => {
const post_type = select("core/editor").getCurrentPostType();
if (post_type === "contributor") {
return <OriginalComponent {...props} />;
}
return (
<div>
<OriginalComponent {...props} />
<ArtistSelect />
</div>
);
};
};
wp.hooks.addFilter(
"editor.PostFeaturedImage",
"blocks/featured-image-artist",
setFeaturedImageArtist
);
I have a block that renders titles of all existing posts in the <Modal/>. I retrieve it from the server using <ServerSideRender/> (that returns plain html). I want to be able to choose one of the titles (ideally save it in the postTitle attribute) and display it in the block.
attributes: {
postTitle: {
type: 'string'
}
},
edit(props) {
const { attributes } = props;
const { postTitle } = props.attributes;
const MyModal = withState( {
isOpen: false,
} )
( ( { isOpen, setState } ) => (
<div>
<Button isDefault onClick={ () => setState( { isOpen: true } ) }>Choose</Button>
{ isOpen ?
<Modal onRequestClose={ () => setState( { isOpen: false } ) }>
<ServerSideRender
block="my-blocks/wordpress-title"
attributes={attributes}
/>
</Modal>
: null }
</div>
) );
return ([
<InspectorControls>
<div>
<strong>Choose Wordpress title:</strong>
<MyModal/>
</div>
</InspectorControls>,
]);
},
Is there any sensible way to retrieve data from a server, so it was possible to operate on it within a block?