@@ -658,6 +658,87 @@ describe('draggable', () => {
658658 await nextUpdate ( dialog ) ;
659659 expect ( dialog . $ . overlay . hasAttribute ( 'draggable' ) ) . to . be . false ;
660660 } ) ;
661+
662+ describe ( 'keepInViewport' , ( ) => {
663+ // Helper to drag to absolute coordinates within the viewport
664+ function dragTo ( target , toX , toY ) {
665+ const targetBounds = target . getBoundingClientRect ( ) ;
666+ const fromXY = {
667+ x : Math . floor ( targetBounds . left + targetBounds . width / 2 ) ,
668+ y : Math . floor ( targetBounds . top + targetBounds . height / 2 ) ,
669+ } ;
670+ const toXY = { x : toX , y : toY } ;
671+ dispatchMouseEvent ( target , 'mousedown' , fromXY ) ;
672+ dispatchMouseEvent ( target , 'mousemove' , fromXY ) ;
673+ dispatchMouseEvent ( target , 'mousemove' , toXY ) ;
674+ dispatchMouseEvent ( target , 'mouseup' , toXY ) ;
675+ }
676+
677+ beforeEach ( async ( ) => {
678+ dialog . keepInViewport = true ;
679+ await nextUpdate ( dialog ) ;
680+ } ) ;
681+
682+ it ( 'should not drag dialog past left viewport edge' , async ( ) => {
683+ dragTo ( container , 0 , bounds . top + bounds . height / 2 ) ;
684+ await nextRender ( ) ;
685+
686+ const draggedBounds = container . getBoundingClientRect ( ) ;
687+ expect ( Math . floor ( draggedBounds . left ) ) . to . be . closeTo ( 0 , 1 ) ;
688+ } ) ;
689+
690+ it ( 'should not drag dialog past top viewport edge' , async ( ) => {
691+ dragTo ( container , bounds . left + bounds . width / 2 , 0 ) ;
692+ await nextRender ( ) ;
693+
694+ const draggedBounds = container . getBoundingClientRect ( ) ;
695+ expect ( Math . floor ( draggedBounds . top ) ) . to . closeTo ( 0 , 1 ) ;
696+ } ) ;
697+
698+ it ( 'should not drag dialog past right viewport edge' , async ( ) => {
699+ dragTo ( container , window . innerWidth , bounds . top + bounds . height / 2 ) ;
700+ await nextRender ( ) ;
701+
702+ const draggedBounds = container . getBoundingClientRect ( ) ;
703+ expect ( Math . floor ( draggedBounds . right ) ) . to . closeTo ( window . innerWidth , 1 ) ;
704+ } ) ;
705+
706+ it ( 'should not drag dialog past bottom viewport edge' , async ( ) => {
707+ dragTo ( container , bounds . left + bounds . width / 2 , window . innerHeight ) ;
708+ await nextRender ( ) ;
709+
710+ const draggedBounds = container . getBoundingClientRect ( ) ;
711+ expect ( Math . floor ( draggedBounds . bottom ) ) . to . closeTo ( window . innerHeight , 1 ) ;
712+ } ) ;
713+
714+ it ( 'should allow normal dragging within viewport' , async ( ) => {
715+ const initialBounds = container . getBoundingClientRect ( ) ;
716+ dx = 50 ;
717+ drag ( container ) ;
718+ await nextRender ( ) ;
719+ const draggedBounds = container . getBoundingClientRect ( ) ;
720+ expect ( Math . floor ( draggedBounds . top ) ) . to . be . eql ( Math . floor ( initialBounds . top + 50 ) ) ;
721+ expect ( Math . floor ( draggedBounds . left ) ) . to . be . eql ( Math . floor ( initialBounds . left + 50 ) ) ;
722+ } ) ;
723+
724+ it ( 'should allow dragging outside of viewport with keepInViewport disabled' , async ( ) => {
725+ dialog . keepInViewport = false ;
726+ await nextUpdate ( dialog ) ;
727+
728+ dragTo ( container , 0 , bounds . top + bounds . height / 2 ) ;
729+ await nextRender ( ) ;
730+
731+ const draggedBounds = container . getBoundingClientRect ( ) ;
732+ expect ( Math . floor ( draggedBounds . left ) ) . to . lessThan ( 0 ) ;
733+ } ) ;
734+
735+ it ( 'should reflect keepInViewport attribute' , async ( ) => {
736+ expect ( dialog . hasAttribute ( 'keep-in-viewport' ) ) . to . be . true ;
737+ dialog . keepInViewport = false ;
738+ await nextUpdate ( dialog ) ;
739+ expect ( dialog . hasAttribute ( 'keep-in-viewport' ) ) . to . be . false ;
740+ } ) ;
741+ } ) ;
661742} ) ;
662743
663744describe ( 'touch' , ( ) => {
0 commit comments