@@ -6,22 +6,28 @@ import {
6
6
ViewChildren ,
7
7
QueryList ,
8
8
AfterViewInit ,
9
+ Provider ,
10
+ ViewEncapsulation ,
9
11
} from '@angular/core' ;
10
12
import { TestBed , ComponentFixture , fakeAsync , flush } from '@angular/core/testing' ;
11
13
import { DragDropModule } from './drag-drop-module' ;
12
14
import { dispatchMouseEvent , dispatchTouchEvent } from '@angular/cdk/testing' ;
15
+ import { Directionality } from '@angular/cdk/bidi' ;
13
16
import { CdkDrag } from './drag' ;
14
17
import { CdkDragDrop } from './drag-events' ;
15
18
import { moveItemInArray , transferArrayItem } from './drag-utils' ;
16
19
import { CdkDrop } from './drop' ;
17
20
18
21
const ITEM_HEIGHT = 25 ;
22
+ const ITEM_WIDTH = 75 ;
19
23
20
24
describe ( 'CdkDrag' , ( ) => {
21
- function createComponent < T > ( componentType : Type < T > ) : ComponentFixture < T > {
25
+ function createComponent < T > ( componentType : Type < T > , providers : Provider [ ] = [ ] ) :
26
+ ComponentFixture < T > {
22
27
TestBed . configureTestingModule ( {
23
28
imports : [ DragDropModule ] ,
24
29
declarations : [ componentType ] ,
30
+ providers,
25
31
} ) . compileComponents ( ) ;
26
32
27
33
return TestBed . createComponent < T > ( componentType ) ;
@@ -171,9 +177,13 @@ describe('CdkDrag', () => {
171
177
dispatchMouseEvent ( fixture . componentInstance . dragElement . nativeElement , 'mousedown' ) ;
172
178
fixture . detectChanges ( ) ;
173
179
174
- expect ( fixture . componentInstance . startedSpy ) . toHaveBeenCalledWith ( jasmine . objectContaining ( {
175
- source : fixture . componentInstance . dragInstance
176
- } ) ) ;
180
+ expect ( fixture . componentInstance . startedSpy ) . toHaveBeenCalled ( ) ;
181
+
182
+ const event = fixture . componentInstance . startedSpy . calls . mostRecent ( ) . args [ 0 ] ;
183
+
184
+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
185
+ // go into an infinite loop trying to stringify the event, if the test fails.
186
+ expect ( event ) . toEqual ( { source : fixture . componentInstance . dragInstance } ) ;
177
187
} ) ) ;
178
188
179
189
it ( 'should dispatch an event when the user has stopped dragging' , fakeAsync ( ( ) => {
@@ -182,9 +192,13 @@ describe('CdkDrag', () => {
182
192
183
193
dragElementViaMouse ( fixture , fixture . componentInstance . dragElement . nativeElement , 5 , 10 ) ;
184
194
185
- expect ( fixture . componentInstance . endedSpy ) . toHaveBeenCalledWith ( jasmine . objectContaining ( {
186
- source : fixture . componentInstance . dragInstance
187
- } ) ) ;
195
+ expect ( fixture . componentInstance . endedSpy ) . toHaveBeenCalled ( ) ;
196
+
197
+ const event = fixture . componentInstance . endedSpy . calls . mostRecent ( ) . args [ 0 ] ;
198
+
199
+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
200
+ // go into an infinite loop trying to stringify the event, if the test fails.
201
+ expect ( event ) . toEqual ( { source : fixture . componentInstance . dragInstance } ) ;
188
202
} ) ) ;
189
203
} ) ;
190
204
@@ -257,13 +271,52 @@ describe('CdkDrag', () => {
257
271
fixture . detectChanges ( ) ;
258
272
259
273
expect ( fixture . componentInstance . droppedSpy ) . toHaveBeenCalledTimes ( 1 ) ;
260
- expect ( fixture . componentInstance . droppedSpy ) . toHaveBeenCalledWith ( jasmine . objectContaining ( {
274
+
275
+ const event = fixture . componentInstance . droppedSpy . calls . mostRecent ( ) . args [ 0 ] ;
276
+
277
+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
278
+ // go into an infinite loop trying to stringify the event, if the test fails.
279
+ expect ( event ) . toEqual ( {
261
280
previousIndex : 0 ,
262
281
currentIndex : 2 ,
263
282
item : firstItem ,
264
283
container : fixture . componentInstance . dropInstance ,
265
284
previousContainer : fixture . componentInstance . dropInstance
266
- } ) ) ;
285
+ } ) ;
286
+
287
+ expect ( dragItems . map ( drag => drag . element . nativeElement . textContent ! . trim ( ) ) )
288
+ . toEqual ( [ 'One' , 'Two' , 'Zero' , 'Three' ] ) ;
289
+ } ) ) ;
290
+
291
+ it ( 'should dispatch the `dropped` event in a horizontal drop zone' , fakeAsync ( ( ) => {
292
+ const fixture = createComponent ( DraggableInHorizontalDropZone ) ;
293
+ fixture . detectChanges ( ) ;
294
+ const dragItems = fixture . componentInstance . dragItems ;
295
+
296
+ expect ( dragItems . map ( drag => drag . element . nativeElement . textContent ! . trim ( ) ) )
297
+ . toEqual ( [ 'Zero' , 'One' , 'Two' , 'Three' ] ) ;
298
+
299
+ const firstItem = dragItems . first ;
300
+ const thirdItemRect = dragItems . toArray ( ) [ 2 ] . element . nativeElement . getBoundingClientRect ( ) ;
301
+
302
+ dragElementViaMouse ( fixture , firstItem . element . nativeElement ,
303
+ thirdItemRect . left + 1 , thirdItemRect . top + 1 ) ;
304
+ flush ( ) ;
305
+ fixture . detectChanges ( ) ;
306
+
307
+ expect ( fixture . componentInstance . droppedSpy ) . toHaveBeenCalledTimes ( 1 ) ;
308
+
309
+ const event = fixture . componentInstance . droppedSpy . calls . mostRecent ( ) . args [ 0 ] ;
310
+
311
+ // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
312
+ // go into an infinite loop trying to stringify the event, if the test fails.
313
+ expect ( event ) . toEqual ( {
314
+ previousIndex : 0 ,
315
+ currentIndex : 2 ,
316
+ item : firstItem ,
317
+ container : fixture . componentInstance . dropInstance ,
318
+ previousContainer : fixture . componentInstance . dropInstance
319
+ } ) ;
267
320
268
321
expect ( dragItems . map ( drag => drag . element . nativeElement . textContent ! . trim ( ) ) )
269
322
. toEqual ( [ 'One' , 'Two' , 'Zero' , 'Three' ] ) ;
@@ -287,6 +340,8 @@ describe('CdkDrag', () => {
287
340
expect ( preview ) . toBeTruthy ( 'Expected preview to be in the DOM' ) ;
288
341
expect ( preview . textContent ! . trim ( ) )
289
342
. toContain ( 'One' , 'Expected preview content to match element' ) ;
343
+ expect ( preview . getAttribute ( 'dir' ) )
344
+ . toBe ( 'ltr' , 'Expected preview element to inherit the directionality.' ) ;
290
345
expect ( previewRect . width ) . toBe ( itemRect . width , 'Expected preview width to match element' ) ;
291
346
expect ( previewRect . height ) . toBe ( itemRect . height , 'Expected preview height to match element' ) ;
292
347
@@ -300,6 +355,22 @@ describe('CdkDrag', () => {
300
355
expect ( preview . parentNode ) . toBeFalsy ( 'Expected preview to be removed from the DOM' ) ;
301
356
} ) ) ;
302
357
358
+ it ( 'should pass the proper direction to the preview in rtl' , fakeAsync ( ( ) => {
359
+ const fixture = createComponent ( DraggableInDropZone , [ {
360
+ provide : Directionality ,
361
+ useValue : ( { value : 'rtl' } )
362
+ } ] ) ;
363
+
364
+ fixture . detectChanges ( ) ;
365
+
366
+ const item = fixture . componentInstance . dragItems . toArray ( ) [ 1 ] . element . nativeElement ;
367
+ dispatchMouseEvent ( item , 'mousedown' ) ;
368
+ fixture . detectChanges ( ) ;
369
+
370
+ expect ( document . querySelector ( '.cdk-drag-preview' ) ! . getAttribute ( 'dir' ) )
371
+ . toBe ( 'rtl' , 'Expected preview element to inherit the directionality.' ) ;
372
+ } ) ) ;
373
+
303
374
it ( 'should create a placeholder element while the item is dragged' , fakeAsync ( ( ) => {
304
375
const fixture = createComponent ( DraggableInDropZone ) ;
305
376
fixture . detectChanges ( ) ;
@@ -358,6 +429,62 @@ describe('CdkDrag', () => {
358
429
cleanup ( ) ;
359
430
} ) ) ;
360
431
432
+ it ( 'should move the placeholder as an item is being sorted to the right' , fakeAsync ( ( ) => {
433
+ const fixture = createComponent ( DraggableInHorizontalDropZone ) ;
434
+ fixture . detectChanges ( ) ;
435
+
436
+ const items = fixture . componentInstance . dragItems . toArray ( ) ;
437
+ const draggedItem = items [ 0 ] . element . nativeElement ;
438
+ const { top, left} = draggedItem . getBoundingClientRect ( ) ;
439
+
440
+ dispatchMouseEvent ( draggedItem , 'mousedown' , left , top ) ;
441
+ fixture . detectChanges ( ) ;
442
+
443
+ const placeholder = document . querySelector ( '.cdk-drag-placeholder' ) ! as HTMLElement ;
444
+
445
+ // Drag over each item one-by-one going to the right.
446
+ for ( let i = 0 ; i < items . length ; i ++ ) {
447
+ const elementRect = items [ i ] . element . nativeElement . getBoundingClientRect ( ) ;
448
+
449
+ // Add a few pixels to the left offset so we get some overlap.
450
+ dispatchMouseEvent ( document , 'mousemove' , elementRect . left + 5 , elementRect . top ) ;
451
+ fixture . detectChanges ( ) ;
452
+ expect ( getElementIndex ( placeholder ) ) . toBe ( i ) ;
453
+ }
454
+
455
+ dispatchMouseEvent ( document , 'mouseup' ) ;
456
+ fixture . detectChanges ( ) ;
457
+ flush ( ) ;
458
+ } ) ) ;
459
+
460
+ it ( 'should move the placeholder as an item is being sorted to the left' , fakeAsync ( ( ) => {
461
+ const fixture = createComponent ( DraggableInHorizontalDropZone ) ;
462
+ fixture . detectChanges ( ) ;
463
+
464
+ const items = fixture . componentInstance . dragItems . toArray ( ) ;
465
+ const draggedItem = items [ items . length - 1 ] . element . nativeElement ;
466
+ const { top, left} = draggedItem . getBoundingClientRect ( ) ;
467
+
468
+ dispatchMouseEvent ( draggedItem , 'mousedown' , left , top ) ;
469
+ fixture . detectChanges ( ) ;
470
+
471
+ const placeholder = document . querySelector ( '.cdk-drag-placeholder' ) ! as HTMLElement ;
472
+
473
+ // Drag over each item one-by-one going to the left.
474
+ for ( let i = items . length - 1 ; i > - 1 ; i -- ) {
475
+ const elementRect = items [ i ] . element . nativeElement . getBoundingClientRect ( ) ;
476
+
477
+ // Remove a few pixels from the right offset so we get some overlap.
478
+ dispatchMouseEvent ( document , 'mousemove' , elementRect . right - 5 , elementRect . top ) ;
479
+ fixture . detectChanges ( ) ;
480
+ expect ( getElementIndex ( placeholder ) ) . toBe ( Math . min ( i + 1 , items . length - 1 ) ) ;
481
+ }
482
+
483
+ dispatchMouseEvent ( document , 'mouseup' ) ;
484
+ fixture . detectChanges ( ) ;
485
+ flush ( ) ;
486
+ } ) ) ;
487
+
361
488
it ( 'should clean up the preview element if the item is destroyed mid-drag' , fakeAsync ( ( ) => {
362
489
const fixture = createComponent ( DraggableInDropZone ) ;
363
490
fixture . detectChanges ( ) ;
@@ -490,6 +617,43 @@ export class DraggableInDropZone {
490
617
}
491
618
492
619
620
+ @Component ( {
621
+ encapsulation : ViewEncapsulation . None ,
622
+ styles : [
623
+ // Use inline blocks here to avoid flexbox issues and not to have to flip floats in rtl.
624
+ `
625
+ .cdk-drop {
626
+ display: block;
627
+ width: 300px;
628
+ background: pink;
629
+ font-size: 0;
630
+ }
631
+
632
+ .cdk-drag {
633
+ width: ${ ITEM_WIDTH } px;
634
+ height: ${ ITEM_HEIGHT } px;
635
+ background: red;
636
+ display: inline-block;
637
+ }
638
+ ` ] ,
639
+ template : `
640
+ <cdk-drop
641
+ orientation="horizontal"
642
+ [data]="items"
643
+ (dropped)="droppedSpy($event)">
644
+ <div *ngFor="let item of items" cdkDrag>{{item}}</div>
645
+ </cdk-drop>
646
+ `
647
+ } )
648
+ export class DraggableInHorizontalDropZone {
649
+ @ViewChildren ( CdkDrag ) dragItems : QueryList < CdkDrag > ;
650
+ @ViewChild ( CdkDrop ) dropInstance : CdkDrop ;
651
+ items = [ 'Zero' , 'One' , 'Two' , 'Three' ] ;
652
+ droppedSpy = jasmine . createSpy ( 'dropped spy' ) . and . callFake ( ( event : CdkDragDrop < string [ ] > ) => {
653
+ moveItemInArray ( this . items , event . previousIndex , event . currentIndex ) ;
654
+ } ) ;
655
+ }
656
+
493
657
@Component ( {
494
658
template : `
495
659
<cdk-drop style="display: block; width: 100px; background: pink;">
0 commit comments