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

Blocks: Move logic for generating class name to BlockEdit component #4009

Merged
merged 5 commits into from
Dec 18, 2017
Merged
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
7 changes: 6 additions & 1 deletion blocks/api/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
export { createBlock, getPossibleBlockTransformations, switchToBlockType, createReusableBlock } from './factory';
export { default as parse, getBlockAttributes } from './parser';
export { default as rawHandler } from './raw-handling';
export { default as serialize, getBlockDefaultClassname, getBlockContent } from './serializer';
export {
default as serialize,
getBlockContent,
getBlockDefaultClassname,
getSaveElement,
} from './serializer';
export { isValidBlock } from './validation';
export { getCategories } from './categories';
export {
Expand Down
39 changes: 29 additions & 10 deletions blocks/api/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,25 @@ export function getBlockDefaultClassname( blockName ) {

/**
* Given a block type containg a save render implementation and attributes, returns the
* static markup to be saved.
* enhanced element to be saved or string when raw HTML expected.
*
* @param {Object} blockType Block type
* @param {Object} attributes Block attributes
* @return {string} Save content
* @return {Object|string} Save content
*/
export function getSaveContent( blockType, attributes ) {
export function getSaveElement( blockType, attributes ) {
const { save } = blockType;
let saveContent;

let saveElement;

if ( save.prototype instanceof Component ) {
saveContent = createElement( save, { attributes } );
saveElement = createElement( save, { attributes } );
} else {
saveContent = save( { attributes } );
saveElement = save( { attributes } );

// Special-case function render implementation to allow raw HTML return
if ( 'string' === typeof saveContent ) {
return saveContent;
if ( 'string' === typeof saveElement ) {
return saveElement;
}
}

Expand All @@ -59,10 +60,28 @@ export function getSaveContent( blockType, attributes ) {

return cloneElement( element, props );
};
const contentWithExtraProps = Children.map( saveContent, addExtraContainerProps );

return Children.map( saveElement, addExtraContainerProps );
}

/**
* Given a block type containg a save render implementation and attributes, returns the
* static markup to be saved.
*
* @param {Object} blockType Block type
* @param {Object} attributes Block attributes
* @return {string} Save content
*/
export function getSaveContent( blockType, attributes ) {
const saveElement = getSaveElement( blockType, attributes );

// Special-case function render implementation to allow raw HTML return
if ( 'string' === typeof saveElement ) {
return saveElement;
}

// Otherwise, infer as element
return renderToString( contentWithExtraProps );
return renderToString( saveElement );
}

/**
Expand Down
21 changes: 18 additions & 3 deletions blocks/block-edit/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
Expand All @@ -6,22 +11,32 @@ import { withFilters } from '@wordpress/components';
/**
* Internal dependencies
*/
import { getBlockType } from '../api';
import {
getBlockType,
getBlockDefaultClassname,
hasBlockSupport,
} from '../api';

export function BlockEdit( props ) {
const { name, ...editProps } = props;
const { name, attributes = {} } = props;
const blockType = getBlockType( name );

if ( ! blockType ) {
return null;
}

// Generate a class name for the block's editable form
const generatedClassName = hasBlockSupport( blockType, 'className', true ) ?
getBlockDefaultClassname( name ) :
null;
const className = classnames( generatedClassName, attributes.className );

// `edit` and `save` are functions or components describing the markup
// with which a block is displayed. If `blockType` is valid, assign
// them preferencially as the render value for the block.
const Edit = blockType.edit || blockType.save;

return <Edit { ...editProps } />;
return <Edit { ...props } className={ className } />;
}

export default withFilters( 'blocks.BlockEdit' )( BlockEdit );
19 changes: 19 additions & 0 deletions blocks/block-edit/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,23 @@ describe( 'BlockEdit', () => {

expect( wrapper.type() ).toBe( save );
} );

it( 'should combine the default class name with a custom one', () => {
const edit = ( { className } ) => <div className={ className } />;
const attributes = {
className: 'my-class',
};
registerBlockType( 'core/test-block', {
edit,
save: noop,
category: 'common',
title: 'block title',
} );

const wrapper = shallow(
<BlockEdit name="core/test-block" attributes={ attributes } />
);

expect( wrapper.prop( 'className' ) ).toBe( 'wp-block-test-block my-class' );
} );
} );
19 changes: 8 additions & 11 deletions blocks/library/block/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*/
import { pickBy, noop } from 'lodash';
import { connect } from 'react-redux';
import classnames from 'classnames';

/**
* WordPress dependencies
Expand All @@ -15,7 +14,8 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { getBlockType, registerBlockType, hasBlockSupport, getBlockDefaultClassname } from '../../api';
import BlockEdit from '../../block-edit';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Is this exposed? How do we use this component when moved to the editor module :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import { registerBlockType } from '../../api';
import ReusableBlockEditPanel from './edit-panel';

class ReusableBlockEdit extends Component {
Expand Down Expand Up @@ -86,23 +86,16 @@ class ReusableBlockEdit extends Component {
}

const reusableBlockAttributes = { ...reusableBlock.attributes, ...attributes };
const blockType = getBlockType( reusableBlock.type );
const BlockEdit = blockType.edit || blockType.save;

// Generate a class name for the block's editable form
const generatedClassName = hasBlockSupport( blockType, 'className', true ) ?
getBlockDefaultClassname( reusableBlock.type ) :
null;
const className = classnames( generatedClassName, reusableBlockAttributes.className );

return [
// We fake the block being read-only by wrapping it with an element that has pointer-events: none
<div key="edit" style={ { pointerEvents: isEditing ? 'auto' : 'none' } }>
<BlockEdit
{ ...this.props }
name={ reusableBlock.type }
focus={ isEditing ? focus : null }
attributes={ reusableBlockAttributes }
setAttributes={ isEditing ? this.setAttributes : noop }
className={ className }
/>
</div>,
focus && (
Expand Down Expand Up @@ -167,6 +160,10 @@ registerBlockType( 'core/block', {
},
},

supports: {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let implementing block decide whether the custom class name is supported.

customClassName: false,
},

edit: ConnectedReusableBlockEdit,
save: () => null,
} );
22 changes: 6 additions & 16 deletions editor/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import { get, partial, reduce, size } from 'lodash';
/**
* WordPress dependencies
*/
import { Component, compose, createElement } from '@wordpress/element';
import { Component, compose } from '@wordpress/element';
import { keycodes } from '@wordpress/utils';
import {
getBlockType,
BlockEdit,
getBlockDefaultClassname,
createBlock,
hasBlockSupport,
getBlockType,
getSaveElement,
isReusableBlock,
} from '@wordpress/blocks';
import { withFilters, withContext } from '@wordpress/components';
Expand Down Expand Up @@ -377,12 +376,6 @@ export class BlockListBlock extends Component {
wrapperProps = blockType.getEditWrapperProps( block.attributes );
}

// Generate a class name for the block's editable form
const generatedClassName = hasBlockSupport( blockType, 'className', true ) ?
getBlockDefaultClassname( block.name ) :
null;
const className = classnames( generatedClassName, block.attributes.className );

// Disable reason: Each block can be selected by clicking on it
/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */
return (
Expand Down Expand Up @@ -424,7 +417,6 @@ export class BlockListBlock extends Component {
onReplace={ isLocked ? undefined : onReplace }
setFocus={ partial( onFocus, block.uid ) }
mergeBlocks={ isLocked ? undefined : this.mergeBlocks }
className={ className }
id={ block.uid }
isSelectionEnabled={ this.props.isSelectionEnabled }
toggleSelection={ this.props.toggleSelection }
Expand All @@ -434,11 +426,9 @@ export class BlockListBlock extends Component {
<BlockHtml uid={ block.uid } />
) }
{ ! isValid && [
createElement( blockType.save, {
key: 'invalid-preview',
attributes: block.attributes,
className,
} ),
<div key="invalid-preview">
{ getSaveElement( blockType, block.attributes ) }
</div>,
<InvalidBlockWarning
key="invalid-warning"
block={ block }
Expand Down