I am creating a custom input via the component method with formkit.
I have the computed which looks like this
<template>
<label
class="InputCheckboxFeature"
:for="props.context.id"
>
<input
:id="props.context.id"
type="checkbox"
class="sr-only"
:checked="checked"
#input="handleInput"
:value="props.context._value"
/>
<span class="InputCheckboxFeature__indicator">
<span class="space-x-4 inline-flex items-center">
<span
v-if="props.context.attrs.icon"
class="InputCheckboxFeature__icon"
v-html="props.context.attrs.icon"
>
</span>
<span class="inline-block">
{{ props.context.label }}
</span>
</span>
<ButtonInfo
v-if="props.context.tooltip"
class="flex-shrink-0"
:text="props.context.tooltip"
aria-label="More Info"
/>
</span>
</label>
</template>
I then have a plugin that strips out all the HTML elements so the custom input will only output the component markup
export const createCleanInputPlugin = (): FormKitPlugin => {
return (node: FormKitNode) => {
node.on('created', () => {
const { props } = node;
if (
!(
props.type === InputType.FEATURED_CHECKBOX &&
props.definition &&
typeof props.definition.schema === 'function'
)
) {
return;
}
const definition = { ...props.definition };
const schema = definition.schema as FormKitExtendableSchemaRoot;
// We replace the schema function with our own higher-order-function
definition.schema = function (extensions = {}) {
const ext = {
...extensions,
...{
inner: { $el: null },
outer: { $el: null },
label: { $el: null },
wrapper: { $el: null },
},
};
// Finally we call the original schema, with our extensions applied
return schema(ext);
};
// Now we replace the input definition
props.definition = definition as FormKitTypeDefinition;
});
};
};
The problem I am facing is that when the element is rendered it is outputting the label twice.
Is it possible to hide the label, and only have it render in the component?
I want to show my list with their own styling depending on the text they contain.
function TypeFilter() {
const { filterOpen, TypeList } = useGlobalContext();
return (
<div className={filterOpen ? "ft-filter show-filter" : "ft-filter"}>
<div className="filter-wrapper">
{TypeList.map((type, index) => {
const { name } = type;
return (
<div className="filter-btn" key={index}>
{name}
</div>
);
})}
</div>
</div>
);
}
Here, the text is on the {name}, is there a way for me to achieve that?
You can conditionally add class name to your div. You need to add styles in your css file for that new classnames.
function TypeFilter() {
const { filterOpen, TypeList } = useGlobalContext();
return (
<div className={filterOpen ? "ft-filter show-filter" : "ft-filter"}>
<div className="filter-wrapper">
{TypeList.map((type, index) => {
const { name } = type;
const nameClass = name === 'your-condition' ? 'red' : 'blue';
return (
<div className=`filter-btn ${nameClass}` key={index}>
{name}
</div>
);
})}
</div>
</div>
);
}
If name matches the criteria, assign it the desired class
{TypeList.map((type, index) => {
const { name } = type;
let textClass;
if (name === "banana") textClass = "banana-class";
return (
<div className={`filter-btn ${textClass}`} key={index}>
{name}
</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 >
);
},
});
As shown in the code snippet below, I am trying to append HTML or JSX to RichText content in vain.
const { registerBlockType } = wp.blocks;
const { RichText } = wp.editor;
registerBlockType( /* ... */, {
// ...
attributes: {
content: {
type: 'array',
source: 'children',
selector: 'p',
default: [],
},
},
edit( { className, attributes, setAttributes } ) {
const updateContentWithString = ( content ) => {
setAttributes( { content: content.concat( 'test' ) } );
}
const updateContentWithHTML = ( content ) => {
setAttributes( { content: content.concat( <Button isDefault >Test Button</Button> ) } );
}
return (
<RichText
tagName="p"
className={ className }
value={ attributes.content }
onChange={ updateContentWithString } // updateContentWithString works fine BUT updateContentWithHTML doesn't work at all
/>
);
},
save( { attributes } ) {
return <RichText.Content tagName="p" value={ attributes.content } />;
}
} );
I am wondering why updateContentWithString() works fine BUT updateContentWithHTML() doesn't work at all.
Kindly guide me on this.
Thanks
Try
setAttributes( { content: content.concat( '<Button isDefault >Test Button</Button> )' } );
In JSX <Button/> will be preprocessed/conversed to React.createElement to create component.
Note: In JSX <Button/> differs from <button/> (component vs. html)
UPDATE:
Above was related to react/JSX only.
I don't know or used Gutenberg (yet) but according to this html elements should be passed as objects, not html source. Probably you should use wp.element.createElement or RichText or even define/use own custom block types.
Search for method of editing/updating/creating html (elements) in Gutenberg.
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?