I am working in wordpress and trying to create a custom gutenberg block with react. I am using the and that is working fine. I have the following fields that requires input like text or a image or a star rating:
Name:,
Workfunction:,
Image:,
Rating:,
Testimonial:
But the output in the editor field isn’t working properly as i wanted. So what is happening:
I put the info in the the info should be displayed in the editor and that is working. So if i add 1 block it’s working like a charm, so you get this:
Name: ben
Workfunction: DEV
Image: media
Rating: 5 stars (i choose 5 stars)
Testimonial: story
When i add a second the following is happening based on the fields i fill in.
Name: ben
Name: test
Workfunction: DEV
Workfunction: DEV
Image: media
Image: media
Rating: i choose 5 stars again, so the output is 10
Rating: i choose 5 stars again, so the output is 10
Testimonial: story
Testimonial: story
Name: ben
Name: test
Workfunction: DEV
Workfunction: DEV
Image: media
Image: media
Rating: i choose 5 stars again, so the output is 10
Rating: i choose 5 stars again, so the output is 10
Testimonial: story
Testimonial: story
It seems te duplicate and add to the same fields. What i am trying to get is the following:
Name: ben
Workfunction: DEV
Image: media
Rating: 5
Testimonial: story
Name: test
Workfunction: DEV
Image: media
Rating: 5
Testimonial: story
This is all happening in the return area and above i think. Could some on explain to me how to resolve this problem and what i have done wrong?
Here is mine code for a better understanding:
/**
* BLOCK: project-drie
*
* 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';
import React from 'react';
import ReactDom from 'react-dom';
import { FaStar } from "react-icons/fa";
import {Helmet} from "react-helmet";
// import components
import { useBlockProps, RichText, InnerBlocks, MediaUpload, InspectorControls, MediaUploadCheck } from '@wordpress/block-editor';
const { __ } = wp.i18n; // Import __() from wp.i18n
const { registerBlockType } = wp.blocks; // Import registerBlockType() from wp.blocks
const { Button, IconButton, PanelBody, TextareaControl, TextControl, SelectControl, CustomSelectControl } = wp.components;
const { Fragment, useState } = wp.element;
/**
* Register: aa Gutenberg Block.
*
* Registers a new block provided a unique name and an object defining its
* behavior. Once registered, the block is made editor as an option to any
* editor interface where blocks are implemented.
*
* @link https://wordpress.org/gutenberg/handbook/block-api/
* @param {string} name Block name.
* @param {Object} settings Block settings.
* @return {?WPBlock} The block, if it has been successfully
* registered; otherwise `undefined`.
*/
registerBlockType( 'cgb/block-project-drie-testimonials', {
title: __( 'project-drie - Testimonials' ), // Block title.
icon: 'shield',
category: 'project drie',
supports: {
align: true,
},
keywords: [
__( 'project-drie — CGB testimonials' ),
__( 'testimonials' ),
__( 'testimonial' ),
],
attributes: {
title: {
type: 'string',
source: 'text',
selector: 'figcaption',
},
subtitle: {
type: 'string',
source: 'text',
selector: 'figcaption',
},
testimonial: {
type: 'array',
default: [],
},
},
/**
* The edit function describes the structure of your block in the context of the editor.
* This represents what the editor will render when the block is used.
*
* The "edit" property must be a valid function.
*
* @link https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/
*
* @param {Object} props Props.
* @returns {Mixed} JSX Component.
*/
edit: props => {
const { attributes: {title, subtitle, testimonial, media, selectcontrol}, className, setAttributes } = props;
const onChangeTitle = title => { setAttributes ( { title } ) };
const onChangeSubtitle = subtitle => { setAttributes ( { subtitle } ) };
const onSelectImage = ( media ) => { setAttributes( { media: media} );};
const handleAddTestimonial = () => {
const testimonial = [ ...props.attributes.testimonial ];
testimonial.push( {
author: '',
position: '',
testimonial: '',
media: '',
selectcontrol: '',
} );
props.setAttributes( { testimonial } );
};
const handleRemoveTestimonial = ( index ) => {
const testimonial = [ ...props.attributes.testimonial ];
testimonial.splice( index, 1 );
props.setAttributes( { testimonial } );
};
const handleTestimonialChange = ( key, value, index ) => {
const testimonial = [ ...props.attributes.testimonial ];
testimonial[ index ][key] = value;
props.setAttributes( { testimonial } );
};
let testimonialFields,
testimonialDisplay;
let authorFields,
authorDisplay;
let positionFields,
positionDisplay;
let mediaFields,
mediaDisplay;
let selectcontrolFields,
selectcontrolDisplay;
if ( props.attributes.testimonial.length ) {
testimonialFields = props.attributes.testimonial.map( ( testimonial, index ) => {
return <Fragment key={ index }>
<TextControl
className="author__react__testimonials"
placeholder="Add testimonial."
value={ testimonial.testimonial }
onChange={ ( value ) => handleTestimonialChange('testimonial', value, index ) }
/>
<TextControl
className="author__react__name"
placeholder="Add Author Name. Example: Ben"
value={ testimonial.author }
onChange={ ( value ) => handleTestimonialChange('author', value, index ) }
/>
<TextControl
className="author__react__name"
placeholder="Add Function. Example: CEO."
value={ testimonial.position }
onChange={ ( value ) => handleTestimonialChange('position', value, index ) }
/>
<MediaUpload
onSelect={ (value) => handleTestimonialChange('media', value, index ) }
allowedTypes="image"
value={ testimonial.media.id }
render={ ( { open } ) => (
<Button className={ testimonial.media.length > 0 ? 'image-button' : 'button button-large' } onClick={ open }>
{ testimonial.media.length > 0 ? __( 'Upload Image', 'gutenberg-examples' ) : <img src={ testimonial.media.url } alt={ __( 'Upload Recipe Image', 'gutenberg-examples' ) } /> }
</Button>
) }
/>
<SelectControl
label={ __( 'Press ctrl to multiple stars:' ) }
value={ testimonial.selectcontrol } // e.g: value = [ 'a', 'c' ]
onChange={ (value) => handleTestimonialChange('selectcontrol', value, index ) }
options={ [
{ value: null, label: 'Select a Star', disabled: false },
{ value: 'star' , label: 'Add 1 star' },
{ value: 'star2', label: 'Add 2 stars' },
{ value: 'star3', label: 'Add 3 stars' },
{ value: 'star4', label: 'Add 4 stars' },
{ value: 'star5', label: 'Add 5 stars' },
] }
/>
<IconButton
className="testimonials__remove__react__testimonial"
icon="no-alt"
label="Delete location"
onClick={ () => handleRemoveTestimonial( index ) }
/>
</Fragment>;
} );
testimonialDisplay = props.attributes.testimonial.map( ( testimonial, index ) => {
return <p key={ index }>{ testimonial.testimonial } </p>;
} );
authorDisplay = props.attributes.testimonial.map( ( testimonial, index ) => {
return <p key={ index }>{ testimonial.author } </p>;
} );
positionDisplay = props.attributes.testimonial.map( ( testimonial, index ) => {
return <p key={ index }>{testimonial.position}</p>;
} );
mediaDisplay = props.attributes.testimonial.map( ( testimonial, index ) => {
return <img key={index} src={testimonial.media.url} alt={testimonial.media.alt} />
} );
selectcontrolDisplay = props.attributes.testimonial.map( ( testimonial, index ) => {
if (testimonial.selectcontrol == 'star') {
return (testimonial.selectcontrol == 'star') ? <span><i className="fas fa-star checked"></i></span> : testimonial.selectcontrol;
} else if (testimonial.selectcontrol == 'star2') {
return (testimonial.selectcontrol == 'star2') ? <span><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i></span> : testimonial.selectcontrol;
} else if (testimonial.selectcontrol == 'star3') {
return (testimonial.selectcontrol == 'star3') ? <span><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i></span> : testimonial.selectcontrol;
} else if (testimonial.selectcontrol == 'star4') {
return (testimonial.selectcontrol == 'star4') ? <span><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i></span>: testimonial.selectcontrol;
} else if (testimonial.selectcontrol == 'star5') {
return (testimonial.selectcontrol == 'star5') ? <span><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i></span> : testimonial.selectcontrol;
}
} );
}
return [
<div className={ className }>
<InspectorControls key="1">
<PanelBody title={ __( 'Testimonial' ) }>
{ testimonialFields }
<Button
isDefault
onClick={ handleAddTestimonial.bind( this ) }
>
{ __( 'Add Testimonials' ) }
</Button>
</PanelBody>
</InspectorControls>
<div key="2" className={ props.className }>
<div className="wp-block-cgb-block-project-drie-testimonial-info">
<div className="wp-block-cgb-block-project-drie-testimonial-title">
<h2>{ __('Title', 'cgb')}</h2>
<RichText
tagName="div"
className="wp-block-cgb-block-project-drie-testimonial-titles"
placeholder={ __('Add your title here', 'cgb')}
onChange={ onChangeTitle}
value={ title }
/>
</div>
<div className="wp-block-cgb-block-project-drie-testimonial-subtext">
<h2>{ __('Sub-Text', 'cgb')}</h2>
<RichText
tagName="p"
className="wp-block-cgb-block-project-drie-testimonial-subtexts"
value={ subtitle }
onChange={ onChangeSubtitle }
placeholder={ __('Add your sub-text here', 'cgb')}
/>
</div>
</div>
<div className="wp-block-cgb-block-project-drie-testimonial-repeater">
<div className="wp-block-cgb-block-project-drie-testimonial-repeater-title">
<h2>Employee</h2>
</div>
<div className="wp-block-cgb-block-project-drie-testimonial-skillname">
<h3>Author Name:</h3>
<span>{ authorDisplay }</span>
</div>
<div className="wp-block-cgb-block-project-drie-testimonial-percentage">
<h3>workFunction:</h3>
<span>{ positionDisplay }</span>
</div>
<div className="wp-block-cgb-block-project-drie-testimonial-bgimages">
<div className="wp-block-cgb-block-project-drie-testimonial-bgimage">
<h2>Author Image</h2>
{mediaDisplay}
</div>
</div>
<div className="wp-block-cgb-block-project-drie-testimonial-ratings">
<div className="wp-block-cgb-block-project-drie-testimonial-rating">
<h2>Rating</h2>
{selectcontrolDisplay}
</div>
</div>
<div className="wp-block-cgb-block-project-drie-testimonial-testimonials">
<div className="wp-block-cgb-block-project-drie-testimonial-testimonial">
<h2>Review</h2>
{testimonialDisplay}
</div>
</div>
</div>
</div>
</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 ) => {
const {attributes: { title } } = props;
const {attributes: { subtitle } } = props;
const {attributes: { media} } = props;
const {attributes: { testimonial } } = props;
const {attributes: { selectcontrol } } = props;
const selectcontrolDisplay = props.attributes.testimonial.map( ( testimonial, index ) => {
if (testimonial.selectcontrol == 'star') {
return (testimonial.selectcontrol == 'star') ? <span><i className="fas fa-star checked"></i></span> : testimonial.selectcontrol;
} else if (testimonial.selectcontrol == 'star2') {
return (testimonial.selectcontrol == 'star2') ? <span><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i></span> : testimonial.selectcontrol;
} else if (testimonial.selectcontrol == 'star3') {
return (testimonial.selectcontrol == 'star3') ? <span><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i></span> : testimonial.selectcontrol;
} else if (testimonial.selectcontrol == 'star4') {
return (testimonial.selectcontrol == 'star4') ? <span><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i></span>: testimonial.selectcontrol;
} else if (testimonial.selectcontrol == 'star5') {
return (testimonial.selectcontrol == 'star5') ? <span><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i><i className="fas fa-star checked"></i></span> : testimonial.selectcontrol;
}
} );
// const skillnameFields = props.attributes.skillname.map( ( skillname, index ) => {
// return <p key={ index }>{ skillname.skill } { skillname.percentage }</p>;
// } );
return (
<section className="testimonial">
<Helmet>
<script type="text/javascript" src="//cdn.jsdelivr.net/gh/kenwheeler/[email protected]/slick/slick.min.js"></script>
</Helmet>
<div className="container testimonial__container">
<div className="row testimonial__row">
<div className="testimonial__info">
<div className="testimonial__subtext">
<span>{ subtitle }</span>
</div>
<div className="testimonial__title">
<h2>{ title }</h2>
</div>
</div>
<div className="items testimonial__testimonials">
{testimonial.map((field, i) => (
<div className="testimonial__testimonial">
<div className="testimonial__card" key={ i }>
<div className="testimonial__image">
<img src={field.media.url} />
</div>
<div className="testimonial__review">
<p>{field.testimonial}</p>
</div>
<div className="testimonial__stars">
<span>{selectcontrolDisplay}</span>
</div>
<div className="testimonial__author">
<div className="testimonial__name">
<span>{ field.author }</span>
</div>
<div className="testimonial__function">
<span>{ field.position }</span>
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</section>
);
},
} );