Skip to content

Commit 378f441

Browse files
authored
Merge pull request #984 from njpanderson/update/block-mover-labels
Add contextually aware title for block mover control (#557)
2 parents 829cf8c + 4a87eb9 commit 378f441

File tree

3 files changed

+264
-3
lines changed

3 files changed

+264
-3
lines changed

editor/block-mover/index.js

+22-3
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,48 @@ import { first, last } from 'lodash';
88
* WordPress dependencies
99
*/
1010
import { IconButton } from 'components';
11+
import { getBlockType } from 'blocks';
1112

1213
/**
1314
* Internal dependencies
1415
*/
1516
import './style.scss';
16-
import { isFirstBlock, isLastBlock } from '../selectors';
17+
import { isFirstBlock, isLastBlock, getBlockOrder, getBlock } from '../selectors';
18+
import { getBlockMoverLabel } from './mover-label';
1719

18-
function BlockMover( { onMoveUp, onMoveDown, isFirst, isLast } ) {
20+
function BlockMover( { onMoveUp, onMoveDown, isFirst, isLast, uids, blockType, firstIndex } ) {
1921
// We emulate a disabled state because forcefully applying the `disabled`
2022
// attribute on the button while it has focus causes the screen to change
2123
// to an unfocused state (body as active element) without firing blur on,
2224
// the rendering parent, leaving it unable to react to focus out.
23-
2425
return (
2526
<div className="editor-block-mover">
2627
<IconButton
2728
className="editor-block-mover__control"
2829
onClick={ isFirst ? null : onMoveUp }
2930
icon="arrow-up-alt2"
31+
label={ getBlockMoverLabel(
32+
uids.length,
33+
blockType && blockType.title,
34+
firstIndex,
35+
isFirst,
36+
isLast,
37+
-1,
38+
) }
3039
aria-disabled={ isFirst }
3140
/>
3241
<IconButton
3342
className="editor-block-mover__control"
3443
onClick={ isLast ? null : onMoveDown }
3544
icon="arrow-down-alt2"
45+
label={ getBlockMoverLabel(
46+
uids.length,
47+
blockType && blockType.title,
48+
firstIndex,
49+
isFirst,
50+
isLast,
51+
1,
52+
) }
3653
aria-disabled={ isLast }
3754
/>
3855
</div>
@@ -43,6 +60,8 @@ export default connect(
4360
( state, ownProps ) => ( {
4461
isFirst: isFirstBlock( state, first( ownProps.uids ) ),
4562
isLast: isLastBlock( state, last( ownProps.uids ) ),
63+
firstIndex: getBlockOrder( state, first( ownProps.uids ) ),
64+
blockType: getBlockType( getBlock( state, first( ownProps.uids ) ).name ),
4665
} ),
4766
( dispatch, ownProps ) => ( {
4867
onMoveDown() {

editor/block-mover/mover-label.js

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/**
2+
* Wordpress dependencies
3+
*/
4+
import { __, sprintf } from 'i18n';
5+
6+
/**
7+
* Return a label for the block movement controls depending on block position.
8+
*
9+
* @param {number} selectedCount Number of blocks selected.
10+
* @param {string} type Block type - in the case of a single block, should
11+
* define its 'type'. I.e. 'Text', 'Heading', 'Image' etc.
12+
* @param {number} firstIndex The index (position - 1) of the first block selected.
13+
* @param {boolean} isFirst This is the first block.
14+
* @param {boolean} isLast This is the last block.
15+
* @param {number} dir Direction of movement (> 0 is considered to be going
16+
* down, < 0 is up).
17+
* @return {string} Label for the block movement controls.
18+
*/
19+
export function getBlockMoverLabel( selectedCount, type, firstIndex, isFirst, isLast, dir ) {
20+
const position = ( firstIndex + 1 );
21+
22+
if ( selectedCount > 1 ) {
23+
return getMultiBlockMoverLabel( selectedCount, firstIndex, isFirst, isLast, dir );
24+
}
25+
26+
if ( isFirst && isLast ) {
27+
// translators: %s: Type of block (i.e. Text, Image etc)
28+
return sprintf( __( 'Block "%s" is the only block, and cannot be moved' ), type );
29+
}
30+
31+
if ( dir > 0 && ! isLast ) {
32+
// moving down
33+
return sprintf(
34+
__( 'Move "%(type)s" block from position %(position)d down to position %(newPosition)d' ),
35+
{
36+
type,
37+
position,
38+
newPosition: ( position + 1 ),
39+
}
40+
);
41+
}
42+
43+
if ( dir > 0 && isLast ) {
44+
// moving down, and is the last item
45+
// translators: %s: Type of block (i.e. Text, Image etc)
46+
return sprintf( __( 'Block "%s" is at the end of the content and can’t be moved down' ), type );
47+
}
48+
49+
if ( dir < 0 && ! isFirst ) {
50+
// moving up
51+
return sprintf(
52+
__( 'Move "%(type)s" block from position %(position)d up to position %(newPosition)d' ),
53+
{
54+
type,
55+
position,
56+
newPosition: ( position - 1 ),
57+
}
58+
);
59+
}
60+
61+
if ( dir < 0 && isFirst ) {
62+
// moving up, and is the first item
63+
// translators: %s: Type of block (i.e. Text, Image etc)
64+
return sprintf( __( 'Block "%s" is at the beginning of the content and can’t be moved up' ), type );
65+
}
66+
}
67+
68+
/**
69+
* Return a label for the block movement controls depending on block position.
70+
*
71+
* @param {number} selectedCount Number of blocks selected.
72+
* @param {number} firstIndex The index (position - 1) of the first block selected.
73+
* @param {boolean} isFirst This is the first block.
74+
* @param {boolean} isLast This is the last block.
75+
* @param {number} dir Direction of movement (> 0 is considered to be going
76+
* down, < 0 is up).
77+
* @return {string} Label for the block movement controls.
78+
*/
79+
export function getMultiBlockMoverLabel( selectedCount, firstIndex, isFirst, isLast, dir ) {
80+
const position = ( firstIndex + 1 );
81+
82+
if ( dir < 0 && isFirst ) {
83+
return __( 'Blocks cannot be moved up as they are already at the top' );
84+
}
85+
86+
if ( dir > 0 && isLast ) {
87+
return __( 'Blocks cannot be moved down as they are already at the bottom' );
88+
}
89+
90+
if ( dir < 0 && ! isFirst ) {
91+
return sprintf(
92+
__( 'Move %(selectedCount)d blocks from position %(position)d up by one place' ),
93+
{
94+
selectedCount,
95+
position,
96+
}
97+
);
98+
}
99+
100+
if ( dir > 0 && ! isLast ) {
101+
return sprintf(
102+
__( 'Move %(selectedCount)d blocks from position %(position)s down by one place' ),
103+
{
104+
selectedCount,
105+
position,
106+
}
107+
);
108+
}
109+
}
+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { expect } from 'chai';
5+
6+
/**
7+
* Internal dependencies
8+
*/
9+
import { getBlockMoverLabel, getMultiBlockMoverLabel } from '../mover-label';
10+
11+
describe( 'block mover', () => {
12+
const dirUp = -1,
13+
dirDown = 1;
14+
15+
describe( 'getBlockMoverLabel', () => {
16+
const type = 'TestType';
17+
18+
it( 'Should generate a title for the first item moving up', () => {
19+
expect( getBlockMoverLabel(
20+
1,
21+
type,
22+
0,
23+
true,
24+
false,
25+
dirUp,
26+
) ).to.equal(
27+
`Block "${ type }" is at the beginning of the content and can’t be moved up`
28+
);
29+
} );
30+
31+
it( 'Should generate a title for the last item moving down', () => {
32+
expect( getBlockMoverLabel(
33+
1,
34+
type,
35+
3,
36+
false,
37+
true,
38+
dirDown,
39+
) ).to.equal(
40+
`Block "${ type }" is at the end of the content and can’t be moved down`
41+
);
42+
} );
43+
44+
it( 'Should generate a title for the second item moving up', () => {
45+
expect( getBlockMoverLabel(
46+
1,
47+
type,
48+
1,
49+
false,
50+
false,
51+
dirUp,
52+
) ).to.equal(
53+
`Move "${ type }" block from position 2 up to position 1`
54+
);
55+
} );
56+
57+
it( 'Should generate a title for the second item moving down', () => {
58+
expect( getBlockMoverLabel(
59+
1,
60+
type,
61+
1,
62+
false,
63+
false,
64+
dirDown,
65+
) ).to.equal(
66+
`Move "${ type }" block from position 2 down to position 3`
67+
);
68+
} );
69+
70+
it( 'Should generate a title for the only item in the list', () => {
71+
expect( getBlockMoverLabel(
72+
1,
73+
type,
74+
0,
75+
true,
76+
true,
77+
dirDown,
78+
) ).to.equal(
79+
`Block "${ type }" is the only block, and cannot be moved`
80+
);
81+
} );
82+
} );
83+
84+
describe( 'getMultiBlockMoverLabel', () => {
85+
it( 'Should generate a title moving multiple blocks up', () => {
86+
expect( getMultiBlockMoverLabel(
87+
4,
88+
1,
89+
false,
90+
true,
91+
dirUp,
92+
) ).to.equal(
93+
'Move 4 blocks from position 2 up by one place'
94+
);
95+
} );
96+
97+
it( 'Should generate a title moving multiple blocks down', () => {
98+
expect( getMultiBlockMoverLabel(
99+
4,
100+
0,
101+
true,
102+
false,
103+
dirDown,
104+
) ).to.equal(
105+
'Move 4 blocks from position 1 down by one place'
106+
);
107+
} );
108+
109+
it( 'Should generate a title for a selection of blocks at the top', () => {
110+
expect( getMultiBlockMoverLabel(
111+
4,
112+
1,
113+
true,
114+
true,
115+
dirUp,
116+
) ).to.equal(
117+
'Blocks cannot be moved up as they are already at the top'
118+
);
119+
} );
120+
121+
it( 'Should generate a title for a selection of blocks at the bottom', () => {
122+
expect( getMultiBlockMoverLabel(
123+
4,
124+
2,
125+
false,
126+
true,
127+
dirDown,
128+
) ).to.equal(
129+
'Blocks cannot be moved down as they are already at the bottom'
130+
);
131+
} );
132+
} );
133+
} );

0 commit comments

Comments
 (0)