@@ -6,24 +6,29 @@ import {
6
6
ViewChildren ,
7
7
QueryList ,
8
8
AfterViewInit ,
9
+ Provider ,
9
10
ViewEncapsulation ,
10
11
} from '@angular/core' ;
11
12
import { TestBed , ComponentFixture , fakeAsync , flush } from '@angular/core/testing' ;
12
13
import { DragDropModule } from './drag-drop-module' ;
13
14
import { dispatchMouseEvent , dispatchTouchEvent } from '@angular/cdk/testing' ;
15
+ import { Directionality } from '@angular/cdk/bidi' ;
14
16
import { CdkDrag } from './drag' ;
15
17
import { CdkDragDrop } from './drag-events' ;
16
18
import { moveItemInArray , transferArrayItem } from './drag-utils' ;
17
19
import { CdkDrop } from './drop' ;
18
20
import { CdkDragHandle } from './drag-handle' ;
19
21
20
22
const ITEM_HEIGHT = 25 ;
23
+ const ITEM_WIDTH = 75 ;
21
24
22
25
describe ( 'CdkDrag' , ( ) => {
23
- function createComponent < T > ( componentType : Type < T > ) : ComponentFixture < T > {
26
+ function createComponent < T > ( componentType : Type < T > , providers : Provider [ ] = [ ] ) :
27
+ ComponentFixture < T > {
24
28
TestBed . configureTestingModule ( {
25
29
imports : [ DragDropModule ] ,
26
30
declarations : [ componentType ] ,
31
+ providers,
27
32
} ) . compileComponents ( ) ;
28
33
29
34
return TestBed . createComponent < T > ( componentType ) ;
@@ -173,9 +178,13 @@ describe('CdkDrag', () => {
173
178
dispatchMouseEvent ( fixture . componentInstance . dragElement . nativeElement , 'mousedown' ) ;
174
179
fixture . detectChanges ( ) ;
175
180
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 } ) ;
179
188
} ) ) ;
180
189
181
190
it ( 'should dispatch an event when the user has stopped dragging' , fakeAsync ( ( ) => {
@@ -184,9 +193,13 @@ describe('CdkDrag', () => {
184
193
185
194
dragElementViaMouse ( fixture , fixture . componentInstance . dragElement . nativeElement , 5 , 10 ) ;
186
195
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 } ) ;
190
203
} ) ) ;
191
204
} ) ;
192
205
@@ -290,13 +303,52 @@ describe('CdkDrag', () => {
290
303
fixture . detectChanges ( ) ;
291
304
292
305
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 ( {
294
312
previousIndex : 0 ,
295
313
currentIndex : 2 ,
296
314
item : firstItem ,
297
315
container : fixture . componentInstance . dropInstance ,
298
316
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
+ } ) ;
300
352
301
353
expect ( dragItems . map ( drag => drag . element . nativeElement . textContent ! . trim ( ) ) )
302
354
. toEqual ( [ 'One' , 'Two' , 'Zero' , 'Three' ] ) ;
@@ -320,6 +372,8 @@ describe('CdkDrag', () => {
320
372
expect ( preview ) . toBeTruthy ( 'Expected preview to be in the DOM' ) ;
321
373
expect ( preview . textContent ! . trim ( ) )
322
374
. toContain ( 'One' , 'Expected preview content to match element' ) ;
375
+ expect ( preview . getAttribute ( 'dir' ) )
376
+ . toBe ( 'ltr' , 'Expected preview element to inherit the directionality.' ) ;
323
377
expect ( previewRect . width ) . toBe ( itemRect . width , 'Expected preview width to match element' ) ;
324
378
expect ( previewRect . height ) . toBe ( itemRect . height , 'Expected preview height to match element' ) ;
325
379
@@ -333,6 +387,22 @@ describe('CdkDrag', () => {
333
387
expect ( preview . parentNode ) . toBeFalsy ( 'Expected preview to be removed from the DOM' ) ;
334
388
} ) ) ;
335
389
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
+
336
406
it ( 'should create a placeholder element while the item is dragged' , fakeAsync ( ( ) => {
337
407
const fixture = createComponent ( DraggableInDropZone ) ;
338
408
fixture . detectChanges ( ) ;
@@ -391,6 +461,62 @@ describe('CdkDrag', () => {
391
461
cleanup ( ) ;
392
462
} ) ) ;
393
463
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
+
394
520
it ( 'should clean up the preview element if the item is destroyed mid-drag' , fakeAsync ( ( ) => {
395
521
const fixture = createComponent ( DraggableInDropZone ) ;
396
522
fixture . detectChanges ( ) ;
@@ -562,6 +688,43 @@ export class DraggableInDropZone {
562
688
}
563
689
564
690
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
+
565
728
@Component ( {
566
729
template : `
567
730
<cdk-drop style="display: block; width: 100px; background: pink;">
0 commit comments