Skip to content

Commit

Permalink
Block List: Show block breadcrumb prior to block
Browse files Browse the repository at this point in the history
  • Loading branch information
aduth committed Mar 21, 2018
1 parent dbcc268 commit c2c24e9
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 24 deletions.
2 changes: 2 additions & 0 deletions editor/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import InvalidBlockWarning from './invalid-block-warning';
import BlockCrashWarning from './block-crash-warning';
import BlockCrashBoundary from './block-crash-boundary';
import BlockHtml from './block-html';
import BlockBreadcrumb from './breadcrumb';
import BlockContextualToolbar from './block-contextual-toolbar';
import BlockMultiControls from './multi-controls';
import BlockMobileToolbar from './block-mobile-toolbar';
Expand Down Expand Up @@ -521,6 +522,7 @@ export class BlockListBlock extends Component {
renderBlockMenu={ renderBlockMenu }
/>
) }
{ isHovered && <BlockBreadcrumb uid={ block.uid } /> }
{ shouldShowContextualToolbar && <BlockContextualToolbar /> }
{ isFirstMultiSelected && <BlockMultiControls rootUID={ rootUID } /> }
<IgnoreNestedEvents
Expand Down
77 changes: 77 additions & 0 deletions editor/components/block-list/breadcrumb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* WordPress dependencies
*/
import { compose } from '@wordpress/element';
import { Dashicon, Tooltip, Toolbar, Button } from '@wordpress/components';
import { withDispatch, withSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import NavigableToolbar from '../navigable-toolbar';
import BlockTitle from '../block-title';

/**
* Stops propagation of the given event argument. Assumes that the event has
* been completely handled and no other listeners should be informed.
*
* For the breadcrumb component, this is used for improved interoperability
* with the block's `onFocus` handler which selects the block, thus conflicting
* with the intention to select the root block.
*
* @param {Event} event Event for which propagation should be stopped.
*/
function stopPropagation( event ) {
event.stopPropagation();
}

/**
* Block breadcrumb component, displaying the label of the block. If the block
* descends from a root block, a button is displayed enabling the user to select
* the root block.
*
* @param {string} props.uid UID of block.
* @param {string} props.rootUID UID of block's root.
* @param {Function} props.selectRootBlock Callback to select root block.
*
* @return {WPElement} Breadcrumb element.
*/
function BlockBreadcrumb( { uid, rootUID, selectRootBlock } ) {
return (
<NavigableToolbar className="editor-block-breadcrumb">
<Toolbar>
{ rootUID && (
<Tooltip text={ __( 'Select parent block' ) }>
<Button
onClick={ selectRootBlock }
onFocus={ stopPropagation }
>
<Dashicon icon="arrow-left" uid={ uid } />
</Button>
</Tooltip>
) }
<BlockTitle uid={ uid } />
</Toolbar>
</NavigableToolbar>
);
}

export default compose( [
withSelect( ( select, ownProps ) => {
const { getBlockRootUID } = select( 'core/editor' );
const { uid } = ownProps;

return {
rootUID: getBlockRootUID( uid ),
};
} ),
withDispatch( ( dispatch, ownProps ) => {
const { rootUID } = ownProps;
const { selectBlock } = dispatch( 'core/editor' );

return {
selectRootBlock: () => selectBlock( rootUID ),
};
} ),
] )( BlockBreadcrumb );
67 changes: 43 additions & 24 deletions editor/components/block-list/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,8 @@
* Block Toolbar
*/

.editor-block-contextual-toolbar {
.editor-block-contextual-toolbar,
.editor-block-breadcrumb {
position: sticky;
z-index: z-index( '.editor-block-contextual-toolbar' );
white-space: nowrap;
Expand Down Expand Up @@ -560,35 +561,53 @@
top: -1px; // stack borders
}

.editor-block-toolbar {
border: 1px solid $light-gray-500;
width: 100%;
// Reset pointer-events on children.
& > * {
pointer-events: auto;
}
}

// this prevents floats from messing up the position
position: absolute;
left: 0;
.editor-block-contextual-toolbar .editor-block-toolbar,
.editor-block-breadcrumb .components-toolbar {
border: 1px solid $light-gray-500;
width: 100%;

.editor-block-list__block[data-align="right"] & {
left: auto;
right: 0;
}
// this prevents floats from messing up the position
position: absolute;
left: 0;

// remove stacked borders in inline toolbar
> div:first-child {
margin-left: -1px;
}
.editor-block-list__block[data-align="right"] & {
left: auto;
right: 0;
}

> .editor-block-switcher:first-child {
margin-left: -2px;
}
// remove stacked borders in inline toolbar
> div:first-child {
margin-left: -1px;
}

@include break-small() {
width: auto;
}
> .editor-block-switcher:first-child {
margin-left: -2px;
}

// Reset pointer-events on children.
& > * {
pointer-events: auto;
@include break-small() {
width: auto;
}
}

.editor-block-breadcrumb .components-toolbar {
padding: 0px 12px;
line-height: $block-toolbar-height - 1px;
font-family: $default-font;
font-size: $default-font-size;
color: $dark-gray-500;
cursor: default;

.components-button {
margin-left: -12px;
margin-right: 12px;
border-right: 1px solid $light-gray-500;
color: $dark-gray-500;
padding-top: 6px;
}
}
10 changes: 10 additions & 0 deletions editor/components/block-title/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Block Title
===========

Renders the block's configured title as a string, or empty if the title cannot be determined.

## Usage

```jsx
<BlockTitle uid="afd1cb17-2c08-4e7a-91be-007ba7ddc3a1" />
```
41 changes: 41 additions & 0 deletions editor/components/block-title/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* WordPress dependencies
*/
import { withSelect } from '@wordpress/data';
import { getBlockType } from '@wordpress/blocks';

/**
* Renders the block's configured title as a string, or empty if the title
* cannot be determined.
*
* @example
*
* ```jsx
* <BlockTitle uid="afd1cb17-2c08-4e7a-91be-007ba7ddc3a1" />
* ```
*
* @param {?string} props.name Block name.
*
* @return {?string} Block title.
*/
export function BlockTitle( { name } ) {
if ( ! name ) {
return null;
}

const blockType = getBlockType( name );
if ( ! blockType ) {
return null;
}

return blockType.title;
}

export default withSelect( ( select, ownProps ) => {
const { getBlockName } = select( 'core/editor' );
const { uid } = ownProps;

return {
name: getBlockName( uid ),
};
} )( BlockTitle );
43 changes: 43 additions & 0 deletions editor/components/block-title/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* External dependencies
*/
import { shallow } from 'enzyme';

/**
* Internal dependencies
*/
import { BlockTitle } from '../';

jest.mock( '@wordpress/blocks', () => {
return {
getBlockType( name ) {
switch ( name ) {
case 'name-not-exists':
return null;

case 'name-exists':
return { title: 'Block Title' };
}
},
};
} );

describe( 'BlockTitle', () => {
it( 'renders nothing if name is falsey', () => {
const wrapper = shallow( <BlockTitle /> );

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

it( 'renders nothing if block type does not exist', () => {
const wrapper = shallow( <BlockTitle name="name-not-exists" /> );

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

it( 'renders title if block type exists', () => {
const wrapper = shallow( <BlockTitle name="name-exists" /> );

expect( wrapper.text() ).toBe( 'Block Title' );
} );
} );
14 changes: 14 additions & 0 deletions editor/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,20 @@ export const getBlockDependantsCacheBust = createSelector(
),
);

/**
* Returns a block's name given its UID, or null if no block exists with the
* UID.
*
* @param {Object} state Editor state.
* @param {string} uid Block unique ID.
*
* @return {string} Block name.
*/
export function getBlockName( state, uid ) {
const block = state.editor.present.blocksByUid[ uid ];
return block ? block.name : null;
}

/**
* Returns a block given its unique ID. This is a parsed copy of the block,
* containing its `blockName`, identifier (`uid`), and current `attributes`
Expand Down
46 changes: 46 additions & 0 deletions editor/store/test/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const {
isEditedPostBeingScheduled,
getEditedPostPreviewLink,
getBlockDependantsCacheBust,
getBlockName,
getBlock,
getBlocks,
getBlockCount,
Expand Down Expand Up @@ -1147,6 +1148,51 @@ describe( 'selectors', () => {
} );
} );

describe( 'getBlockName', () => {
it( 'returns null if no block by uid', () => {
const state = {
currentPost: {},
editor: {
present: {
blocksByUid: {},
blockOrder: {},
edits: {},
},
},
};

const name = getBlockName( state, 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' );

expect( name ).toBe( null );
} );

it( 'returns block name', () => {
const state = {
currentPost: {},
editor: {
present: {
blocksByUid: {
'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
uid: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
name: 'core/paragraph',
attributes: {},
},
},
blockOrder: {
'': [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': [],
},
edits: {},
},
},
};

const name = getBlockName( state, 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' );

expect( name ).toBe( 'core/paragraph' );
} );
} );

describe( 'getBlock', () => {
it( 'should return the block', () => {
const state = {
Expand Down

0 comments on commit c2c24e9

Please sign in to comment.