Skip to content

Commit

Permalink
State: Update reusable blocks as pointers to state blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
aduth committed Feb 28, 2018
1 parent 47c609e commit 8a970d1
Show file tree
Hide file tree
Showing 11 changed files with 313 additions and 652 deletions.
22 changes: 0 additions & 22 deletions blocks/api/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@ import {
find,
first,
flatMap,
uniqueId,
} from 'lodash';

/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { applyFilters } from '@wordpress/hooks';

/**
Expand Down Expand Up @@ -257,23 +255,3 @@ export function switchToBlockType( blocks, name ) {
return applyFilters( 'blocks.switchToBlockType.transformedBlock', transformedBlock, blocks );
} );
}

/**
* Creates a new reusable block.
*
* @param {string} type The type of the block referenced by the reusable
* block.
* @param {Object} attributes The attributes of the block referenced by the
* reusable block.
*
* @return {Object} A reusable block object.
*/
export function createReusableBlock( type, attributes ) {
return {
id: -uniqueId(), // Temorary id replaced when the block is saved server side
isTemporary: true,
title: __( 'Untitled block' ),
type,
attributes,
};
}
15 changes: 0 additions & 15 deletions blocks/api/test/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
cloneBlock,
getPossibleBlockTransformations,
switchToBlockType,
createReusableBlock,
} from '../factory';
import { getBlockTypes, unregisterBlockType, setUnknownTypeHandlerName, registerBlockType } from '../registration';

Expand Down Expand Up @@ -700,18 +699,4 @@ describe( 'block factory', () => {
} );
} );
} );

describe( 'createReusableBlock', () => {
it( 'should create a reusable block', () => {
const type = 'core/test-block';
const attributes = { name: 'Big Bird' };

expect( createReusableBlock( type, attributes ) ).toMatchObject( {
id: expect.any( Number ),
title: 'Untitled block',
type,
attributes,
} );
} );
} );
} );
118 changes: 62 additions & 56 deletions blocks/library/block/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/**
* External dependencies
*/
import { pickBy, noop } from 'lodash';
import { connect } from 'react-redux';
import { noop, partial } from 'lodash';

/**
* WordPress dependencies
*/
import { Component, Fragment } from '@wordpress/element';
import { Component, Fragment, compose } from '@wordpress/element';
import { Placeholder, Spinner, Disabled } from '@wordpress/components';
import { withSelect, withDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

/**
Expand All @@ -25,12 +25,11 @@ class ReusableBlockEdit extends Component {
this.stopEditing = this.stopEditing.bind( this );
this.setAttributes = this.setAttributes.bind( this );
this.setTitle = this.setTitle.bind( this );
this.updateReusableBlock = this.updateReusableBlock.bind( this );
this.save = this.save.bind( this );

this.state = {
isEditing: false,
title: null,
attributes: null,
};
}

Expand All @@ -41,62 +40,62 @@ class ReusableBlockEdit extends Component {
}

startEditing() {
this.setState( { isEditing: true } );
const { reusableBlock } = this.props;

this.setState( {
isEditing: true,
title: reusableBlock.title,
} );
}

stopEditing() {
this.setState( {
isEditing: false,
title: null,
attributes: null,
} );
}

setAttributes( attributes ) {
this.setState( ( prevState ) => ( {
attributes: { ...prevState.attributes, ...attributes },
} ) );
const { updateAttributes, block } = this.props;
updateAttributes( block.uid, attributes );
}

setTitle( title ) {
this.setState( { title } );
}

updateReusableBlock() {
const { title, attributes } = this.state;
save() {
const { reusableBlock, onUpdateTitle, onSave } = this.props;

// Use pickBy to include only changed (assigned) values in payload
const payload = pickBy( {
title,
attributes,
} );
const { title } = this.state;
if ( title !== reusableBlock.title ) {
onUpdateTitle( title );
}

onSave();

this.props.updateReusableBlock( payload );
this.props.saveReusableBlock();
this.stopEditing();
}

render() {
const { isSelected, reusableBlock, isFetching, isSaving } = this.props;
const { isEditing, title, attributes } = this.state;
const { isSelected, reusableBlock, block, isFetching, isSaving } = this.props;
const { isEditing, title } = this.state;

if ( ! reusableBlock && isFetching ) {
return <Placeholder><Spinner /></Placeholder>;
}

if ( ! reusableBlock ) {
if ( ! reusableBlock || ! block ) {
return <Placeholder>{ __( 'Block has been deleted or is unavailable.' ) }</Placeholder>;
}

const reusableBlockAttributes = { ...reusableBlock.attributes, ...attributes };

let element = (
<BlockEdit
{ ...this.props }
id={ reusableBlock.uid }
name={ reusableBlock.type }
isSelected={ isEditing && isSelected }
attributes={ reusableBlockAttributes }
id={ block.uid }
name={ block.name }
attributes={ block.attributes }
setAttributes={ isEditing ? this.setAttributes : noop }
/>
);
Expand All @@ -116,7 +115,7 @@ class ReusableBlockEdit extends Component {
isSaving={ isSaving && ! reusableBlock.isTemporary }
onEdit={ this.startEditing }
onChangeTitle={ this.setTitle }
onSave={ this.updateReusableBlock }
onSave={ this.save }
onCancel={ this.stopEditing }
/>
) }
Expand All @@ -125,34 +124,41 @@ class ReusableBlockEdit extends Component {
}
}

const ConnectedReusableBlockEdit = connect(
( state, ownProps ) => ( {
reusableBlock: state.reusableBlocks.data[ ownProps.attributes.ref ],
isFetching: state.reusableBlocks.isFetching[ ownProps.attributes.ref ],
isSaving: state.reusableBlocks.isSaving[ ownProps.attributes.ref ],
const EnhancedReusableBlockEdit = compose( [
withSelect( ( select, ownProps ) => {
const {
getReusableBlock,
isFetchingReusableBlock,
isSavingReusableBlock,
getBlock,
} = select( 'core/editor' );
const { ref } = ownProps.attributes;
const reusableBlock = getReusableBlock( ref );

return {
reusableBlock,
isFetching: isFetchingReusableBlock( ref ),
isSaving: isSavingReusableBlock( ref ),
block: reusableBlock ? getBlock( reusableBlock.uid ) : null,
};
} ),
( dispatch, ownProps ) => ( {
fetchReusableBlock() {
dispatch( {
type: 'FETCH_REUSABLE_BLOCKS',
id: ownProps.attributes.ref,
} );
},
updateReusableBlock( reusableBlock ) {
dispatch( {
type: 'UPDATE_REUSABLE_BLOCK',
id: ownProps.attributes.ref,
reusableBlock,
} );
},
saveReusableBlock() {
dispatch( {
type: 'SAVE_REUSABLE_BLOCK',
id: ownProps.attributes.ref,
} );
},
} )
)( ReusableBlockEdit );
withDispatch( ( dispatch, ownProps ) => {
const {
fetchReusableBlocks,
updateBlockAttributes,
updateReusableBlockTitle,
saveReusableBlock,
} = dispatch( 'core/editor' );
const { ref } = ownProps.attributes;

return {
fetchReusableBlock: partial( fetchReusableBlocks, ref ),
updateAttributes: updateBlockAttributes,
onUpdateTitle: partial( updateReusableBlockTitle, ref ),
onSave: partial( saveReusableBlock, ref ),
};
} ),
] )( ReusableBlockEdit );

export const name = 'core/block';

Expand All @@ -172,6 +178,6 @@ export const settings = {
html: false,
},

edit: ConnectedReusableBlockEdit,
edit: EnhancedReusableBlockEdit,
save: () => null,
};
49 changes: 40 additions & 9 deletions editor/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,22 @@ export function resetBlocks( blocks ) {
};
}

/**
* Returns an action object used in signalling that blocks have been received.
* Unlike resetBlocks, these should be appended to the existing known set, not
* replacing.
*
* @param {Object[]} blocks Array of block objects.
*
* @return {Object} Action object.
*/
export function receiveBlocks( blocks ) {
return {
type: 'RECEIVE_BLOCKS',
blocks,
};
}

/**
* Returns an action object used in signalling that the block attributes with
* the specified UID has been updated.
Expand Down Expand Up @@ -443,20 +459,18 @@ export function fetchReusableBlocks( id ) {
}

/**
* Returns an action object used to insert or update a reusable block into
* the store.
* Returns an action object used in signalling that reusable blocks have been
* received. Results is an array of objects containing reusableBlock (details
* about reusable persistence) and parsedBlock (the original block).
*
* @param {Object} id The ID of the reusable block to update.
* @param {Object} reusableBlock The new reusable block object. Any omitted keys
* are not changed.
* @param {Object[]} results Reusable blocks received.
*
* @return {Object} Action object.
*/
export function updateReusableBlock( id, reusableBlock ) {
export function receiveReusableBlocks( results ) {
return {
type: 'UPDATE_REUSABLE_BLOCK',
id,
reusableBlock,
type: 'RECEIVE_REUSABLE_BLOCKS',
results,
};
}

Expand Down Expand Up @@ -489,6 +503,23 @@ export function deleteReusableBlock( id ) {
};
}

/**
* Returns an action object used in signalling that a reusable block's title is
* to be updated.
*
* @param {number} id The ID of the reusable block to update.
* @param {string} title The new title.
*
* @return {Object} Action object.
*/
export function updateReusableBlockTitle( id, title ) {
return {
type: 'UPDATE_REUSABLE_BLOCK_TITLE',
id,
title,
};
}

/**
* Returns an action object used to convert a reusable block into a static
* block.
Expand Down
Loading

0 comments on commit 8a970d1

Please sign in to comment.