-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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 contextually aware title for block mover control (#557) #984
Changes from 1 commit
032d581
8b85043
f8e5f20
bb865f6
1b47eca
c5697b9
45ef29d
351134d
2465af5
73edbd4
4a87eb9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,26 +13,47 @@ import { IconButton } from 'components'; | |
* Internal dependencies | ||
*/ | ||
import './style.scss'; | ||
import { isFirstBlock, isLastBlock } from '../selectors'; | ||
import { isFirstBlock, isLastBlock, getBlockOrder, getBlock } from '../selectors'; | ||
import { blockMoverLabel } from './mover-label'; | ||
import { getBlockType } from 'blocks'; | ||
|
||
function BlockMover( { onMoveUp, onMoveDown, isFirst, isLast } ) { | ||
function BlockMover( { onMoveUp, onMoveDown, isFirst, isLast, uids, block, firstPosition } ) { | ||
// We emulate a disabled state because forcefully applying the `disabled` | ||
// attribute on the button while it has focus causes the screen to change | ||
// to an unfocused state (body as active element) without firing blur on, | ||
// the rendering parent, leaving it unable to react to focus out. | ||
let blockType; | ||
|
||
if ( uids.length === 1 && block ) { | ||
blockType = getBlockType( block.name ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the only thing you are using |
||
} | ||
|
||
return ( | ||
<div className="editor-block-mover"> | ||
<IconButton | ||
className="editor-block-mover__control" | ||
onClick={ isFirst ? null : onMoveUp } | ||
icon="arrow-up-alt2" | ||
label={ blockMoverLabel( uids.length, { | ||
type: blockType && blockType.title, | ||
isFirst, | ||
isLast, | ||
firstPosition, | ||
dir: -1, | ||
} ) } | ||
aria-disabled={ isFirst } | ||
/> | ||
<IconButton | ||
className="editor-block-mover__control" | ||
onClick={ isLast ? null : onMoveDown } | ||
icon="arrow-down-alt2" | ||
label={ blockMoverLabel( uids.length, { | ||
type: blockType && blockType.title, | ||
isFirst, | ||
isLast, | ||
firstPosition, | ||
dir: 1, | ||
} ) } | ||
aria-disabled={ isLast } | ||
/> | ||
</div> | ||
|
@@ -43,6 +64,8 @@ export default connect( | |
( state, ownProps ) => ( { | ||
isFirst: isFirstBlock( state, first( ownProps.uids ) ), | ||
isLast: isLastBlock( state, last( ownProps.uids ) ), | ||
firstPosition: getBlockOrder( state, first( ownProps.uids ) ), | ||
block: getBlock( state, first( ownProps.uids ) ), | ||
} ), | ||
( dispatch, ownProps ) => ( { | ||
onMoveDown() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { __, sprintf } from 'i18n'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs to have the dependencies header comments. |
||
|
||
export function blockMoverLabel( selectedCount, { type, firstPosition, isFirst, isLast, dir } ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'd be good to add a function comment here describing purpose and spec. Also, maybe |
||
const position = ( firstPosition + 1 ); | ||
|
||
if ( selectedCount > 1 ) { | ||
return multiBlockMoverLabel( selectedCount, { isFirst, isLast, firstPosition, dir } ); | ||
} | ||
|
||
if ( isFirst && isLast ) { | ||
return sprintf( __( 'Block "%s" is the only block, and cannot be moved' ), type ); | ||
} | ||
|
||
if ( dir > 0 && ! isLast ) { | ||
// moving down | ||
return sprintf( | ||
__( 'Move "%s" block from position %s down to position %s' ), | ||
type, | ||
position, | ||
( position + 1 ) | ||
); | ||
} | ||
|
||
if ( dir > 0 && isLast ) { | ||
// moving down, and is the last item | ||
return sprintf( __( 'Block "%s" is at the end of the content and can’t be moved down' ), type ); | ||
} | ||
|
||
if ( dir < 0 && ! isFirst ) { | ||
// moving up | ||
return sprintf( | ||
__( 'Move "%s" block from position %s up to position %s' ), | ||
type, | ||
position, | ||
( position - 1 ) | ||
); | ||
} | ||
|
||
if ( dir < 0 && isFirst ) { | ||
// moving up, and is the first item | ||
return sprintf( __( 'Block "%s" is at the beginning of the content and can’t be moved up' ), type ); | ||
} | ||
|
||
return ''; | ||
} | ||
|
||
export function multiBlockMoverLabel( selectedCount, { isFirst, isLast, firstPosition, dir } ) { | ||
const position = ( firstPosition + 1 ); | ||
|
||
if ( dir < 0 && isFirst ) { | ||
return __( 'Blocks cannot be moved up as they are already at the top' ); | ||
} | ||
|
||
if ( dir > 0 && isLast ) { | ||
return __( 'Blocks cannot be moved down as they are already at the bottom' ); | ||
} | ||
|
||
if ( dir < 0 && ! isFirst ) { | ||
return sprintf( | ||
__( 'Move %s blocks from position %s up by one place' ), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To accommodate languages which might swap placeholder ordering, it's recommended to use either argument swapping or named placeholders.
https://codex.wordpress.org/I18n_for_WordPress_Developers#Placeholders There's a few other instances of this in the file. Examples: __( 'Move %1$s blocks from position %2$s up by one place' ), __( 'Move %(selectedCount)s blocks from position %(position)s up by one place' ), Additionally, a |
||
selectedCount, | ||
position | ||
); | ||
} | ||
|
||
if ( dir > 0 && ! isLast ) { | ||
return sprintf( | ||
__( 'Move %s blocks from position %s down by one place' ), | ||
selectedCount, | ||
position | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
/** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice work on the tests 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. Hopefully that's all the coverage you'd need. |
||
* External dependencies | ||
*/ | ||
import { expect } from 'chai'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { blockMoverLabel, multiBlockMoverLabel } from '../mover-label'; | ||
|
||
describe( 'block mover', () => { | ||
describe( 'blockMoverLabel', () => { | ||
const type = 'TestType'; | ||
|
||
it( 'Should generate a title for the first item moving up', () => { | ||
expect( blockMoverLabel( 1, { | ||
type, | ||
firstPosition: 0, | ||
isFirst: true, | ||
isLast: false, | ||
dir: -1, | ||
} ) ).to.equal( | ||
`Block "${ type }" is at the beginning of the content and can’t be moved up` | ||
); | ||
} ); | ||
|
||
it( 'Should generate a title for the last item moving down', () => { | ||
expect( blockMoverLabel( 1, { | ||
type, | ||
firstPosition: 3, | ||
isFirst: false, | ||
isLast: true, | ||
dir: 1, | ||
} ) ).to.equal( | ||
`Block "${ type }" is at the end of the content and can’t be moved down` | ||
); | ||
} ); | ||
|
||
it( 'Should generate a title for the second item moving up', () => { | ||
expect( blockMoverLabel( 1, { | ||
type, | ||
firstPosition: 1, | ||
isFirst: false, | ||
isLast: false, | ||
dir: -1, | ||
} ) ).to.equal( | ||
`Move "${ type }" block from position 2 up to position 1` | ||
); | ||
} ); | ||
|
||
it( 'Should generate a title for the second item moving down', () => { | ||
expect( blockMoverLabel( 1, { | ||
type, | ||
firstPosition: 1, | ||
isFirst: false, | ||
isLast: false, | ||
dir: 1, | ||
} ) ).to.equal( | ||
`Move "${ type }" block from position 2 down to position 3` | ||
); | ||
} ); | ||
|
||
it( 'Should generate a title for the only item in the list', () => { | ||
expect( blockMoverLabel( 1, { | ||
type, | ||
firstPosition: 0, | ||
isFirst: true, | ||
isLast: true, | ||
dir: 1, | ||
} ) ).to.equal( | ||
`Block "${ type }" is the only block, and cannot be moved` | ||
); | ||
} ); | ||
} ); | ||
|
||
describe( 'multiBlockMoverLabel', () => { | ||
it( 'Should generate a title moving multiple blocks up', () => { | ||
expect( multiBlockMoverLabel( 4, { | ||
firstPosition: 1, | ||
isFirst: false, | ||
isLast: true, | ||
dir: -1, | ||
} ) ).to.equal( | ||
'Move 4 blocks from position 2 up by one place' | ||
); | ||
} ); | ||
|
||
it( 'Should generate a title moving multiple blocks down', () => { | ||
expect( multiBlockMoverLabel( 4, { | ||
firstPosition: 0, | ||
isFirst: true, | ||
isLast: false, | ||
dir: 1, | ||
} ) ).to.equal( | ||
'Move 4 blocks from position 1 down by one place' | ||
); | ||
} ); | ||
|
||
it( 'Should generate a title for a selection of blocks at the top', () => { | ||
expect( multiBlockMoverLabel( 4, { | ||
firstPosition: 1, | ||
isFirst: true, | ||
isLast: true, | ||
dir: -1, | ||
} ) ).to.equal( | ||
'Blocks cannot be moved up as they are already at the top' | ||
); | ||
} ); | ||
|
||
it( 'Should generate a title for a selection of blocks at the bottom', () => { | ||
expect( multiBlockMoverLabel( 4, { | ||
firstPosition: 2, | ||
isFirst: false, | ||
isLast: true, | ||
dir: 1, | ||
} ) ).to.equal( | ||
'Blocks cannot be moved down as they are already at the bottom' | ||
); | ||
} ); | ||
} ); | ||
} ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are both
firstPosition
andisFirst
needed?