I'm trying to create a custom WordPress gutenberg block that allows me to select any image and text.
I can create the block using this code and I can see content on the frontend of the site however the block crashes if I view the block to edit it.
The console gives the following error: -
Block validation: Block validation failed for cgb/block-imagecta (
Object { name: "cgb/block-imagecta", icon: {…}, attributes: {…}, keywords: (3) […], save: save(), title: "imagecta - CGB Block", category: "common", edit: edit()
}
).
Content generated by save function:
<div class="wp-block-cgb-block-imagecta"><div class="imageCtaBg"><img class="bg" alt="sergserge"/><p></p></div></div>
Content retrieved from post body:
<div class="wp-block-cgb-block-imagecta"><div class="imageCtaBg"><img class="bg" alt="sergserge"/><p>dxfbxdfbxdfbfb</p></div></div>
Is this related to the attributes at all?
/**
* BLOCK: imagecta
*
* Registering a basic block with Gutenberg.
* Simple block, renders and saves the same content without any interactivity.
*/
// 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 {FormFileUpload} = wp.components;
const {RichText, MediaUpload} = wp.blockEditor;
registerBlockType('cgb/block-imagecta', {
title: __('imagecta - CGB Block'),
icon: 'shield',
category: 'common',
keywords: [
__('imagecta — CGB Block'),
__('CGB Example'),
__('create-guten-block'),
],
attributes: {
content: {
type: 'html',
selector: '.captionText',
},
logo: {
type: 'string',
default: null,
},
},
edit: (props) => {
const {attributes: {content}, setAttributes} = props;
function onImageSelect(imageObject) {
setAttributes({
logo: imageObject.sizes.full.url
});
}
const onChangeContent = (newContent) => {
setAttributes({content: newContent});
};
// Creates a <p class='wp-block-cgb-block-imagecta'></p>.
return (
<div className={props.className}>
<MediaUpload
onSelect={onImageSelect}
type="image"
value={logo} // make sure you destructured backgroundImage from props.attributes!
render={({open}) => (
<button onClick={open}>
Upload Image!
</button>
)}
/>
<div className='imageCtaBg'>
<img src={logo} class="bg" alt={'sergserge'}/>
<RichText
tagName={'p'}
className='captionText'
onChange={onChangeContent}
value={content}
placeholder='Enter text...'
/>
</div>
</div>
);
},
save: (props) => {
const {attributes: {content}, setAttributes} = props;
return (
<div className={props.className}>
<div className={'imageCtaBg'}>
<img src={props.attributes.logo} className="bg" alt={'sergserge'}/>
<RichText.Content tagName={"p"} value={props.attributes.content}/>
</div>
</div>
);
},
});
The issue was to do with the content attribute.
Instead of giving the richtext the class of 'captionText' I moved it do a wrapping div and changed the content attribute to the following: -
content: {
type: 'string',
source:'html',
selector: '.captionText',
},
This finds the text inside of that div and allows me to update the block with no validation issues.
Related
I want to have a selected state on a nav item in next 13, I could find no way of getting any context on a server component so ended up with a client component like this
'use client';
import Image from 'next/image';
import Styles from '../styles/header.module.css';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
interface MainRoute {
name: string;
path: string;
index: number;
}
const mainRoutes = [
{ name: 'home', path: '/' },
{ name: 'path 2', path: '/path2' },
{ name: 'path 3', path: '/path3' },
] as MainRoute[];
export default function Header({}) {
const path = usePathname();
return (
<header>
<div className={Styles.header}>
<h1>
App title
</h1>
</div>
<div className={Styles.header}>
<ul id="mainNav">
{mainRoutes.map((route, index) => (
<li key={index}>
<Link
className={path === route.path ? Styles.selected : ''}
href={route.path}
>
{route.name}
</Link>{' '}
{index !== mainRoutes.length - 1 ? <span>|</span> : ''}
</li>
))}
</ul>
</div>
</header>
);
}
Is this the best way to achieve this basic styling?
As far as I am aware, now all of this code has to be shipped over to the client.
I have a Navbar with a Navigation Links, which are highlighted as active, depending on the page a user is on.
import { useRouter } from 'next/dist/client/router'
const navigation = [
{ name: 'Home', url: '/', active: true},
{ name: 'Quiz', url:'/quiz', active: false}
]
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export const Navigation = () => {
const router = useRouter();
return (
<div className="flex space-x-4 justify-self-center">
{navigation.map((item) => (
<a
key={item.name}
href={item.url}
className={`px-3 py-2 rounded-md text-sm font-medium ${
router.asPath === item.url ? "bg-gray-900 text-white" : 'text-gray-300 hover:text-gray-700'
}`}
aria-current={item.active? 'page' : undefined}
>
{item.name}
</a>
))}
</div>
)
}
It works as is should, but I can't create a story for it, because of the Next Router. I get the following error: Cannot read property 'asPath' of null.
I tried to follow the instructions in the answer I found here: How to use NextJS's 'useRouter()' hook inside Storybook , but unfortunately it didn't give me the result I am striving for. So basically my "Story-Navbar" shouldn't redirect, but just highlight the Navigation Link, while I click on it. Is that possible? Here is the story for Navigation:
import { Navigation } from './Navigation';
export default {
title: 'Example/Navigation',
component: Navigation,
};
export const DefautlNavigation = () => {
return (
<Navigation />
)
}
The storybook Next.js router add-on works for useRouter() hook. Here is the example in their docs:
import { withNextRouter } from 'storybook-addon-next-router';
import MyComponentThatHasANextLink from '../component-that-has-a-next-link';
export default {
title: 'My Story',
decorators: [withNextRouter], // not necessary if defined in preview.js
};
// if you have the actions addon
// you can click the links and see the route change events there
export const Example = () => <MyComponentThatHasANextLink />;
Example.story = {
parameters: {
nextRouter: {
path: '/profile/[id]',
asPath: '/profile/lifeiscontent',
query: {
id: 'lifeiscontent',
},
},
},
};
I am trying to build a simple navbar but when I define a setResponsivness function inside my useEffect
I am getting the error Rendered fewer hooks than expected. This may be caused by an accidental early return statement. I looked at similar answers for the same but till wasn't able to fix
Here s my code
import React,{useEffect,useState} from 'react'
import {AppBar ,Toolbar, Container ,makeStyles,Button, IconButton} from '#material-ui/core'
import MenuIcon from '#material-ui/icons/Menu'
const usestyles = makeStyles({
root:{
display:'flex',
justifyContent:'space-between' ,
maxWidth:'700px'
},
menubtn:{
fontFamily: "Work Sans, sans-serif",
fontWeight: 500,
paddingRight:'79px',
color: "white",
textAlign: "left",
},
menuicon:{
edge: "start",color: "inherit",paddingLeft:'0'
}
})
const menudata = [
{
label: "home",
href: "/",
},
{
label: "About",
href: "/about",
},
{
label: "Skill",
href: "/skills",
},
{
label: "Projects",
href: "/projects",
},
{
label: "Contact",
href: "/contact",
},
];
//yet to target link for the smooth scroll
function getmenubuttons(){
const {menubtn} = usestyles();
return menudata.map(({label,href})=>{
return <Button className={menubtn}>{label}</Button>
})
}
//to display navbar on desktop screen
function displaydesktop(){
const { root } = usestyles() //destructuring our custom defined css classes
return <Toolbar ><Container maxWidth={false} className={root}>{getmenubuttons()}</Container> </Toolbar>
}
//to display navbar on mobile screen
function displaymobile(){
const {menuicon} =usestyles() ;
return <Toolbar><IconButton className={menuicon}><MenuIcon /> </IconButton></Toolbar>
}
function Navbar() {
const [state, setState] = useState({mobileview:false});
const {mobileview} = state;
useEffect(() => {
const setResponsiveness = () => {
return window.innerWidth < 900
? setState((prevState) => ({ ...prevState, mobileview: true }))
: setState((prevState) => ({ ...prevState, mobileview: false }));
};
setResponsiveness();
window.addEventListener("resize", () => setResponsiveness());
}, []);
return (
<div>
<AppBar> {mobileview?displaymobile():displaydesktop()} </AppBar>
</div>
)
}
export default Navbar;
Your problem seems to be here
{mobileview?displaymobile():displaydesktop()}
For example the displaymobile function inside uses hooks right (usestyles)? Then it means you are rendering hooks inside conditions (mobileview being condition) which is not allowed by rules of hooks.
You can fix it like this:
<div>
<AppBar> {mobileview ? <Displaymobile /> : <Displaydesktop />} </AppBar>
</div>
Also change definition of component using capital letters as that is how react refers to components. e.g.
function Displaydesktop() {
const { root } = usestyles(); //destructuring our custom defined css classes
return (
<Toolbar>
<Container maxWidth={false} className={root}>
{getmenubuttons()}
</Container>{" "}
</Toolbar>
);
}
Now we consume them as components. Probably when you used lower case letters and called those as functions in your render, react interpreted them as custom hooks, hence the warnings.
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 >
);
},
});
Goal
I need to modify the CSS for the reactjs-dropdown-component.
Background/Overview
I've downloaded the dropdown, imported it, and have it fully operational in react. However, I'm fairly new to coding and haven't yet been in a situation where I need to do significant styling to a downloaded component. Do I just recreate the stylesheet for it and import it?
The css for this component is in this Github repo: https://github.com/dbilgili/Custom-ReactJS-Dropdown-Components/blob/master/src/styles/stylus/dropdown.styl
The instructions I followed for downloading/using this dropdown are here: https://github.com/dbilgili/Custom-ReactJS-Dropdown-Components
I'm not sure that it's necessary but here is code where I'm using the dropdown
import React from 'react';
import './EventContainer.css';
import { Dropdown } from 'reactjs-dropdown-component';
import { dining } from './EventContainerIcons.js';
class EventContainer extends React.Component {
constructor(props){
super(props);
this.state = {
...props.event,
activityIcon: [
{
id: 0,
title: <img src={dining} width="64" height="64" alt="dining icon" />,
selected: false,
key: 'activityIcon'
},
{
id: 1,
title: 'Orange',
selected: false,
key: 'activityIcon'
},
{
id: 2,
title: 'Strawberry',
selected: false,
key: 'activityIcon'
}
],
};
}
handleTypeChange = (e) => {
this.setState({
type: e.target.value
})
}
handleTimeChange = (e) => {
this.setState({
time: e.target.value
})
}
handleSummaryChange = (e) => {
this.setState({
summary: e.target.value
})
}
handleNotesChange = (e) => {
this.setState({
notes: e.target.value
})
}
resetThenSet = (id, key) => {
let temp = this.state[key];
temp.forEach(item => (item.selected = false));
temp[id].selected = true;
this.setState({
[key]: temp
});
};
render(){
return (
<div className="eventContainer-flex">
<Dropdown
title="Event Type"
list={this.state.activityIcon}
resetThenSet={this.resetThenSet}
/>
<div>
<input
className="time-input-styling"
type="time"
value={this.state.time}
onChange={this.handleTimeChange}/>
</div>
<div>
<textarea
className="textarea-styling"
/*placeholder="Write summary here"*/
value={this.state.summary}
onChange={this.handleSummaryChange}
cols={60}
rows={3} />
</div>
<div>
<textarea
className="textarea-styling"
/*placeholder="Write notes here"*/
value={this.state.notes}
onChange={this.handleNotesChange}
cols={30}
rows={3} />
</div>
</div>
)
}
}
export default EventContainer;
In the documentation, they alreayd said it:
Refer to the following styling file for overriding the default styles.
You can create your own styling file with the same class names in
order to do your custom styling.
So, you have to create your css file, with the classes and override the classes you want.