Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Editor: Support className in save for custom className difference #11828

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions packages/block-editor/src/hooks/custom-class-name.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/**
* External dependencies
*/
import { assign, difference, omit } from 'lodash';
import { assign, difference } from 'lodash';
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { Fragment } from '@wordpress/element';
import { addFilter } from '@wordpress/hooks';
import { addFilter, removeFilter } from '@wordpress/hooks';
import { TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import {
Expand Down Expand Up @@ -132,19 +132,21 @@ export function getHTMLRootElementClasses( innerHTML ) {
*/
export function addParsedDifference( blockAttributes, blockType, innerHTML ) {
if ( hasBlockSupport( blockType, 'customClassName', true ) ) {
// To determine difference, serialize block given the known set of
// attributes, with the exception of `className`. This will determine
// the default set of classes. From there, any difference in innerHTML
// can be considered as custom classes.
const attributesSansClassName = omit( blockAttributes, [ 'className' ] );
const serialized = getSaveContent( blockType, attributesSansClassName );
// To determine difference, serialize the block given the known set of
// attributes to determine the default set of classes. From there, any
// difference in innerHTML can be considered as custom classes.
removeFilter( 'blocks.getSaveContent.extraProps', 'core/custom-class-name/save-props' );
const serialized = getSaveContent( blockType, blockAttributes );
addFilter( 'blocks.getSaveContent.extraProps', 'core/custom-class-name/save-props', addSaveProps );
const defaultClasses = getHTMLRootElementClasses( serialized );
const actualClasses = getHTMLRootElementClasses( innerHTML );
const customClasses = difference( actualClasses, defaultClasses );

if ( customClasses.length ) {
blockAttributes.className = customClasses.join( ' ' );
} else if ( serialized ) {
blockAttributes.className = classnames( {
[ blockAttributes.className ]: ! serialized,
}, ...customClasses );

if ( ! blockAttributes.className ) {
delete blockAttributes.className;
}
}
Expand Down
18 changes: 17 additions & 1 deletion packages/block-editor/src/hooks/test/custom-class-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import { getHTMLRootElementClasses } from '../custom-class-name';

describe( 'custom className', () => {
const blockSettings = {
save: () => <div className="default" />,
save: ( { attributes } ) => {
if ( attributes.className === 'is-varied-on-classname' ) {
return <div className="varied" />;
}

return <div className="default" />;
},
category: 'common',
title: 'block title',
};
Expand Down Expand Up @@ -157,6 +163,16 @@ describe( 'custom className', () => {
expect( attributes.className ).toBe( 'custom1 custom3' );
} );

it( 'should allow save to consider the className attribute', () => {
const attributes = addParsedDifference(
{ className: 'is-varied-on-classname' },
blockSettings,
'<div class="varied is-varied-on-classname foo"></div>'
);

expect( attributes.className ).toBe( 'is-varied-on-classname foo' );
} );

it( 'should not remove the custom classes for dynamic blocks', () => {
const attributes = addParsedDifference(
{ className: 'custom1' },
Expand Down
18 changes: 9 additions & 9 deletions packages/blocks/src/api/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -620,30 +620,30 @@ export function isEquivalentHTML( actual, expected ) {
*
* Logs to console in development environments when invalid.
*
* @param {string|Object} blockTypeOrName Block type.
* @param {Object} attributes Parsed block attributes.
* @param {string} innerHTML Original block content.
* @param {string|Object} blockTypeOrName Block type.
* @param {Object} attributes Parsed block attributes.
* @param {string} expectedBlockContent Original block content.
*
* @return {boolean} Whether block is valid.
*/
export function isValidBlockContent( blockTypeOrName, attributes, innerHTML ) {
export function isValidBlockContent( blockTypeOrName, attributes, expectedBlockContent ) {
const blockType = normalizeBlockType( blockTypeOrName );
let saveContent;
let actualBlockContent;
try {
saveContent = getSaveContent( blockType, attributes );
actualBlockContent = getSaveContent( blockType, attributes );
} catch ( error ) {
log.error( 'Block validation failed because an error occurred while generating block content:\n\n%s', error.toString() );
return false;
}

const isValid = isEquivalentHTML( innerHTML, saveContent );
const isValid = isEquivalentHTML( expectedBlockContent, actualBlockContent );
if ( ! isValid ) {
log.error(
'Block validation failed for `%s` (%o).\n\nExpected:\n\n%s\n\nActual:\n\n%s',
blockType.name,
blockType,
saveContent,
innerHTML
expectedBlockContent,
actualBlockContent
);
}

Expand Down