@@ -6,22 +6,28 @@ import {
66 ViewChildren ,
77 QueryList ,
88 AfterViewInit ,
9+ Provider ,
10+ ViewEncapsulation ,
911} from '@angular/core' ;
1012import { TestBed , ComponentFixture , fakeAsync , flush } from '@angular/core/testing' ;
1113import { DragDropModule } from './drag-drop-module' ;
1214import { dispatchMouseEvent , dispatchTouchEvent } from '@angular/cdk/testing' ;
15+ import { Directionality } from '@angular/cdk/bidi' ;
1316import { CdkDrag } from './drag' ;
1417import { CdkDragDrop } from './drag-events' ;
1518import { moveItemInArray , transferArrayItem } from './drag-utils' ;
1619import { CdkDrop } from './drop' ;
1720
1821const ITEM_HEIGHT = 25 ;
22+ const ITEM_WIDTH = 75 ;
1923
2024describe ( 'CdkDrag' , ( ) => {
21- function createComponent < T > ( componentType : Type < T > ) : ComponentFixture < T > {
25+ function createComponent < T > ( componentType : Type < T > , providers : Provider [ ] = [ ] ) :
26+ ComponentFixture < T > {
2227 TestBed . configureTestingModule ( {
2328 imports : [ DragDropModule ] ,
2429 declarations : [ componentType ] ,
30+ providers,
2531 } ) . compileComponents ( ) ;
2632
2733 return TestBed . createComponent < T > ( componentType ) ;
@@ -101,9 +107,13 @@ describe('CdkDrag', () => {
101107 dispatchMouseEvent ( fixture . componentInstance . dragElement . nativeElement , 'mousedown' ) ;
102108 fixture . detectChanges ( ) ;
103109
104- expect ( fixture . componentInstance . startedSpy ) . toHaveBeenCalledWith ( jasmine . objectContaining ( {
105- source : fixture . componentInstance . dragInstance
106- } ) ) ;
110+ expect ( fixture . componentInstance . startedSpy ) . toHaveBeenCalled ( ) ;
111+
112+ const event = fixture . componentInstance . startedSpy . calls . mostRecent ( ) . args [ 0 ] ;
113+
114+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
115+ // go into an infinite loop trying to stringify the event, if the test fails.
116+ expect ( event ) . toEqual ( { source : fixture . componentInstance . dragInstance } ) ;
107117 } ) ) ;
108118
109119 it ( 'should dispatch an event when the user has stopped dragging' , fakeAsync ( ( ) => {
@@ -112,9 +122,13 @@ describe('CdkDrag', () => {
112122
113123 dragElementViaMouse ( fixture , fixture . componentInstance . dragElement . nativeElement , 5 , 10 ) ;
114124
115- expect ( fixture . componentInstance . endedSpy ) . toHaveBeenCalledWith ( jasmine . objectContaining ( {
116- source : fixture . componentInstance . dragInstance
117- } ) ) ;
125+ expect ( fixture . componentInstance . endedSpy ) . toHaveBeenCalled ( ) ;
126+
127+ const event = fixture . componentInstance . endedSpy . calls . mostRecent ( ) . args [ 0 ] ;
128+
129+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
130+ // go into an infinite loop trying to stringify the event, if the test fails.
131+ expect ( event ) . toEqual ( { source : fixture . componentInstance . dragInstance } ) ;
118132 } ) ) ;
119133 } ) ;
120134
@@ -187,13 +201,52 @@ describe('CdkDrag', () => {
187201 fixture . detectChanges ( ) ;
188202
189203 expect ( fixture . componentInstance . droppedSpy ) . toHaveBeenCalledTimes ( 1 ) ;
190- expect ( fixture . componentInstance . droppedSpy ) . toHaveBeenCalledWith ( jasmine . objectContaining ( {
204+
205+ const event = fixture . componentInstance . droppedSpy . calls . mostRecent ( ) . args [ 0 ] ;
206+
207+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
208+ // go into an infinite loop trying to stringify the event, if the test fails.
209+ expect ( event ) . toEqual ( {
191210 previousIndex : 0 ,
192211 currentIndex : 2 ,
193212 item : firstItem ,
194213 container : fixture . componentInstance . dropInstance ,
195214 previousContainer : fixture . componentInstance . dropInstance
196- } ) ) ;
215+ } ) ;
216+
217+ expect ( dragItems . map ( drag => drag . element . nativeElement . textContent ! . trim ( ) ) )
218+ . toEqual ( [ 'One' , 'Two' , 'Zero' , 'Three' ] ) ;
219+ } ) ) ;
220+
221+ it ( 'should dispatch the `dropped` event in a horizontal drop zone' , fakeAsync ( ( ) => {
222+ const fixture = createComponent ( DraggableInHorizontalDropZone ) ;
223+ fixture . detectChanges ( ) ;
224+ const dragItems = fixture . componentInstance . dragItems ;
225+
226+ expect ( dragItems . map ( drag => drag . element . nativeElement . textContent ! . trim ( ) ) )
227+ . toEqual ( [ 'Zero' , 'One' , 'Two' , 'Three' ] ) ;
228+
229+ const firstItem = dragItems . first ;
230+ const thirdItemRect = dragItems . toArray ( ) [ 2 ] . element . nativeElement . getBoundingClientRect ( ) ;
231+
232+ dragElementViaMouse ( fixture , firstItem . element . nativeElement ,
233+ thirdItemRect . left + 1 , thirdItemRect . top + 1 ) ;
234+ flush ( ) ;
235+ fixture . detectChanges ( ) ;
236+
237+ expect ( fixture . componentInstance . droppedSpy ) . toHaveBeenCalledTimes ( 1 ) ;
238+
239+ const event = fixture . componentInstance . droppedSpy . calls . mostRecent ( ) . args [ 0 ] ;
240+
241+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
242+ // go into an infinite loop trying to stringify the event, if the test fails.
243+ expect ( event ) . toEqual ( {
244+ previousIndex : 0 ,
245+ currentIndex : 2 ,
246+ item : firstItem ,
247+ container : fixture . componentInstance . dropInstance ,
248+ previousContainer : fixture . componentInstance . dropInstance
249+ } ) ;
197250
198251 expect ( dragItems . map ( drag => drag . element . nativeElement . textContent ! . trim ( ) ) )
199252 . toEqual ( [ 'One' , 'Two' , 'Zero' , 'Three' ] ) ;
@@ -217,6 +270,8 @@ describe('CdkDrag', () => {
217270 expect ( preview ) . toBeTruthy ( 'Expected preview to be in the DOM' ) ;
218271 expect ( preview . textContent ! . trim ( ) )
219272 . toContain ( 'One' , 'Expected preview content to match element' ) ;
273+ expect ( preview . getAttribute ( 'dir' ) )
274+ . toBe ( 'ltr' , 'Expected preview element to inherit the directionality.' ) ;
220275 expect ( previewRect . width ) . toBe ( itemRect . width , 'Expected preview width to match element' ) ;
221276 expect ( previewRect . height ) . toBe ( itemRect . height , 'Expected preview height to match element' ) ;
222277
@@ -230,6 +285,22 @@ describe('CdkDrag', () => {
230285 expect ( preview . parentNode ) . toBeFalsy ( 'Expected preview to be removed from the DOM' ) ;
231286 } ) ) ;
232287
288+ it ( 'should pass the proper direction to the preview in rtl' , fakeAsync ( ( ) => {
289+ const fixture = createComponent ( DraggableInDropZone , [ {
290+ provide : Directionality ,
291+ useValue : ( { value : 'rtl' } )
292+ } ] ) ;
293+
294+ fixture . detectChanges ( ) ;
295+
296+ const item = fixture . componentInstance . dragItems . toArray ( ) [ 1 ] . element . nativeElement ;
297+ dispatchMouseEvent ( item , 'mousedown' ) ;
298+ fixture . detectChanges ( ) ;
299+
300+ expect ( document . querySelector ( '.cdk-drag-preview' ) ! . getAttribute ( 'dir' ) )
301+ . toBe ( 'rtl' , 'Expected preview element to inherit the directionality.' ) ;
302+ } ) ) ;
303+
233304 it ( 'should create a placeholder element while the item is dragged' , fakeAsync ( ( ) => {
234305 const fixture = createComponent ( DraggableInDropZone ) ;
235306 fixture . detectChanges ( ) ;
@@ -310,6 +381,62 @@ describe('CdkDrag', () => {
310381 flush ( ) ;
311382 } ) ) ;
312383
384+ it ( 'should move the placeholder as an item is being sorted to the right' , fakeAsync ( ( ) => {
385+ const fixture = createComponent ( DraggableInHorizontalDropZone ) ;
386+ fixture . detectChanges ( ) ;
387+
388+ const items = fixture . componentInstance . dragItems . toArray ( ) ;
389+ const draggedItem = items [ 0 ] . element . nativeElement ;
390+ const { top, left} = draggedItem . getBoundingClientRect ( ) ;
391+
392+ dispatchMouseEvent ( draggedItem , 'mousedown' , left , top ) ;
393+ fixture . detectChanges ( ) ;
394+
395+ const placeholder = document . querySelector ( '.cdk-drag-placeholder' ) ! as HTMLElement ;
396+
397+ // Drag over each item one-by-one going to the right.
398+ for ( let i = 0 ; i < items . length ; i ++ ) {
399+ const elementRect = items [ i ] . element . nativeElement . getBoundingClientRect ( ) ;
400+
401+ // Add a few pixels to the left offset so we get some overlap.
402+ dispatchMouseEvent ( document , 'mousemove' , elementRect . left + 5 , elementRect . top ) ;
403+ fixture . detectChanges ( ) ;
404+ expect ( getElementIndex ( placeholder ) ) . toBe ( i ) ;
405+ }
406+
407+ dispatchMouseEvent ( document , 'mouseup' ) ;
408+ fixture . detectChanges ( ) ;
409+ flush ( ) ;
410+ } ) ) ;
411+
412+ it ( 'should move the placeholder as an item is being sorted to the left' , fakeAsync ( ( ) => {
413+ const fixture = createComponent ( DraggableInHorizontalDropZone ) ;
414+ fixture . detectChanges ( ) ;
415+
416+ const items = fixture . componentInstance . dragItems . toArray ( ) ;
417+ const draggedItem = items [ items . length - 1 ] . element . nativeElement ;
418+ const { top, left} = draggedItem . getBoundingClientRect ( ) ;
419+
420+ dispatchMouseEvent ( draggedItem , 'mousedown' , left , top ) ;
421+ fixture . detectChanges ( ) ;
422+
423+ const placeholder = document . querySelector ( '.cdk-drag-placeholder' ) ! as HTMLElement ;
424+
425+ // Drag over each item one-by-one going to the left.
426+ for ( let i = items . length - 1 ; i > - 1 ; i -- ) {
427+ const elementRect = items [ i ] . element . nativeElement . getBoundingClientRect ( ) ;
428+
429+ // Remove a few pixels from the right offset so we get some overlap.
430+ dispatchMouseEvent ( document , 'mousemove' , elementRect . right - 5 , elementRect . top ) ;
431+ fixture . detectChanges ( ) ;
432+ expect ( getElementIndex ( placeholder ) ) . toBe ( Math . min ( i + 1 , items . length - 1 ) ) ;
433+ }
434+
435+ dispatchMouseEvent ( document , 'mouseup' ) ;
436+ fixture . detectChanges ( ) ;
437+ flush ( ) ;
438+ } ) ) ;
439+
313440 it ( 'should clean up the preview element if the item is destroyed mid-drag' , fakeAsync ( ( ) => {
314441 const fixture = createComponent ( DraggableInDropZone ) ;
315442 fixture . detectChanges ( ) ;
@@ -442,6 +569,43 @@ export class DraggableInDropZone {
442569}
443570
444571
572+ @Component ( {
573+ encapsulation : ViewEncapsulation . None ,
574+ styles : [
575+ // Use inline blocks here to avoid flexbox issues and not to have to flip floats in rtl.
576+ `
577+ .cdk-drop {
578+ display: block;
579+ width: 300px;
580+ background: pink;
581+ font-size: 0;
582+ }
583+
584+ .cdk-drag {
585+ width: ${ ITEM_WIDTH } px;
586+ height: ${ ITEM_HEIGHT } px;
587+ background: red;
588+ display: inline-block;
589+ }
590+ ` ] ,
591+ template : `
592+ <cdk-drop
593+ orientation="horizontal"
594+ [data]="items"
595+ (dropped)="droppedSpy($event)">
596+ <div *ngFor="let item of items" cdkDrag>{{item}}</div>
597+ </cdk-drop>
598+ `
599+ } )
600+ export class DraggableInHorizontalDropZone {
601+ @ViewChildren ( CdkDrag ) dragItems : QueryList < CdkDrag > ;
602+ @ViewChild ( CdkDrop ) dropInstance : CdkDrop ;
603+ items = [ 'Zero' , 'One' , 'Two' , 'Three' ] ;
604+ droppedSpy = jasmine . createSpy ( 'dropped spy' ) . and . callFake ( ( event : CdkDragDrop < string [ ] > ) => {
605+ moveItemInArray ( this . items , event . previousIndex , event . currentIndex ) ;
606+ } ) ;
607+ }
608+
445609@Component ( {
446610 template : `
447611 <cdk-drop style="display: block; width: 100px; background: pink;">
0 commit comments