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

Add Reusable Block deletion #4139

Merged
merged 9 commits into from
Jan 4, 2018
2 changes: 1 addition & 1 deletion blocks/api/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export function switchToBlockType( blocks, name ) {
*/
export function createReusableBlock( type, attributes ) {
return {
id: +uniqueId(), // Temorary id replaced when the block is saved server side
id: -uniqueId(), // Temorary id replaced when the block is saved server side
isTemporary: true,
title: __( 'Untitled block' ),
type,
Expand Down
8 changes: 1 addition & 7 deletions blocks/library/block/edit-panel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import './style.scss';
const { ESCAPE } = keycodes;

function ReusableBlockEditPanel( props ) {
const { isEditing, title, isSaving, onEdit, onDetach, onChangeTitle, onSave, onCancel } = props;
const { isEditing, title, isSaving, onEdit, onChangeTitle, onSave, onCancel } = props;

return [
( ! isEditing && ! isSaving ) && (
Expand All @@ -27,12 +27,6 @@ function ReusableBlockEditPanel( props ) {
onClick={ onEdit }>
{ __( 'Edit' ) }
</Button>
<Button
isLarge
className="reusable-block-edit-panel__button"
onClick={ onDetach }>
{ __( 'Detach' ) }
</Button>
</div>
),
( isEditing || isSaving ) && (
Expand Down
18 changes: 8 additions & 10 deletions blocks/library/block/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,17 @@ class ReusableBlockEdit extends Component {
}

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

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

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

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

return [
Expand All @@ -103,9 +107,8 @@ class ReusableBlockEdit extends Component {
key="panel"
isEditing={ isEditing }
title={ title !== null ? title : reusableBlock.title }
isSaving={ isSaving }
isSaving={ isSaving && ! reusableBlock.isTemporary }
onEdit={ this.startEditing }
onDetach={ convertBlockToStatic }
onChangeTitle={ this.setTitle }
onSave={ this.updateReusableBlock }
onCancel={ this.stopEditing }
Expand All @@ -118,6 +121,7 @@ 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 ],
} ),
( dispatch, ownProps ) => ( {
Expand All @@ -140,12 +144,6 @@ const ConnectedReusableBlockEdit = connect(
id: ownProps.attributes.ref,
} );
},
convertBlockToStatic() {
dispatch( {
type: 'CONVERT_BLOCK_TO_STATIC',
uid: ownProps.id,
} );
},
} )
)( ReusableBlockEdit );

Expand Down
4 changes: 2 additions & 2 deletions editor/components/block-list/block-mobile-toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
* Internal dependencies
*/
import BlockMover from '../block-mover';
import BlockDeleteButton from '../block-settings-menu/block-delete-button';
import BlockRemoveButton from '../block-settings-menu/block-remove-button';
import BlockSettingsMenu from '../block-settings-menu';

function BlockMobileToolbar( { uid } ) {
return (
<div className="editor-block-list__block-mobile-toolbar">
<BlockMover uids={ [ uid ] } />
<BlockDeleteButton uids={ [ uid ] } small />
<BlockRemoveButton uids={ [ uid ] } small />
<BlockSettingsMenu uids={ [ uid ] } />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ import { compose } from '@wordpress/element';
*/
import { removeBlocks } from '../../store/actions';

export function BlockDeleteButton( { onDelete, onClick = noop, isLocked, small = false } ) {
export function BlockRemoveButton( { onRemove, onClick = noop, isLocked, small = false } ) {
if ( isLocked ) {
return null;
}

const label = __( 'Delete' );
const label = __( 'Remove' );

return (
<IconButton
className="editor-block-settings-menu__control"
onClick={ flow( onDelete, onClick ) }
onClick={ flow( onRemove, onClick ) }
icon="trash"
label={ small ? label : undefined }
>
Expand All @@ -39,7 +39,7 @@ export default compose(
connect(
undefined,
( dispatch, ownProps ) => ( {
onDelete() {
onRemove() {
dispatch( removeBlocks( ownProps.uids ) );
},
} )
Expand All @@ -51,4 +51,4 @@ export default compose(
isLocked: !! templateLock,
};
} ),
)( BlockDeleteButton );
)( BlockRemoveButton );
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function BlockTransformations( { blocks, small = false, onTransform, onClick = n
return null;
}
return (
<div className="editor-block-settings-menu__block-transformations">
<div className="editor-block-settings-menu__section">
{ possibleBlockTransformations.map( ( { name, title, icon } ) => {
/* translators: label indicating the transformation of a block into another block */
const shownText = sprintf( __( 'Turn into %s' ), title );
Expand Down
8 changes: 4 additions & 4 deletions editor/components/block-settings-menu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import { IconButton, Dropdown, NavigableMenu } from '@wordpress/components';
import './style.scss';
import BlockInspectorButton from './block-inspector-button';
import BlockModeToggle from './block-mode-toggle';
import BlockDeleteButton from './block-delete-button';
import BlockRemoveButton from './block-remove-button';
import BlockTransformations from './block-transformations';
import ReusableBlockToggle from './reusable-block-toggle';
import ReusableBlockSettings from './reusable-block-settings';
import UnknownConverter from './unknown-converter';
import { selectBlock } from '../../store/actions';

Expand Down Expand Up @@ -57,8 +57,8 @@ function BlockSettingsMenu( { uids, onSelect, focus } ) {
<BlockInspectorButton onClick={ onClose } />
{ count === 1 && <BlockModeToggle uid={ uids[ 0 ] } onToggle={ onClose } /> }
{ count === 1 && <UnknownConverter uid={ uids[ 0 ] } /> }
<BlockDeleteButton uids={ uids } />
{ count === 1 && <ReusableBlockToggle uid={ uids[ 0 ] } onToggle={ onClose } /> }
<BlockRemoveButton uids={ uids } />
{ count === 1 && <ReusableBlockSettings uid={ uids[ 0 ] } onToggle={ onClose } /> }
<BlockTransformations uids={ uids } onClick={ onClose } />
</NavigableMenu>
) }
Expand Down
86 changes: 86 additions & 0 deletions editor/components/block-settings-menu/reusable-block-settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* External dependencies
*/
import { connect } from 'react-redux';
import { noop } from 'lodash';

/**
* WordPress dependencies
*/
import { Fragment } from '@wordpress/element';
import { IconButton } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { isReusableBlock } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { getBlock, getReusableBlock } from '../../store/selectors';
import { convertBlockToStatic, convertBlockToReusable, deleteReusableBlock } from '../../store/actions';

export function ReusableBlockSettings( { reusableBlock, onConvertToStatic, onConvertToReusable, onDelete } ) {
return (
<Fragment>
{ ! reusableBlock && (
<IconButton
className="editor-block-settings-menu__control"
icon="controls-repeat"
onClick={ onConvertToReusable }
>
{ __( 'Convert to Reusable Block' ) }
</IconButton>
) }
{ reusableBlock && (
<div className="editor-block-settings-menu__section">
<IconButton
className="editor-block-settings-menu__control"
icon="controls-repeat"
onClick={ onConvertToStatic }
>
{ __( 'Detach from Reusable Block' ) }
</IconButton>
<IconButton
className="editor-block-settings-menu__control"
icon="no"
disabled={ reusableBlock.isTemporary }
onClick={ () => onDelete( reusableBlock.id ) }
>
{ __( 'Delete Reusable Block' ) }
</IconButton>
</div>
) }
</Fragment>
);
}

export default connect(
( state, { uid } ) => {
const block = getBlock( state, uid );
return {
reusableBlock: isReusableBlock( block ) ? getReusableBlock( state, block.attributes.ref ) : null,
};
},
( dispatch, { uid, onToggle = noop } ) => ( {
onConvertToStatic() {
dispatch( convertBlockToStatic( uid ) );
onToggle();
},
onConvertToReusable() {
dispatch( convertBlockToReusable( uid ) );
onToggle();
},
onDelete( id ) {
// TODO: Make this a <Confirm /> component or similar
// eslint-disable-next-line no-alert
const hasConfirmed = window.confirm( __(
'Are you sure you want to delete this Reusable Block?\n\n' +
'It will be permanently removed from all posts and pages that use it.'
) );

if ( hasConfirmed ) {
dispatch( deleteReusableBlock( id ) );
onToggle();
}
},
} )
)( ReusableBlockSettings );
49 changes: 0 additions & 49 deletions editor/components/block-settings-menu/reusable-block-toggle.js

This file was deleted.

2 changes: 1 addition & 1 deletion editor/components/block-settings-menu/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
}
}

.editor-block-settings-menu__block-transformations {
.editor-block-settings-menu__section {
margin-top: $item-spacing;
padding-top: $item-spacing;
border-top: 1px solid $light-gray-500;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* External dependencies
*/
import { shallow } from 'enzyme';

/**
* Internal dependencies
*/
import { ReusableBlockSettings } from '../reusable-block-settings';

describe( 'ReusableBlockSettings', () => {
it( 'should allow converting a static block to reusable', () => {
const onConvert = jest.fn();
const wrapper = shallow(
<ReusableBlockSettings
reusableBlock={ null }
onConvertToReusable={ onConvert }
/>
);

const text = wrapper.find( 'IconButton' ).children().text();
expect( text ).toEqual( 'Convert to Reusable Block' );

wrapper.find( 'IconButton' ).simulate( 'click' );
expect( onConvert ).toHaveBeenCalled();
} );

it( 'should allow converting a reusable block to static', () => {
const onConvert = jest.fn();
const wrapper = shallow(
<ReusableBlockSettings
reusableBlock={ {} }
onConvertToStatic={ onConvert }
/>
);

const text = wrapper.find( 'IconButton' ).first().children().text();
expect( text ).toEqual( 'Detach from Reusable Block' );

wrapper.find( 'IconButton' ).first().simulate( 'click' );
expect( onConvert ).toHaveBeenCalled();
} );

it( 'should allow deleting a reusable block', () => {
const onDelete = jest.fn();
const wrapper = shallow(
<ReusableBlockSettings
reusableBlock={ { id: 123 } }
onDelete={ onDelete }
/>
);

const text = wrapper.find( 'IconButton' ).last().children().text();
expect( text ).toEqual( 'Delete Reusable Block' );

wrapper.find( 'IconButton' ).last().simulate( 'click' );
expect( onDelete ).toHaveBeenCalledWith( 123 );
} );
} );
Loading