Search code examples
javascriptwordpresswordpress-gutenberggutenberg-blocks

Toggle control class on or off on custom Gutenberg Block


I have a custom Gutenberg block called the Callout Block, which consists of a header, text, and button. The goal is to add a toggle option in the block inspector sidebar, allowing the user to choose between a horizontal or vertical layout. This toggle will control how the items are stacked within the block, both in the editor and on the frontend.

Some callout blocks are stacked vertically, as shown in example 1, while others are stacked horizontally, as shown in example 2.

Example 1:

  • Main Heading
  • Text
  • Button

Example 2:

  • Main Heading, Text, Button

Therefore, the ability to toggle a class and provide flexibility to the callout block is crucial. I have successfully implemented the class toggle in the editor, but I'm facing difficulties in applying it on the frontend. My setup includes the following files: edit.js, index.js, and save.js. Please find the code below:

edit.js

import { __ } from '@wordpress/i18n';
import {
  RichText,
  InspectorControls,
  MediaUpload,
  useBlockProps,
  InnerBlocks,
} from '@wordpress/block-editor';
import { PanelBody, Button, ToggleControl } from '@wordpress/components';

const ALLOWED_BLOCKS = ['core/button'];

import './editor.scss';

const Edit = ({ attributes, setAttributes }) => {
  const { toggleOn, customClass } = attributes;

  const onToggleChange = (newValue) => {
    setAttributes({ toggleOn: newValue });
  };

  const blockProps = useBlockProps();

  const { blockBackground } = attributes;

  function onSelectBlockBackground(newBlockBackground) {
    setAttributes({
      blockBackground: newBlockBackground.sizes.full.url,
    });
  }

  return (
    <div {...blockProps}>
      <InspectorControls>
        <PanelBody title={__('Toggle on horizontal or vertical layout')}>
          <ToggleControl
            label={__('Toggle On')}
            checked={toggleOn}
            onChange={onToggleChange}
          />
        </PanelBody>
        <PanelBody title={__('Select Background Image')}>
          <p>
            <strong>{__('Select a Background Image:')}</strong>
          </p>
          <MediaUpload
            onSelect={onSelectBlockBackground}
            type="image"
            value={attributes.blockBackground.url}
            render={({ open }) => (
              <Button
                className="editor-media-placeholder__button is-button is-default is-large"
                icon="upload"
                onClick={open}
              >
                {__('Select Image')}
              </Button>
            )}
          />
        </PanelBody>
      </InspectorControls>

      <div
        className={`wp-block-create-block-callout-block ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`}
      >
        <div
          className="callout-cta"
          style={{
            backgroundImage: blockBackground ? `url("${blockBackground}")` : 'none',
            backgroundSize: 'cover',
            backgroundPosition: 'center',
            backgroundRepeat: 'no-repeat',
          }}
        >
          <RichText
            tagName="h2"
            value={attributes.mainHeading}
            onChange={(mainHeading) => setAttributes({ mainHeading })}
            placeholder={__('Main Heading')}
            className="main-heading"
          />
          <RichText
            tagName="p"
            value={attributes.mainContent}
            onChange={(mainContent) => setAttributes({ mainContent })}
            placeholder={__('Main Content')}
            className="main-content"
          />
          <InnerBlocks allowedBlocks={ALLOWED_BLOCKS} />
        </div>
      </div>
    </div>
  );
};

export default Edit;

index.js

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { RichText, InspectorControls, MediaUpload, useBlockProps } from '@wordpress/block-editor';
import { PanelBody, Button } from '@wordpress/components';
import './style.scss';
import Edit from './edit';
import save from './save';

const validAlignments = ['full'];

registerBlockType('create-block/callout-block', {
    title: __('Callout Block', 'callout-block'),
    supports: {
        html: false,
        align: true,
    },
    attributes: {
        mainHeading: {
            type: 'string',
            source: 'html',
            selector: '.main-heading',
        },
        mainContent: {
            type: 'string',
            source: 'html',
            selector: '.main-content',
        },
        blockBackground: {
            type: 'string',
            default: '',
        },
        align: {
            type: 'string',
            default: 'full',
        },
        toggleOn: {
          type: 'boolean',
          default: false,
          source: 'attribute',
          attribute: 'data-toggle-on', // Use the attribute name that corresponds to the class you're toggling
        },
    },
    getEditWrapperProps( attributes ) {
      const { align } = attributes;
      if (-1 !== validAlignments.indexOf( align ) ) {
        return { 'data-align': align };
      }
    },
    edit: Edit,
    save,
});

save.js

import { __ } from '@wordpress/i18n';
import {
  RichText,
  InspectorControls,
  MediaUpload,
  useBlockProps,
  InnerBlocks,
} from '@wordpress/block-editor';
import { PanelBody, Button } from '@wordpress/components';

export default function save({ className, attributes }) {
  const blockProps = useBlockProps.save({
    'data-align': attributes.align, // Add the data-align attribute with the align value
  });

  const { toggleOn } = attributes;

  return (
    <div className={`wp-block-create-block-callout-block ${className} ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`} {...blockProps}>
      <div
        className="callout-cta"
        style={{
          backgroundImage: attributes.blockBackground !== '' ? `url("${attributes.blockBackground}")` : 'none',
          backgroundSize: 'cover',
          backgroundPosition: 'center',
          backgroundRepeat: 'no-repeat',
        }}
      >
        <RichText.Content
          tagName="h2"
          value={attributes.mainHeading}
          className="main-heading"
        />
        <RichText.Content
          tagName="p"
          value={attributes.mainContent}
          className="main-content"
        />
        <InnerBlocks.Content {...blockProps} />
      </div>
    </div>
  );
}

Solution

  • You could consider applying your custom classes via the useBlockProps.save() call:

    const { toggleOn } = attributes;
    
    const blockProps = useBlockProps.save({
      'data-align': attributes.align, // Add the data-align attribute with the align value,
      className: `wp-block-create-block-callout-block ${className} ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`,
    });
    
    return (
      <div {...blockProps}>