@@ -46,13 +46,15 @@ describe('Listbox Pattern', () => {
46
46
47
47
function getOptions ( listbox : TestListbox , values : string [ ] ) : TestOption [ ] {
48
48
return values . map ( ( value , index ) => {
49
+ const element = document . createElement ( 'div' ) ;
50
+ element . role = 'option' ;
49
51
return new OptionPattern ( {
50
52
value : signal ( value ) ,
51
53
id : signal ( `option-${ index } ` ) ,
52
54
disabled : signal ( false ) ,
53
55
searchTerm : signal ( value ) ,
54
56
listbox : signal ( listbox ) ,
55
- element : signal ( { focus : ( ) => { } } as HTMLElement ) ,
57
+ element : signal ( element ) ,
56
58
} ) ;
57
59
} ) ;
58
60
}
@@ -439,4 +441,158 @@ describe('Listbox Pattern', () => {
439
441
} ) ;
440
442
} ) ;
441
443
} ) ;
444
+
445
+ describe ( 'Pointer Events' , ( ) => {
446
+ function click ( options : WritableSignal < TestOption [ ] > , index : number , mods ?: ModifierKeys ) {
447
+ return {
448
+ target : options ( ) [ index ] . element ( ) ,
449
+ shiftKey : mods ?. shift ,
450
+ ctrlKey : mods ?. control ,
451
+ } as unknown as PointerEvent ;
452
+ }
453
+
454
+ describe ( 'follows focus & single select' , ( ) => {
455
+ it ( 'should select a single option on click' , ( ) => {
456
+ const { listbox, options} = getDefaultPatterns ( {
457
+ multi : signal ( false ) ,
458
+ selectionMode : signal ( 'follow' ) ,
459
+ } ) ;
460
+ listbox . onPointerdown ( click ( options , 0 ) ) ;
461
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Apple' ] ) ;
462
+ } ) ;
463
+ } ) ;
464
+
465
+ describe ( 'explicit focus & single select' , ( ) => {
466
+ it ( 'should select an unselected option on click' , ( ) => {
467
+ const { listbox, options} = getDefaultPatterns ( {
468
+ multi : signal ( false ) ,
469
+ selectionMode : signal ( 'explicit' ) ,
470
+ } ) ;
471
+ listbox . onPointerdown ( click ( options , 0 ) ) ;
472
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Apple' ] ) ;
473
+ } ) ;
474
+
475
+ it ( 'should deselect a selected option on click' , ( ) => {
476
+ const { listbox, options} = getDefaultPatterns ( {
477
+ multi : signal ( false ) ,
478
+ value : signal ( [ 'Apple' ] ) ,
479
+ selectionMode : signal ( 'explicit' ) ,
480
+ } ) ;
481
+ listbox . onPointerdown ( click ( options , 0 ) ) ;
482
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ ] ) ;
483
+ } ) ;
484
+ } ) ;
485
+
486
+ describe ( 'explicit focus & multi select' , ( ) => {
487
+ it ( 'should select an unselected option on click' , ( ) => {
488
+ const { listbox, options} = getDefaultPatterns ( {
489
+ multi : signal ( true ) ,
490
+ selectionMode : signal ( 'explicit' ) ,
491
+ } ) ;
492
+ listbox . onPointerdown ( click ( options , 0 ) ) ;
493
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Apple' ] ) ;
494
+ } ) ;
495
+
496
+ it ( 'should deselect a selected option on click' , ( ) => {
497
+ const { listbox, options} = getDefaultPatterns ( {
498
+ multi : signal ( true ) ,
499
+ value : signal ( [ 'Apple' ] ) ,
500
+ selectionMode : signal ( 'explicit' ) ,
501
+ } ) ;
502
+ listbox . onPointerdown ( click ( options , 0 ) ) ;
503
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ ] ) ;
504
+ } ) ;
505
+
506
+ it ( 'should select options from anchor on shift + click' , ( ) => {
507
+ const { listbox, options} = getDefaultPatterns ( {
508
+ multi : signal ( true ) ,
509
+ selectionMode : signal ( 'explicit' ) ,
510
+ } ) ;
511
+ listbox . onPointerdown ( click ( options , 2 ) ) ;
512
+ listbox . onPointerdown ( click ( options , 5 , { shift : true } ) ) ;
513
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Banana' , 'Blackberry' , 'Blueberry' , 'Cantaloupe' ] ) ;
514
+ } ) ;
515
+
516
+ it ( 'should deselect options from anchor on shift + click' , ( ) => {
517
+ const { listbox, options} = getDefaultPatterns ( {
518
+ multi : signal ( true ) ,
519
+ selectionMode : signal ( 'explicit' ) ,
520
+ } ) ;
521
+ listbox . onPointerdown ( click ( options , 2 ) ) ;
522
+ listbox . onPointerdown ( click ( options , 5 ) ) ;
523
+ listbox . onPointerdown ( click ( options , 2 , { shift : true } ) ) ;
524
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ ] ) ;
525
+ } ) ;
526
+ } ) ;
527
+
528
+ describe ( 'follows focus & multi select' , ( ) => {
529
+ it ( 'should select a single option on click' , ( ) => {
530
+ const { listbox, options} = getDefaultPatterns ( {
531
+ multi : signal ( true ) ,
532
+ selectionMode : signal ( 'follow' ) ,
533
+ } ) ;
534
+ listbox . onPointerdown ( click ( options , 0 ) ) ;
535
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Apple' ] ) ;
536
+ listbox . onPointerdown ( click ( options , 1 ) ) ;
537
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Apricot' ] ) ;
538
+ listbox . onPointerdown ( click ( options , 2 ) ) ;
539
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Banana' ] ) ;
540
+ } ) ;
541
+
542
+ it ( 'should select an unselected option on ctrl + click' , ( ) => {
543
+ const { listbox, options} = getDefaultPatterns ( {
544
+ multi : signal ( true ) ,
545
+ selectionMode : signal ( 'follow' ) ,
546
+ } ) ;
547
+ listbox . onPointerdown ( click ( options , 0 ) ) ;
548
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Apple' ] ) ;
549
+ listbox . onPointerdown ( click ( options , 1 , { control : true } ) ) ;
550
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Apple' , 'Apricot' ] ) ;
551
+ listbox . onPointerdown ( click ( options , 2 , { control : true } ) ) ;
552
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Apple' , 'Apricot' , 'Banana' ] ) ;
553
+ } ) ;
554
+
555
+ it ( 'should deselect a selected option on ctrl + click' , ( ) => {
556
+ const { listbox, options} = getDefaultPatterns ( {
557
+ multi : signal ( true ) ,
558
+ selectionMode : signal ( 'follow' ) ,
559
+ } ) ;
560
+ listbox . onPointerdown ( click ( options , 0 ) ) ;
561
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Apple' ] ) ;
562
+ listbox . onPointerdown ( click ( options , 0 , { control : true } ) ) ;
563
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ ] ) ;
564
+ } ) ;
565
+
566
+ it ( 'should select options from anchor on shift + click' , ( ) => {
567
+ const { listbox, options} = getDefaultPatterns ( {
568
+ multi : signal ( true ) ,
569
+ selectionMode : signal ( 'follow' ) ,
570
+ } ) ;
571
+ listbox . onPointerdown ( click ( options , 2 ) ) ;
572
+ listbox . onPointerdown ( click ( options , 5 , { shift : true } ) ) ;
573
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ 'Banana' , 'Blackberry' , 'Blueberry' , 'Cantaloupe' ] ) ;
574
+ } ) ;
575
+
576
+ it ( 'should deselect options from anchor on shift + click' , ( ) => {
577
+ const { listbox, options} = getDefaultPatterns ( {
578
+ multi : signal ( true ) ,
579
+ selectionMode : signal ( 'follow' ) ,
580
+ } ) ;
581
+ listbox . onPointerdown ( click ( options , 2 ) ) ;
582
+ listbox . onPointerdown ( click ( options , 5 , { control : true } ) ) ;
583
+ listbox . onPointerdown ( click ( options , 2 , { shift : true } ) ) ;
584
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ ] ) ;
585
+ } ) ;
586
+ } ) ;
587
+
588
+ it ( 'should only navigate when readonly' , ( ) => {
589
+ const { listbox, options} = getDefaultPatterns ( { readonly : signal ( true ) } ) ;
590
+ listbox . onPointerdown ( click ( options , 0 ) ) ;
591
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ ] ) ;
592
+ listbox . onPointerdown ( click ( options , 1 ) ) ;
593
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ ] ) ;
594
+ listbox . onPointerdown ( click ( options , 2 ) ) ;
595
+ expect ( listbox . inputs . value ( ) ) . toEqual ( [ ] ) ;
596
+ } ) ;
597
+ } ) ;
442
598
} ) ;
0 commit comments