@@ -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 ) ;
@@ -101,9 +107,13 @@ describe('CdkDrag', () => {
101
107
dispatchMouseEvent ( fixture . componentInstance . dragElement . nativeElement , 'mousedown' ) ;
102
108
fixture . detectChanges ( ) ;
103
109
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 } ) ;
107
117
} ) ) ;
108
118
109
119
it ( 'should dispatch an event when the user has stopped dragging' , fakeAsync ( ( ) => {
@@ -112,9 +122,13 @@ describe('CdkDrag', () => {
112
122
113
123
dragElementViaMouse ( fixture , fixture . componentInstance . dragElement . nativeElement , 5 , 10 ) ;
114
124
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 } ) ;
118
132
} ) ) ;
119
133
} ) ;
120
134
@@ -187,13 +201,52 @@ describe('CdkDrag', () => {
187
201
fixture . detectChanges ( ) ;
188
202
189
203
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 ( {
191
210
previousIndex : 0 ,
192
211
currentIndex : 2 ,
193
212
item : firstItem ,
194
213
container : fixture . componentInstance . dropInstance ,
195
214
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
+ } ) ;
197
250
198
251
expect ( dragItems . map ( drag => drag . element . nativeElement . textContent ! . trim ( ) ) )
199
252
. toEqual ( [ 'One' , 'Two' , 'Zero' , 'Three' ] ) ;
@@ -217,6 +270,8 @@ describe('CdkDrag', () => {
217
270
expect ( preview ) . toBeTruthy ( 'Expected preview to be in the DOM' ) ;
218
271
expect ( preview . textContent ! . trim ( ) )
219
272
. toContain ( 'One' , 'Expected preview content to match element' ) ;
273
+ expect ( preview . getAttribute ( 'dir' ) )
274
+ . toBe ( 'ltr' , 'Expected preview element to inherit the directionality.' ) ;
220
275
expect ( previewRect . width ) . toBe ( itemRect . width , 'Expected preview width to match element' ) ;
221
276
expect ( previewRect . height ) . toBe ( itemRect . height , 'Expected preview height to match element' ) ;
222
277
@@ -230,6 +285,22 @@ describe('CdkDrag', () => {
230
285
expect ( preview . parentNode ) . toBeFalsy ( 'Expected preview to be removed from the DOM' ) ;
231
286
} ) ) ;
232
287
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
+
233
304
it ( 'should create a placeholder element while the item is dragged' , fakeAsync ( ( ) => {
234
305
const fixture = createComponent ( DraggableInDropZone ) ;
235
306
fixture . detectChanges ( ) ;
@@ -310,6 +381,62 @@ describe('CdkDrag', () => {
310
381
flush ( ) ;
311
382
} ) ) ;
312
383
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
+
313
440
it ( 'should clean up the preview element if the item is destroyed mid-drag' , fakeAsync ( ( ) => {
314
441
const fixture = createComponent ( DraggableInDropZone ) ;
315
442
fixture . detectChanges ( ) ;
@@ -442,6 +569,43 @@ export class DraggableInDropZone {
442
569
}
443
570
444
571
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
+
445
609
@Component ( {
446
610
template : `
447
611
<cdk-drop style="display: block; width: 100px; background: pink;">
0 commit comments