Search code examples
javascriptreactjswordpresswordpress-gutenberggutenberg-blocks

Toggle control breaks after gutenberg block is updated/published


In my block, I have set up a toggle control to add or remove a class and change the layout of the block in both the editor and the front end of the site.

Instances where it works:

  • Adding a new page
  • Adding a new block
  • Toggling on to change the layout of the block
  • The block's appearance changes in both the editor and the front end after it's saved

Instances where it doesn't work:

  • When I edit the same block, the toggle appears off, but the changes remain saved.
  • If I try to re-toggle on and off, nothing happens.

While it works perfectly fine initially, it seems that once the block is saved, there is no way to re-toggle or change the layout again. I'm working with three main files: edit.js, index.js, and save.js. Please refer to the code provided 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, align } = attributes;

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

  const onSelectBlockBackground = (newBlockBackground) => {
    setAttributes({
      blockBackground: newBlockBackground.sizes.full.url,
    });
  };

  const blockProps = useBlockProps({
    className: `wp-block-create-block-callout-block ${customClass} ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`,
    'data-align': align,
  });

  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={`callout-cta`}
        style={{
          backgroundImage: attributes.blockBackground !== '' ? `url("${attributes.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>
  );
};

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 { 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 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

  • Looking at your other question in relation to this question/code, I suggest using Block Styles would be a better fit for your scenario.

    The need for toggle and related getEditWrapperProps() is removed entirely with Block Styles, with the advantage that the User can visually preview the style to be applied. Block Styles also enables the option to register additional styles in the future without needing to change the underlying block properties. It may also be useful for your custom background properties.

    Eg. Using Custom Block Styles Block Styles UI

    1. Remove the toggle and related code/attributes.

    2. Register a block style for "horizontal" for your block (as the default layout is "stacked/vertical").

    index.js

    wp.blocks.registerBlockStyle('create-block/callout-block', [
        {
            name: 'horizontal',
            label: 'Horizontal',
        }
    ]);
    
    1. Add the required CSS to style the layout (applies to both the Editor & Front End)

    style.scss

    .wp-block-create-block-callout-block {
        padding:0.25em;
        border: 2px solid orange;
        
        display:flex !important;
        flex-direction: column; /* default is stacked */
        
        &.is-style-horizontal{
            flex-direction: row;
            border-color: purple;
        }
    }
    
    1. Enable the Block Style preview by defining an "example" in registerBlockType():

    index.js

    registerBlockType('create-block/callout-block', {
    ...
        example: {
            attributes: {
                mainHeading: "Hello World",
                mainContent: "Lorem Ipsum"
            }
        }
    ...
    });
    
    1. Compile and test - make sure clear your cache and to re-add the block fresh.