I’m trying to add a custom color control to the group and cover blocks, and if a color is selected, construct a custom className for the block. This custom class is used for a section separator.

What I’ve done:

  • I have filtered blocks.registerBlockType and added the custom
    attribute.
  • I’ve made the function to filter editor.BlockEdit
    properly and add a PanelColorSettings control to the group and
    cover blocks only.
  • I have filtered blocks.getSaveContent.extraProps
    to add the custom class on save.
  • I have filtered editor.BlockListBlock to add this same custom class into the block wrapped while editing, so I can see the changes on the fly.

I’ve been able to get the control to appear, and the className to save to the block when saving the post properly. On the front end, it works. And the first time you edit, it works.

What’s missing:

The problem I’m now having is that these custom classNames don’t get replaced when you select a different color in the color panel and publish changes. If you select 1 color, hit publish, refresh, and select the 2nd color, publish, refresh, the block will have the custom classes for both colors.

When you first select a color on a block with no previous color selected, you can see the changes on the fly in the editor. When you hit publish and refresh, and then cycle through the colors, you won’t see changes on the fly, because the className for your first color is still present on the block.

What I think the problem is:

I need these filters to remove the custom classes they’ve previously applied? Or an abstraction between “classes typed in the advanced box by a user” vs “classes added to the block by my filter”. Nothing in the docs seems to satisfy that.

My code (updated):

/**
 * External Dependencies
 */
import classnames from 'classnames';

/**
 * WordPress Dependencies
 */
const { __ } = wp.i18n;
const { addFilter } = wp.hooks;
const { Fragment } = wp.element;
const { createHigherOrderComponent } = wp.compose;
const { InspectorControls, getColorObjectByColorValue, PanelColorSettings } = wp.blockEditor;
const { select } = wp.data;


// Restrict to specific block names
const allowedBlocks = ['core/group', 'core/cover'];

/**
 * Add custom attribute for section separator.
 *
 * @param {Object} settings Settings for the block.
 *
 * @return {Object} settings Modified settings.
 */
function addAttributes(settings) {

    //check if object exists for old Gutenberg version compatibility
    //add allowedBlocks restriction
    if (typeof settings.attributes !== 'undefined' && allowedBlocks.includes(settings.name)) {

        settings.attributes = Object.assign(settings.attributes, {
            dividerColor: {
                type: 'string',
                default: null,
            }
        });

    }

    return settings;
}

/**
 * Add section divider controls on Advanced Block Panel.
 *
 * @param {function} BlockEdit Block edit component.
 *
 * @return {function} BlockEdit Modified block edit component.
 */
const withAdvancedControls = createHigherOrderComponent((BlockEdit) => {
    return (props) => {

        if (!allowedBlocks.includes(props.name)) {
            return(<BlockEdit {...props} />);
        }

        const {
            name,
            attributes,
            setAttributes,
            isSelected,
        } = props;

        const {
            dividerColor
        } = attributes;

        const changeColor = (value) => {
            setAttributes({ dividerColor: value });
            console.log(props);
        }

        return (
            <Fragment>
                <BlockEdit {...props} />
                {isSelected && allowedBlocks.includes(name) &&
                    <InspectorControls>
                        <PanelColorSettings 
                            title={__('Section Divider')}
                            colorSettings={[
                                {
                                    value: dividerColor,
                                    onChange: changeColor,
                                    label: __('Choose a color to enable the divider.')
                                }
                            ]}
                        />
                    </InspectorControls>
                }
            </Fragment>
        );
    };
}, 'withAdvancedControls');

/**
 * Add custom element class in save element.
 *
 * @param {Object} extraProps     Block element.
 * @param {Object} blockType      Blocks object.
 * @param {Object} attributes     Blocks attributes.
 *
 * @return {Object} extraProps Modified block element.
 */
function applyExtraClass(extraProps, blockType, attributes) {

    const { dividerColor } = attributes;

    //check if attribute exists for old Gutenberg version compatibility
    //add class only when dividerColor is not empty
    //add allowedBlocks restriction
    if (typeof dividerColor !== 'undefined'
        && dividerColor
        && allowedBlocks.includes(blockType.name)) {
        const settings = select('core/editor').getEditorSettings();
        const colorObject = getColorObjectByColorValue(settings.colors, dividerColor);
        if (colorObject) {
            extraProps.className = classnames(extraProps.className, 'divider divider-' + colorObject.slug);
        }
    }

    return extraProps;
}

/**
 * Add size class to the block in the editor
 *
 * @param {Object} BlockListBlock  BlockListBlock object.
 *
 * @return {Object}                Modified BlockListBlock object.
 */
 const addDividerClass = createHigherOrderComponent((BlockListBlock) => {
    return (props) => {
      const {
        attributes,
        className,
        name,
      } = props;

      const { dividerColor } = attributes;
  
      if (!allowedBlocks.includes(name)) {
        return <BlockListBlock {...props} />;
      }

      const settings = select('core/editor').getEditorSettings();
      const colorObject = getColorObjectByColorValue(settings.colors, dividerColor);

      if (!colorObject) {
        return <BlockListBlock {...props} />;
      }
  
      return (
        <BlockListBlock
          {...props}
          className={classnames(className, 'divider divider-' + colorObject.slug)}
        />
      );
    };
  }, 'addDividerClass');

//add filters

addFilter(
    'blocks.registerBlockType',
    'ursa6/custom-attributes',
    addAttributes
);

addFilter(
    'editor.BlockEdit',
    'ursa6/custom-advanced-control',
    withAdvancedControls
);

addFilter(
    'blocks.getSaveContent.extraProps',
    'ursa6/applyExtraClass',
    applyExtraClass
);

addFilter(
    'editor.BlockListBlock',
    'ursa6/addDividerClass',
    addDividerClass
 );

In my theme’s functions, I have a custom color palette set for the above getColorObjectByColorValue to find a match.

add_theme_support( 'editor-color-palette', array(
    array(
        'name' => __( 'Dark 1', 'textdomain' ),
        'slug' => 'dk1',
        'color' => '#282828',
    ),
    array(
        'name' => __( 'Light 1', 'textdomain' ),
        'slug' => 'lt1',
        'color' => '#FCFCF8',
    ),
    array(
        'name' => __( 'Mid', 'textdomain' ),
        'slug' => 'mid',
        'color' => '#F7F7F7',
    ),
    array(
        'name' => __( 'Accent 1', 'textdomain' ),
        'slug' => 'accent1',
        'color' => '#E6A225',
    ),
    array(
        'name' => __( 'Accent 2', 'textdomain' ),
        'slug' => 'accent2',
        'color' => '#048081',
    ),
    array(
        'name' => __( 'Accent 3', 'textdomain' ),
        'slug' => 'accent3',
        'color' => '#882437',
    ),
  )
);

Can someone tell me where I went wrong?

1 Answer
1

Your code looks ok and would work in any other component other than colors, because colors need the higher-order-component withColors. In the past I struggled also dealing with the behaviour, that your are describing and found these two solutions:

  1. If you want to insert your own color component, check the second example from here https://awhitepixel.com/blog/add-custom-settings-to-existing-wordpress-gutenberg-blocks/.

  2. Otherwise you can just add color support in the blocks.registerBlockType. This is also what i’m using right now and is working great:
    if( ['core/quote'].includes(name) ){ const supports = { ...props.supports, color: true, }; props = { ...props, supports }; }

Here is also a link https://css-tricks.com/a-crash-course-in-wordpress-block-filters/

Tags:

Leave a Reply

Your email address will not be published. Required fields are marked *