-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
list-view.spec.js
1109 lines (999 loc) · 30.4 KB
/
list-view.spec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* WordPress dependencies
*/
const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );
test.describe( 'List View', () => {
test.use( {
listViewUtils: async ( { page, pageUtils, editor }, use ) => {
await use( new ListViewUtils( { page, pageUtils, editor } ) );
},
} );
test.beforeEach( async ( { admin } ) => {
await admin.createNewPost();
} );
test( 'allows a user to drag a block to a new sibling position', async ( {
editor,
page,
pageUtils,
} ) => {
// Insert a couple of blocks of different types.
await editor.insertBlock( { name: 'core/heading' } );
await editor.insertBlock( { name: 'core/image' } );
await editor.insertBlock( { name: 'core/paragraph' } );
// Open List View.
await pageUtils.pressKeys( 'access+o' );
const listView = page.getByRole( 'treegrid', {
name: 'Block navigation structure',
} );
// The last inserted block should be selected.
await expect(
listView.getByRole( 'gridcell', {
name: 'Paragraph',
exact: true,
selected: true,
} )
).toBeVisible();
// Ensure the setup is correct before dragging.
await expect
.poll( editor.getBlocks )
.toMatchObject( [
{ name: 'core/heading' },
{ name: 'core/image' },
{ name: 'core/paragraph' },
] );
// Drag the paragraph above the heading.
const paragraphBlockItem = listView.getByRole( 'gridcell', {
name: 'Paragraph',
exact: true,
} );
const imageBlockItem = listView.getByRole( 'gridcell', {
name: 'Image',
exact: true,
} );
const headingBlockItem = listView.getByRole( 'gridcell', {
name: 'Heading',
exact: true,
} );
await paragraphBlockItem.hover();
await page.mouse.down();
// To work around a drag and drop bug in Safari, the list view applies
// `pointer-events: none` to the list view while dragging, so that
// `onDragLeave` is not fired when dragging within the list view.
// Without the `force: true` option, the `hover` action will fail
// as playwright will complain that pointer-events are intercepted.
// https://bugs.webkit.org/show_bug.cgi?id=66547
// See: https://github.com/WordPress/gutenberg/pull/56625
// Hover over each block to mimic moving up the list view.
// Also, hover twice to ensure a dragover event is dispatched.
// See: https://playwright.dev/docs/input#dragging-manually
await imageBlockItem.hover( { force: true } );
await imageBlockItem.hover( { force: true } );
await headingBlockItem.hover( { force: true } );
await headingBlockItem.hover( { force: true } );
// Disable reason: Need to wait until the throttle timeout of 250ms has passed.
/* eslint-disable playwright/no-wait-for-timeout */
await editor.page.waitForTimeout( 300 );
/* eslint-enable playwright/no-wait-for-timeout */
await page.mouse.up();
// Ensure the block was dropped correctly.
await expect
.poll( editor.getBlocks )
.toMatchObject( [
{ name: 'core/paragraph' },
{ name: 'core/heading' },
{ name: 'core/image' },
] );
} );
// Check for regressions of https://github.com/WordPress/gutenberg/issues/38763.
test( 'shows the correct amount of blocks after a block is removed in the canvas', async ( {
editor,
page,
pageUtils,
} ) => {
// Insert a couple of blocks of different types.
await editor.insertBlock( { name: 'core/image' } );
await editor.insertBlock( { name: 'core/heading' } );
await editor.insertBlock( { name: 'core/paragraph' } );
// Open List View.
await pageUtils.pressKeys( 'access+o' );
const listView = page.getByRole( 'treegrid', {
name: 'Block navigation structure',
} );
// The last inserted block should be selected.
await expect(
listView.getByRole( 'gridcell', {
name: 'Paragraph',
exact: true,
selected: true,
} )
).toBeVisible();
// Go to the image block in List View.
await pageUtils.pressKeys( 'ArrowUp', { times: 2 } );
await expect(
listView.getByRole( 'link', {
name: 'Image',
} )
).toBeFocused();
// Select the image block in the canvas.
await page.keyboard.press( 'Enter' );
const imageBlock = editor.canvas.getByRole( 'document', {
name: 'Block: Image',
} );
await expect(
imageBlock.getByRole( 'button', { name: 'Upload' } )
).toBeFocused();
await page.keyboard.press( 'ArrowUp' );
await expect( imageBlock ).toBeFocused();
// Delete the image block in the canvas.
await page.keyboard.press( 'Backspace' );
// List View should have two rows.
await expect( listView.getByRole( 'row' ) ).toHaveCount( 2 );
} );
test( 'expands nested list items', async ( {
editor,
page,
pageUtils,
} ) => {
await editor.insertBlock( { name: 'core/cover' } );
// Click first color option from the block placeholder's color picker to
// make the inner blocks appear.
await editor.canvas
.getByRole( 'document', { name: 'Block: Cover' } )
.getByRole( 'option', { name: /Color: /i } )
.first()
.click();
// Open List View.
await pageUtils.pressKeys( 'access+o' );
const listView = page.getByRole( 'treegrid', {
name: 'Block navigation structure',
} );
// Things start off expanded.
await expect(
listView.getByRole( 'link', {
name: 'Cover',
expanded: true,
} )
).toBeVisible();
// The child paragraph block should be selected.
await expect(
listView.getByRole( 'gridcell', {
name: 'Paragraph',
exact: true,
selected: true,
} )
).toBeVisible();
// Collapse the Cover block.
await listView
.getByRole( 'gridcell', { name: 'Cover', exact: true } )
.getByTestId( 'list-view-expander', { includeHidden: true } )
// Force the click to bypass the visibility check. The expander is
// intentionally aria-hidden. See the implementation for details.
.click( { force: true } );
// Check that we're collapsed.
await expect( listView.getByRole( 'row' ) ).toHaveCount( 1 );
// Click the Cover block List View item.
await listView
.getByRole( 'link', {
name: 'Cover',
expanded: false,
} )
.click();
// Click the Cover block title placeholder.
await editor.canvas
.getByRole( 'document', { name: 'Block: Cover' } )
.getByRole( 'document', { name: /Empty block/i } )
.click();
// The child paragraph block in List View should be selected.
await expect(
listView.getByRole( 'gridcell', {
name: 'Paragraph',
exact: true,
selected: true,
} )
).toBeVisible();
} );
test( 'moves focus to start/end of list with Home/End keys', async ( {
editor,
page,
pageUtils,
} ) => {
// Insert a couple of blocks of different types.
await editor.insertBlock( { name: 'core/image' } );
await editor.insertBlock( { name: 'core/heading' } );
await editor.insertBlock( { name: 'core/paragraph' } );
await editor.insertBlock( { name: 'core/columns' } );
await editor.insertBlock( { name: 'core/group' } );
// Open List View.
await pageUtils.pressKeys( 'access+o' );
const listView = page.getByRole( 'treegrid', {
name: 'Block navigation structure',
} );
// The last inserted block should be selected.
await expect(
listView.getByRole( 'gridcell', {
name: 'Group',
exact: true,
selected: true,
} )
).toBeVisible();
// Press Home to go to the first inserted block (image).
await page.keyboard.press( 'Home' );
await expect(
listView.getByRole( 'link', {
name: 'Image',
} )
).toBeFocused();
// Press End followed by Arrow Up to go to the second to last block (columns).
await page.keyboard.press( 'End' );
await page.keyboard.press( 'ArrowUp' );
await expect(
listView.getByRole( 'link', {
name: 'Columns',
exact: true,
} )
).toBeFocused();
// Navigate the right column to image block options button via Home key.
await page.keyboard.press( 'ArrowRight' );
await page.keyboard.press( 'Home' );
await expect(
listView.getByRole( 'button', { name: 'Options for Image' } )
).toBeFocused();
// Navigate the right column to group block options button.
await page.keyboard.press( 'End' );
await expect(
listView.getByRole( 'button', { name: 'Options for Group' } )
).toBeFocused();
} );
// If list view sidebar is open and focus is not inside the sidebar, move
// focus to the sidebar when using the shortcut. If focus is inside the
// sidebar, shortcut should close the sidebar.
test( 'ensures List View global shortcut works properly', async ( {
editor,
page,
pageUtils,
} ) => {
await editor.insertBlock( { name: 'core/image' } );
await editor.insertBlock( {
name: 'core/paragraph',
attributes: { content: 'Paragraph text' },
} );
await expect(
editor.canvas.getByRole( 'document', {
name: 'Block: Paragraph',
} )
).toBeFocused();
// Open List View.
await pageUtils.pressKeys( 'access+o' );
const listView = page.getByRole( 'treegrid', {
name: 'Block navigation structure',
} );
// The paragraph item should be selected.
await expect(
listView.getByRole( 'gridcell', {
name: 'Paragraph',
exact: true,
selected: true,
} )
).toBeVisible();
// Navigate to the image block item.
await page.keyboard.press( 'ArrowUp' );
const imageItem = listView.getByRole( 'link', {
name: 'Image',
} );
await expect( imageItem ).toBeFocused();
// Hit enter to focus the Image block.
await page.keyboard.press( 'Enter' );
await expect(
editor.canvas
.getByRole( 'document', {
name: 'Block: Image',
} )
.getByRole( 'button', { name: 'Upload' } )
).toBeFocused();
// Since focus is now at the image block upload button in the canvas,
// pressing the list view shortcut should bring focus back to the image
// block in the list view.
await pageUtils.pressKeys( 'access+o' );
await expect( imageItem ).toBeFocused();
// Since focus is now inside the list view, the shortcut should close
// the sidebar.
await pageUtils.pressKeys( 'access+o' );
// Focus should now be on the list view toggle button.
await expect(
page.getByRole( 'button', { name: 'Document Overview' } )
).toBeFocused();
// List View should be closed.
await expect( listView ).toBeHidden();
// Open List View.
await pageUtils.pressKeys( 'access+o' );
// Focus the list view close button and make sure the shortcut will
// close the list view. This is to catch a bug where elements could be
// out of range of the sidebar region. Must shift+tab 2 times to reach
// close button before tab panel.
await pageUtils.pressKeys( 'shift+Tab' );
await expect(
page
.getByRole( 'region', { name: 'Document Overview' } )
.getByRole( 'button', {
name: 'Close',
} )
).toBeFocused();
// Close List View and ensure it's closed.
await pageUtils.pressKeys( 'access+o' );
await expect( listView ).toBeHidden();
// Open List View.
await pageUtils.pressKeys( 'access+o' );
// Focus the outline tab and select it. This test ensures the outline
// tab receives similar focus events based on the shortcut.
await pageUtils.pressKeys( 'shift+Tab' );
await page.keyboard.press( 'ArrowRight' );
const outlineButton = page.getByRole( 'tab', {
name: 'Outline',
} );
await expect( outlineButton ).toBeFocused();
await page.keyboard.press( 'Enter' );
// From here, tab in to the editor so focus can be checked on return to
// the outline tab in the sidebar.
await pageUtils.pressKeys( 'Tab', { times: 2 } );
// Focus should be placed on the outline tab button since there is
// nothing to focus inside the tab itself.
await pageUtils.pressKeys( 'access+o' );
await expect( outlineButton ).toBeFocused();
// Close List View and ensure it's closed.
await pageUtils.pressKeys( 'access+o' );
await expect( listView ).toBeHidden();
} );
test( 'should place focus on the currently selected block in the canvas', async ( {
editor,
page,
pageUtils,
} ) => {
// Insert a couple of blocks of different types.
await editor.insertBlock( { name: 'core/image' } );
await editor.insertBlock( { name: 'core/heading' } );
await editor.insertBlock( { name: 'core/paragraph' } );
// Open List View.
await pageUtils.pressKeys( 'access+o' );
const listView = page.getByRole( 'treegrid', {
name: 'Block navigation structure',
} );
// The last inserted block should be selected.
await expect(
listView.getByRole( 'gridcell', {
name: 'Paragraph',
exact: true,
selected: true,
} )
).toBeVisible();
// Go to the image block in List View.
await pageUtils.pressKeys( 'ArrowUp', { times: 2 } );
await expect(
listView.getByRole( 'link', {
name: 'Image',
} )
).toBeFocused();
// Select the image block in the canvas.
await page.keyboard.press( 'Enter' );
const imageBlock = editor.canvas.getByRole( 'document', {
name: 'Block: Image',
} );
await expect(
imageBlock.getByRole( 'button', { name: 'Upload' } )
).toBeFocused();
// Triggering the List View shortcut should result in the image block gaining focus.
await pageUtils.pressKeys( 'access+o' );
await expect(
listView.getByRole( 'link', {
name: 'Image',
} )
).toBeFocused();
} );
test( 'should cut, copy, paste, select, duplicate, delete, and deselect blocks using keyboard', async ( {
editor,
page,
pageUtils,
listViewUtils,
} ) => {
// Insert some blocks of different types.
await editor.insertBlock( {
name: 'core/group',
innerBlocks: [ { name: 'core/pullquote' } ],
} );
await editor.insertBlock( {
name: 'core/columns',
innerBlocks: [
{
name: 'core/column',
innerBlocks: [
{ name: 'core/heading' },
{ name: 'core/paragraph' },
],
},
{
name: 'core/column',
innerBlocks: [ { name: 'core/verse' } ],
},
],
} );
await editor.insertBlock( { name: 'core/file' } );
// Open List View.
const listView = await listViewUtils.openListView();
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'The last inserted block should be selected and focused.'
)
.toMatchObject( [
{ name: 'core/group' },
{ name: 'core/columns' },
{ name: 'core/file', selected: true, focused: true },
] );
// Move up to columns block, expand, and then move to the first column block.
await page.keyboard.press( 'ArrowUp' );
await page.keyboard.press( 'ArrowRight' );
await page.keyboard.press( 'ArrowDown' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'The last inserted block should be selected, while the first column block should be focused.'
)
.toMatchObject( [
{ name: 'core/group' },
{
name: 'core/columns',
innerBlocks: [
{ name: 'core/column', selected: false, focused: true },
{ name: 'core/column' },
],
},
{ name: 'core/file', selected: true, focused: false },
] );
// Select all sibling column blocks at current level.
await pageUtils.pressKeys( 'primary+a' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'All column blocks should be selected, with the first one focused.'
)
.toMatchObject( [
{ name: 'core/group', selected: false, focused: false },
{
name: 'core/columns',
innerBlocks: [
{ name: 'core/column', selected: true, focused: true },
{ name: 'core/column', selected: true, focused: false },
],
selected: false,
},
{ name: 'core/file', selected: false, focused: false },
] );
// Select next parent (the columns block).
await pageUtils.pressKeys( 'primary+a' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'The columns block should be selected and focused.'
)
.toMatchObject( [
{ name: 'core/group', selected: false, focused: false },
{
name: 'core/columns',
innerBlocks: [
{ name: 'core/column' },
{ name: 'core/column' },
],
selected: true,
focused: true,
},
{ name: 'core/file', selected: false, focused: false },
] );
// Select all siblings at root level.
await pageUtils.pressKeys( 'primary+a' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'All blocks should be selected.'
)
.toMatchObject( [
{ name: 'core/group', selected: true, focused: false },
{
name: 'core/columns',
innerBlocks: [
{ name: 'core/column' },
{ name: 'core/column' },
],
selected: true,
focused: true,
},
{ name: 'core/file', selected: true, focused: false },
] );
// Deselect blocks via Escape key.
await page.keyboard.press( 'Escape' );
// Collapse the columns block.
await page.keyboard.press( 'ArrowLeft' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'All blocks should be deselected, with focus on the Columns block.'
)
.toMatchObject( [
{ name: 'core/group', selected: false, focused: false },
{
name: 'core/columns',
selected: false,
focused: true,
},
{ name: 'core/file', selected: false, focused: false },
] );
// Move focus and selection to the file block to set up for testing duplication.
await listView
.getByRole( 'gridcell', { name: 'File', exact: true } )
.dblclick();
// Test duplication behaviour.
await pageUtils.pressKeys( 'primaryShift+d' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Duplicating a block should retain selection on existing block, move focus to duplicated block.'
)
.toMatchObject( [
{ name: 'core/group' },
{ name: 'core/columns' },
{ name: 'core/file', selected: true },
{ name: 'core/file', focused: true },
] );
// Move focus to the first file block, and then delete it.
await page.keyboard.press( 'ArrowUp' );
await page.keyboard.press( 'Delete' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Deleting a block should move focus and selection to the previous block'
)
.toMatchObject( [
{ name: 'core/group' },
{ name: 'core/columns', selected: true, focused: true },
{ name: 'core/file' },
] );
// Expand the current column.
await page.keyboard.press( 'ArrowRight' );
await page.keyboard.press( 'ArrowDown' );
await page.keyboard.press( 'ArrowDown' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Move focus but do not select the second column'
)
.toMatchObject( [
{ name: 'core/group' },
{
name: 'core/columns',
selected: true,
innerBlocks: [
{ name: 'core/column' },
{ name: 'core/column', focused: true },
],
},
{ name: 'core/file' },
] );
await page.keyboard.press( 'Delete' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Deleting a inner block moves focus to the previous inner block'
)
.toMatchObject( [
{ name: 'core/group' },
{
name: 'core/columns',
selected: true,
innerBlocks: [
{
name: 'core/column',
selected: false,
focused: true,
},
],
},
{ name: 'core/file' },
] );
// Expand the current column.
await page.keyboard.press( 'ArrowRight' );
// Move focus and select the Heading block.
await listView
.getByRole( 'gridcell', { name: 'Heading', exact: true } )
.dblclick();
// Select both inner blocks in the column.
await page.keyboard.press( 'Shift+ArrowDown' );
await page.keyboard.press( 'Backspace' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Deleting multiple blocks moves focus to the parent block'
)
.toMatchObject( [
{ name: 'core/group' },
{
name: 'core/columns',
innerBlocks: [
{
name: 'core/column',
selected: true,
focused: true,
innerBlocks: [],
},
],
},
{ name: 'core/file' },
] );
// Move focus and select the first block.
await listView
.getByRole( 'gridcell', { name: 'Group', exact: true } )
.dblclick();
await page.keyboard.press( 'Backspace' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Deleting the first block moves focus to the second block'
)
.toMatchObject( [
{
name: 'core/columns',
selected: true,
focused: true,
},
{ name: 'core/file' },
] );
// Delete remaining blocks.
// Keyboard shortcut should also work.
await pageUtils.pressKeys( 'access+z' );
await pageUtils.pressKeys( 'access+z' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Deleting the only blocks left will create a default block and focus/select it'
)
.toMatchObject( [
{
name: 'core/paragraph',
selected: true,
focused: true,
},
] );
await editor.insertBlock( { name: 'core/heading' } );
await page.evaluate( () =>
window.wp.data.dispatch( 'core/block-editor' ).clearSelectedBlock()
);
await listView
.getByRole( 'gridcell', { name: 'Paragraph' } )
.getByRole( 'link' )
.focus();
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Block selection is cleared and focus is on the paragraph block'
)
.toMatchObject( [
{ name: 'core/paragraph', selected: false, focused: true },
{ name: 'core/heading', selected: false },
] );
await pageUtils.pressKeys( 'access+z' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Deleting blocks without existing selection will not select blocks'
)
.toMatchObject( [
{ name: 'core/heading', selected: false, focused: true },
] );
// Insert a block that is locked and cannot be removed.
await editor.insertBlock( {
name: 'core/file',
attributes: { lock: { move: false, remove: true } },
} );
// Click on the Heading block to select it.
await listView
.getByRole( 'gridcell', { name: 'Heading', exact: true } )
.click();
await listView
.getByRole( 'gridcell', { name: 'File' } )
.getByRole( 'link' )
.focus();
for ( const keys of [ 'Delete', 'Backspace', 'access+z' ] ) {
await pageUtils.pressKeys( keys );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Trying to delete locked blocks should not do anything'
)
.toMatchObject( [
{ name: 'core/heading', selected: true, focused: false },
{ name: 'core/file', selected: false, focused: true },
] );
}
// Deselect blocks via Escape key.
await page.keyboard.press( 'Escape' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Pressing Escape should deselect blocks'
)
.toMatchObject( [
{ name: 'core/heading', selected: false, focused: false },
{ name: 'core/file', selected: false, focused: true },
] );
// Copy and paste blocks. To begin, add another Group block.
await editor.insertBlock( {
name: 'core/group',
innerBlocks: [
{ name: 'core/paragraph' },
{ name: 'core/pullquote' },
],
} );
// Click the newly inserted Group block List View item to ensure it is focused.
await listView
.getByRole( 'link', {
name: 'Group',
expanded: false,
} )
.click();
// Move down to group block, expand, and then move to the paragraph block.
await page.keyboard.press( 'ArrowDown' );
await page.keyboard.press( 'ArrowRight' );
await page.keyboard.press( 'ArrowDown' );
await page.keyboard.press( 'ArrowDown' );
await pageUtils.pressKeys( 'primary+c' );
await page.keyboard.press( 'ArrowUp' );
await pageUtils.pressKeys( 'primary+v' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Should be able to copy focused block and paste in the list view via keyboard shortcuts'
)
.toMatchObject( [
{ name: 'core/heading', selected: false, focused: false },
{ name: 'core/file', selected: false, focused: false },
{
name: 'core/group',
selected: true,
innerBlocks: [
{
name: 'core/pullquote',
selected: false,
focused: true,
},
{
name: 'core/pullquote',
selected: false,
focused: false,
},
],
},
] );
// Cut and paste blocks.
await page.keyboard.press( 'ArrowUp' );
await pageUtils.pressKeys( 'primary+x' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Should be able to cut a block in the list view, with the preceding block being selected'
)
.toMatchObject( [
{ name: 'core/heading', selected: false, focused: false },
{ name: 'core/file', selected: true, focused: true },
] );
await pageUtils.pressKeys( 'primary+v' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Should be able to paste previously cut block in the list view via keyboard shortcuts'
)
.toMatchObject( [
{ name: 'core/heading', selected: false, focused: false },
{
name: 'core/group',
selected: true,
focused: true,
innerBlocks: [
{
name: 'core/pullquote',
selected: false,
focused: false,
},
{
name: 'core/pullquote',
selected: false,
focused: false,
},
],
},
] );
} );
test( 'block settings dropdown menu', async ( {
editor,
page,
pageUtils,
listViewUtils,
} ) => {
// Insert some blocks of different types.
await editor.insertBlock( { name: 'core/heading' } );
await editor.insertBlock( { name: 'core/file' } );
// Open List View.
const listView = await listViewUtils.openListView();
await listView
.getByRole( 'button', { name: 'Options for Heading' } )
.click();
await page
.getByRole( 'menu', { name: 'Options for Heading' } )
.getByRole( 'menuitem', { name: 'Duplicate' } )
.click();
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Should duplicate a block and move focus'
)
.toMatchObject( [
{ name: 'core/heading', selected: false },
{ name: 'core/heading', selected: false, focused: true },
{ name: 'core/file', selected: true },
] );
await page.keyboard.press( 'Shift+ArrowUp' );
await listView
.getByRole( 'button', { name: 'Options for Heading' } )
.first()
.click();
await page
.getByRole( 'menu', { name: 'Options for Heading' } )
.getByRole( 'menuitem', { name: 'Delete' } )
.click();
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Should delete multiple selected blocks using the dropdown menu'
)
.toMatchObject( [
{ name: 'core/file', selected: true, focused: true },
] );
await page.keyboard.press( 'ArrowRight' );
const optionsForFileToggle = listView
.getByRole( 'row' )
.filter( {
has: page.getByRole( 'gridcell', { name: 'File' } ),
} )
.getByRole( 'button', { name: 'Options for File' } );
const optionsForFileMenu = page.getByRole( 'menu', {
name: 'Options for File',
} );
await expect(
optionsForFileToggle,
'Pressing arrow right should move focus to the menu dropdown toggle button'
).toBeFocused();
await page.keyboard.press( 'Enter' );
await expect(
optionsForFileMenu,
'Pressing Enter should open the menu dropdown'
).toBeVisible();
await page.keyboard.press( 'Escape' );
await expect(
optionsForFileMenu,
'Pressing Escape should close the menu dropdown'
).toBeHidden();
await expect(
optionsForFileToggle,
'Should move focus back to the toggle button'
).toBeFocused();
await page.keyboard.press( 'Space' );
await expect(
optionsForFileMenu,
'Pressing Space should also open the menu dropdown'
).toBeVisible();
await pageUtils.pressKeys( 'primaryAlt+t' ); // Keyboard shortcut for Insert before.
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Pressing keyboard shortcut should also work when the menu is opened and focused'
)
.toMatchObject( [
{ name: 'core/paragraph', selected: true, focused: false },