Gutenberg: Error when using blocks.getSaveElement to wrap blocks in wrapper div with block style variations

Created on 12 Sep 2018  Â·  16Comments  Â·  Source: WordPress/gutenberg

Describe the bug

I try to wrap core blocks into wrapper elements. For that, I use the blocks.getSaveElement filter like that:

wp.hooks.addFilter(
    'blocks.getSaveElement',
    'namespace/modify-get-save-element',
    modifyGetSaveElement
);

/**
 * Wrap block in div.
 * 
 * @param {object} element 
 * @param {object} blockType 
 * @param {object} attributes 
 * 
 * @return The element.
 */
function modifyGetSaveElement( element, blockType, attributes  ) {
    if (!element) {
        return;
    }

    if (blockType.name === 'core/quote') {
        return (
            <div className='quote-wrapper'>
                {element}
            </div>
        )
    }

    return element;
}

This works well when no block style variation is selected. When using a variation, I get block validation errors:

Block validation: Expected attribute `class` of value `wp-block-quote`, saw `wp-block-quote is-style-large`.
Block validation: Expected attribute `class` of value `wp-block-quote`, saw `wp-block-quote is-style-large`.
Block validation: Expected attribute `class` of value `wp-block-quote blocks-quote-style-1`, saw `wp-block-quote is-style-large`.

I don’t know if I am just using the filter wrong maybe? I also tried to use cloneElement(), because it is mentioned in the Handbook (https://wordpress.org/gutenberg/handbook/extensibility/extending-blocks/#blocks-getsaveelement), but that did not make a difference.

To Reproduce
Steps to reproduce the behavior:

  1. Put the code from above into a plugin, compile it via Babel and activate it.
  2. Insert a quote block.
  3. Select the large quote style.
  4. Save the post and reload the editor.
  5. You should get block validation errors.

Expected behavior

No block validation error :)

Desktop (please complete the following information):

  • OS: Windows 10
  • Browser: Firefox
  • Version 64.0a1 (2018-09-10) (64-Bit)

Additional context

  • Gutenberg 3.7.
[Feature] Style Variations [Type] Bug

Most helpful comment

Tried it out and got the same error. Reopening the issue.

To reproduce:

Add the following code into a plugin:

wp.hooks.addFilter(
    'blocks.getSaveElement',
    'namespace/modify-get-save-element',
    modifyGetSaveElement
);

function modifyGetSaveElement( element, blockType, attributes  ) {
    if (!element) {
        return;
    }

    if (blockType.name === 'core/quote') {
        return (
            <div className='quote-wrapper'>
                {element}
            </div>
        )
    }

    return element;
}

Enable the plugin, insert a quote block and select the large style. Save and reload the editor.

Issue is present in both – Gutenberg that comes with WP 5.1 and the Gutenberg plugin 5.2.

All 16 comments

I found a past issue that sounds similar to me. As a first check, do the replies in #6380 help you?

For some reason the 'is-style-large' etc classes have to be on the outer (root) element for the block. So in your case the div.quote-wrapper you've added.

I haven't had time to trace this but getActiveStyle takes a class string and array of styles and returns the active style from that:
https://github.com/WordPress/gutenberg/blob/master/packages/editor/src/components/block-styles/index.js#L30

I'd guess the class of the root block element is being used as the source of truth for the active style rather than (say) an attribute.

wp.hooks.addFilter('blocks.getSaveElement', 'pw-examples/wrap-core-blocks', function (element, blockType, attributes) {
  if ( blockType.name.substr(0, 5) === 'core/' && wp.element.isValidElement(element) ) {
    return wp.element.createElement('div', {
      'class': 'wp-block-wrapper ' + (
          typeof element.props.className === 'string' && element.props.className.match(/(^|\s+)wp-block-/) ? 
          element.props.className.replace(/wp-block-/, 'wp-block-wrapper-') : 
          'wp-block-wrapper-' + blockType.name.replace(/\//, '-').replace(/^core-/, '')
        ),
      'data-type': blockType.name
    }, element);
  }
  return element;
});

The above is my hacky work around; it will transform the wp-block-* class to wp-block-wrapper-* and leave the rest of the classes intact.

e.g. wp-block-quote is-style-large becomes wp-block-wrapper wp-block-wrapper-quote is-style-large

This seems to work although I've only tested it on a few blocks.

@f4w-pwharton hi, I tried your script but i have this error.
Can you help me?

Image error

this happen when i reload the page because gutenberg expects a 'p' element but i modified the elment with wrapped 'div'.

There's a way to ignore this wrapper on gutenberg editor rendering side?

Most of the core blocks are static (not dynamic) so their rendering is done in the block editor when saving the post.

Options:

  1. Turn them all in to dynamic blocks: You'd probably have rewrite all the renderers in PHP so this wouldn't really be feasible.
  2. Write a block deprecation for each of the core blocks to convert them automatically. There is probably a way to automate this and inject it via the registerBlockType filter
  3. Recreate all your content (delete/insert the block again) - or only use this wrapper on new sites.

Whatever you choose, you'll run in to this problem again when WP updates one of the core blocks as their included deprecation won't work; so you'll have to work out a way around this (maybe via point 2. above).

@florianbrinkmann since this issue was originally posted some time ago, may I check in with you to ask if you're still seeing block validation errors when using block style variations for your custom block?

Hey @designsimply, tested it with WP 5.0.3 and seems to work now!

Hey @florianbrinkmann, I tried to use your snippet and it seems to work fine, but when I reload the editor I get the message "This block contains unexpected or invalid content". Any idea how to fix?

Hi @thiagolcks, are you running the filter inside wp.domReady()? If so, move the code outside of that and it should work (I had this issue with a current project and needed some time to find the reason).

@florianbrinkmann, nops, I'm using it outside domReady. Just using your snippet as it is.

Hm, what error is triggered exectly?

This error:

Block validation: Block validation failed for `core/quote` ({name: "core/quote", title: "Quote", description: "Give quoted text visual emphasis. "In quoting others, we cite ourselves." — Julio Cortázar", icon: {…}, category: "common", …}).

Expected:

<blockquote class="wp-block-quote blocks-quote-style-1 quote-wrapper"><p>Something</p><p></p></blockquote>

Actual:

<div class="quote-wrapper"><blockquote class="wp-block-quote"><p>Something</p><p></p></blockquote></div>

Thanks you, @florianbrinkmann

Tried it out and got the same error. Reopening the issue.

To reproduce:

Add the following code into a plugin:

wp.hooks.addFilter(
    'blocks.getSaveElement',
    'namespace/modify-get-save-element',
    modifyGetSaveElement
);

function modifyGetSaveElement( element, blockType, attributes  ) {
    if (!element) {
        return;
    }

    if (blockType.name === 'core/quote') {
        return (
            <div className='quote-wrapper'>
                {element}
            </div>
        )
    }

    return element;
}

Enable the plugin, insert a quote block and select the large style. Save and reload the editor.

Issue is present in both – Gutenberg that comes with WP 5.1 and the Gutenberg plugin 5.2.

I'm experiencing a similar issue with the following custom code:

/**
 * Wrap block in div.
 *
 * @param {object} element
 * @param {object} blockType
 * @param {object} attributes
 *
 * @return The element.
 */
function modifyGetSaveElement( element, blockType, attributes  ) {
    if (!element) {
        return;
    }

    if (blockType.name === 'core/quote' || blockType.name === 'core/paragraph' || blockType.name ===  'core/heading') {
        return (
            <div class='feature-content'>
                <div class='feature-body'>
                    <div class='feature-cell'>
                        {element}
                    </div>
                </div>
            </div>
        )
    }

    if (blockType.name === 'core/media-text' || blockType.name === 'core/columns') {
        return (
            <div class='feature-content'>
                <div class='feature-body'>
                    {element}
                </div>
            </div>
        )
    }

    return element;
}

When I add testclass to a column block with a paragraph block:

image

Then the block serializes as expected:

image

However, if I "Save Draft" and reload the page, the block is crashed:

image

@danielbachhuber I ran into the same issue (verison 5.4), although as @strive-phil suggested, adding the class to the new element container as shown code below seems to allow everything to work as it should.

import classnames from 'classnames';

const pbUpdateGroupWrapper = function ( element, blockType, blockAttributes ) {
    if (!element) {
        return;
    }

    if (blockType.name === 'core/group') {
        const sectionClassNames = classnames( 'o-section', blockAttributes.className );
        const containerClassNames = classnames( 'o-container' );

        return (
            <section className={ sectionClassNames }>
                <div className={ containerClassNames }>
                    { element }
                </div>
            </section>
        )
    }
    return element;
};

@florianbrinkmann It's weird how the original code you posted works for you without a style variation.

When I use the exact code you have and use the default style I'm still getting a block validation error. I mean it makes sense since in that code there's no deprecation added... but why does it work for you?

edit: Does adding this code break your existing quote comments? I'm guessing that's my problem. because it technically works fine for new sites, but it will break existing core blocks of that type, which would mean extending core blocks is pretty useless without deprecating properly.

Hi @fastpenguin91,

I have not tested it, but yes, after thinking about it, I guess you are right, and it will only work for blocks that are added after the code change, with old blocks breaking because of block validation error. Maybe the best way to add a wrapper would be to use the render_block PHP filter.

Was this page helpful?
0 / 5 - 0 ratings