5

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
  • 1
    I was able to get it even closer by adding at 'editor.BlockListBlock' filter as well. The block wrapper now gets the proper class in the editor, the class saves to the block on the front end. It seems the last issue is the duplication of the custom class. When the block is saved, the custom class is added to the class field in the advanced tab, and when the page is reloaded and the block color changed, it appends the new color class to the contents of the class field in the advanced tab. Commented Jan 29, 2022 at 23:17

1 Answer 1

0

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/

1
  • Hey @Margarita, appreciate you stopping by. I've seen those both, and tried both, but ultimately have the same problem. The issue with the WhitePixel example is that it doesn't allow for user-supplied custom classes on the block. I'm pretty sure that's my problem. It almost seems like I need to strip my custom class in the save function when no color is selected, or when the color is changed? Doesn't feel efficient though. Commented Feb 1, 2022 at 21:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.