@@ -6,24 +6,29 @@ import {
66 ViewChildren ,
77 QueryList ,
88 AfterViewInit ,
9+ Provider ,
910 ViewEncapsulation ,
1011} from '@angular/core' ;
1112import { TestBed , ComponentFixture , fakeAsync , flush } from '@angular/core/testing' ;
1213import { DragDropModule } from './drag-drop-module' ;
1314import { dispatchMouseEvent , dispatchTouchEvent } from '@angular/cdk/testing' ;
15+ import { Directionality } from '@angular/cdk/bidi' ;
1416import { CdkDrag } from './drag' ;
1517import { CdkDragDrop } from './drag-events' ;
1618import { moveItemInArray , transferArrayItem } from './drag-utils' ;
1719import { CdkDrop } from './drop' ;
1820import { CdkDragHandle } from './drag-handle' ;
1921
2022const ITEM_HEIGHT = 25 ;
23+ const ITEM_WIDTH = 75 ;
2124
2225describe ( 'CdkDrag' , ( ) => {
23- function createComponent < T > ( componentType : Type < T > ) : ComponentFixture < T > {
26+ function createComponent < T > ( componentType : Type < T > , providers : Provider [ ] = [ ] ) :
27+ ComponentFixture < T > {
2428 TestBed . configureTestingModule ( {
2529 imports : [ DragDropModule ] ,
2630 declarations : [ componentType ] ,
31+ providers,
2732 } ) . compileComponents ( ) ;
2833
2934 return TestBed . createComponent < T > ( componentType ) ;
@@ -173,9 +178,13 @@ describe('CdkDrag', () => {
173178 dispatchMouseEvent ( fixture . componentInstance . dragElement . nativeElement , 'mousedown' ) ;
174179 fixture . detectChanges ( ) ;
175180
176- expect ( fixture . componentInstance . startedSpy ) . toHaveBeenCalledWith ( jasmine . objectContaining ( {
177- source : fixture . componentInstance . dragInstance
178- } ) ) ;
181+ expect ( fixture . componentInstance . startedSpy ) . toHaveBeenCalled ( ) ;
182+
183+ const event = fixture . componentInstance . startedSpy . calls . mostRecent ( ) . args [ 0 ] ;
184+
185+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
186+ // go into an infinite loop trying to stringify the event, if the test fails.
187+ expect ( event ) . toEqual ( { source : fixture . componentInstance . dragInstance } ) ;
179188 } ) ) ;
180189
181190 it ( 'should dispatch an event when the user has stopped dragging' , fakeAsync ( ( ) => {
@@ -184,9 +193,13 @@ describe('CdkDrag', () => {
184193
185194 dragElementViaMouse ( fixture , fixture . componentInstance . dragElement . nativeElement , 5 , 10 ) ;
186195
187- expect ( fixture . componentInstance . endedSpy ) . toHaveBeenCalledWith ( jasmine . objectContaining ( {
188- source : fixture . componentInstance . dragInstance
189- } ) ) ;
196+ expect ( fixture . componentInstance . endedSpy ) . toHaveBeenCalled ( ) ;
197+
198+ const event = fixture . componentInstance . endedSpy . calls . mostRecent ( ) . args [ 0 ] ;
199+
200+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
201+ // go into an infinite loop trying to stringify the event, if the test fails.
202+ expect ( event ) . toEqual ( { source : fixture . componentInstance . dragInstance } ) ;
190203 } ) ) ;
191204 } ) ;
192205
@@ -290,13 +303,52 @@ describe('CdkDrag', () => {
290303 fixture . detectChanges ( ) ;
291304
292305 expect ( fixture . componentInstance . droppedSpy ) . toHaveBeenCalledTimes ( 1 ) ;
293- expect ( fixture . componentInstance . droppedSpy ) . toHaveBeenCalledWith ( jasmine . objectContaining ( {
306+
307+ const event = fixture . componentInstance . droppedSpy . calls . mostRecent ( ) . args [ 0 ] ;
308+
309+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
310+ // go into an infinite loop trying to stringify the event, if the test fails.
311+ expect ( event ) . toEqual ( {
294312 previousIndex : 0 ,
295313 currentIndex : 2 ,
296314 item : firstItem ,
297315 container : fixture . componentInstance . dropInstance ,
298316 previousContainer : fixture . componentInstance . dropInstance
299- } ) ) ;
317+ } ) ;
318+
319+ expect ( dragItems . map ( drag => drag . element . nativeElement . textContent ! . trim ( ) ) )
320+ . toEqual ( [ 'One' , 'Two' , 'Zero' , 'Three' ] ) ;
321+ } ) ) ;
322+
323+ it ( 'should dispatch the `dropped` event in a horizontal drop zone' , fakeAsync ( ( ) => {
324+ const fixture = createComponent ( DraggableInHorizontalDropZone ) ;
325+ fixture . detectChanges ( ) ;
326+ const dragItems = fixture . componentInstance . dragItems ;
327+
328+ expect ( dragItems . map ( drag => drag . element . nativeElement . textContent ! . trim ( ) ) )
329+ . toEqual ( [ 'Zero' , 'One' , 'Two' , 'Three' ] ) ;
330+
331+ const firstItem = dragItems . first ;
332+ const thirdItemRect = dragItems . toArray ( ) [ 2 ] . element . nativeElement . getBoundingClientRect ( ) ;
333+
334+ dragElementViaMouse ( fixture , firstItem . element . nativeElement ,
335+ thirdItemRect . left + 1 , thirdItemRect . top + 1 ) ;
336+ flush ( ) ;
337+ fixture . detectChanges ( ) ;
338+
339+ expect ( fixture . componentInstance . droppedSpy ) . toHaveBeenCalledTimes ( 1 ) ;
340+
341+ const event = fixture . componentInstance . droppedSpy . calls . mostRecent ( ) . args [ 0 ] ;
342+
343+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
344+ // go into an infinite loop trying to stringify the event, if the test fails.
345+ expect ( event ) . toEqual ( {
346+ previousIndex : 0 ,
347+ currentIndex : 2 ,
348+ item : firstItem ,
349+ container : fixture . componentInstance . dropInstance ,
350+ previousContainer : fixture . componentInstance . dropInstance
351+ } ) ;
300352
301353 expect ( dragItems . map ( drag => drag . element . nativeElement . textContent ! . trim ( ) ) )
302354 . toEqual ( [ 'One' , 'Two' , 'Zero' , 'Three' ] ) ;
@@ -320,6 +372,8 @@ describe('CdkDrag', () => {
320372 expect ( preview ) . toBeTruthy ( 'Expected preview to be in the DOM' ) ;
321373 expect ( preview . textContent ! . trim ( ) )
322374 . toContain ( 'One' , 'Expected preview content to match element' ) ;
375+ expect ( preview . getAttribute ( 'dir' ) )
376+ . toBe ( 'ltr' , 'Expected preview element to inherit the directionality.' ) ;
323377 expect ( previewRect . width ) . toBe ( itemRect . width , 'Expected preview width to match element' ) ;
324378 expect ( previewRect . height ) . toBe ( itemRect . height , 'Expected preview height to match element' ) ;
325379
@@ -333,6 +387,22 @@ describe('CdkDrag', () => {
333387 expect ( preview . parentNode ) . toBeFalsy ( 'Expected preview to be removed from the DOM' ) ;
334388 } ) ) ;
335389
390+ it ( 'should pass the proper direction to the preview in rtl' , fakeAsync ( ( ) => {
391+ const fixture = createComponent ( DraggableInDropZone , [ {
392+ provide : Directionality ,
393+ useValue : ( { value : 'rtl' } )
394+ } ] ) ;
395+
396+ fixture . detectChanges ( ) ;
397+
398+ const item = fixture . componentInstance . dragItems . toArray ( ) [ 1 ] . element . nativeElement ;
399+ dispatchMouseEvent ( item , 'mousedown' ) ;
400+ fixture . detectChanges ( ) ;
401+
402+ expect ( document . querySelector ( '.cdk-drag-preview' ) ! . getAttribute ( 'dir' ) )
403+ . toBe ( 'rtl' , 'Expected preview element to inherit the directionality.' ) ;
404+ } ) ) ;
405+
336406 it ( 'should create a placeholder element while the item is dragged' , fakeAsync ( ( ) => {
337407 const fixture = createComponent ( DraggableInDropZone ) ;
338408 fixture . detectChanges ( ) ;
@@ -391,6 +461,62 @@ describe('CdkDrag', () => {
391461 cleanup ( ) ;
392462 } ) ) ;
393463
464+ it ( 'should move the placeholder as an item is being sorted to the right' , fakeAsync ( ( ) => {
465+ const fixture = createComponent ( DraggableInHorizontalDropZone ) ;
466+ fixture . detectChanges ( ) ;
467+
468+ const items = fixture . componentInstance . dragItems . toArray ( ) ;
469+ const draggedItem = items [ 0 ] . element . nativeElement ;
470+ const { top, left} = draggedItem . getBoundingClientRect ( ) ;
471+
472+ dispatchMouseEvent ( draggedItem , 'mousedown' , left , top ) ;
473+ fixture . detectChanges ( ) ;
474+
475+ const placeholder = document . querySelector ( '.cdk-drag-placeholder' ) ! as HTMLElement ;
476+
477+ // Drag over each item one-by-one going to the right.
478+ for ( let i = 0 ; i < items . length ; i ++ ) {
479+ const elementRect = items [ i ] . element . nativeElement . getBoundingClientRect ( ) ;
480+
481+ // Add a few pixels to the left offset so we get some overlap.
482+ dispatchMouseEvent ( document , 'mousemove' , elementRect . left + 5 , elementRect . top ) ;
483+ fixture . detectChanges ( ) ;
484+ expect ( getElementIndex ( placeholder ) ) . toBe ( i ) ;
485+ }
486+
487+ dispatchMouseEvent ( document , 'mouseup' ) ;
488+ fixture . detectChanges ( ) ;
489+ flush ( ) ;
490+ } ) ) ;
491+
492+ it ( 'should move the placeholder as an item is being sorted to the left' , fakeAsync ( ( ) => {
493+ const fixture = createComponent ( DraggableInHorizontalDropZone ) ;
494+ fixture . detectChanges ( ) ;
495+
496+ const items = fixture . componentInstance . dragItems . toArray ( ) ;
497+ const draggedItem = items [ items . length - 1 ] . element . nativeElement ;
498+ const { top, left} = draggedItem . getBoundingClientRect ( ) ;
499+
500+ dispatchMouseEvent ( draggedItem , 'mousedown' , left , top ) ;
501+ fixture . detectChanges ( ) ;
502+
503+ const placeholder = document . querySelector ( '.cdk-drag-placeholder' ) ! as HTMLElement ;
504+
505+ // Drag over each item one-by-one going to the left.
506+ for ( let i = items . length - 1 ; i > - 1 ; i -- ) {
507+ const elementRect = items [ i ] . element . nativeElement . getBoundingClientRect ( ) ;
508+
509+ // Remove a few pixels from the right offset so we get some overlap.
510+ dispatchMouseEvent ( document , 'mousemove' , elementRect . right - 5 , elementRect . top ) ;
511+ fixture . detectChanges ( ) ;
512+ expect ( getElementIndex ( placeholder ) ) . toBe ( Math . min ( i + 1 , items . length - 1 ) ) ;
513+ }
514+
515+ dispatchMouseEvent ( document , 'mouseup' ) ;
516+ fixture . detectChanges ( ) ;
517+ flush ( ) ;
518+ } ) ) ;
519+
394520 it ( 'should clean up the preview element if the item is destroyed mid-drag' , fakeAsync ( ( ) => {
395521 const fixture = createComponent ( DraggableInDropZone ) ;
396522 fixture . detectChanges ( ) ;
@@ -562,6 +688,43 @@ export class DraggableInDropZone {
562688}
563689
564690
691+ @Component ( {
692+ encapsulation : ViewEncapsulation . None ,
693+ styles : [
694+ // Use inline blocks here to avoid flexbox issues and not to have to flip floats in rtl.
695+ `
696+ .cdk-drop {
697+ display: block;
698+ width: 300px;
699+ background: pink;
700+ font-size: 0;
701+ }
702+
703+ .cdk-drag {
704+ width: ${ ITEM_WIDTH } px;
705+ height: ${ ITEM_HEIGHT } px;
706+ background: red;
707+ display: inline-block;
708+ }
709+ ` ] ,
710+ template : `
711+ <cdk-drop
712+ orientation="horizontal"
713+ [data]="items"
714+ (dropped)="droppedSpy($event)">
715+ <div *ngFor="let item of items" cdkDrag>{{item}}</div>
716+ </cdk-drop>
717+ `
718+ } )
719+ export class DraggableInHorizontalDropZone {
720+ @ViewChildren ( CdkDrag ) dragItems : QueryList < CdkDrag > ;
721+ @ViewChild ( CdkDrop ) dropInstance : CdkDrop ;
722+ items = [ 'Zero' , 'One' , 'Two' , 'Three' ] ;
723+ droppedSpy = jasmine . createSpy ( 'dropped spy' ) . and . callFake ( ( event : CdkDragDrop < string [ ] > ) => {
724+ moveItemInArray ( this . items , event . previousIndex , event . currentIndex ) ;
725+ } ) ;
726+ }
727+
565728@Component ( {
566729 template : `
567730 <cdk-drop style="display: block; width: 100px; background: pink;">
0 commit comments