Skip to content

Commit d99f9f9

Browse files
committed
fix(cdk/drag-drop): remove preview wrapper
Switches back to positioning the preview directly instead of using a wrapper which can be breaking for existing apps. Instead we load a stylesheet dynamically with the necessary resets.
1 parent 0309adf commit d99f9f9

File tree

9 files changed

+118
-214
lines changed

9 files changed

+118
-214
lines changed

src/cdk/drag-drop/BUILD.bazel

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ load(
44
"ng_module",
55
"ng_test_library",
66
"ng_web_test_suite",
7+
"sass_binary",
78
)
89

910
package(default_visibility = ["//visibility:public"])
@@ -14,6 +15,9 @@ ng_module(
1415
["**/*.ts"],
1516
exclude = ["**/*.spec.ts"],
1617
),
18+
assets = [
19+
":resets_scss",
20+
],
1721
deps = [
1822
"//src:dev_mode_types",
1923
"//src/cdk/a11y",
@@ -44,6 +48,11 @@ ng_test_library(
4448
],
4549
)
4650

51+
sass_binary(
52+
name = "resets_scss",
53+
src = "resets.scss",
54+
)
55+
4756
ng_web_test_suite(
4857
name = "unit_tests",
4958
deps = [":unit_test_sources"],

src/cdk/drag-drop/directives/config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,4 @@ export interface DragDropConfig extends Partial<DragRefConfig> {
4444
listOrientation?: DropListOrientation;
4545
zIndex?: number;
4646
previewContainer?: 'global' | 'parent';
47-
disablePreviewPopover?: boolean;
4847
}

src/cdk/drag-drop/directives/drag.spec.ts

Lines changed: 32 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
ViewEncapsulation,
2424
} from '@angular/core';
2525
import {TestBed, ComponentFixture, fakeAsync, flush, tick} from '@angular/core/testing';
26-
import {DOCUMENT} from '@angular/common';
2726
import {ViewportRuler, CdkScrollableModule} from '@angular/cdk/scrolling';
2827
import {_supportsShadowDom} from '@angular/cdk/platform';
2928
import {of as observableOf} from 'rxjs';
@@ -2490,7 +2489,6 @@ describe('CdkDrag', () => {
24902489
startDraggingViaMouse(fixture, item);
24912490

24922491
const preview = document.querySelector('.cdk-drag-preview') as HTMLElement;
2493-
const previewContainer = document.querySelector('.cdk-drag-preview-container') as HTMLElement;
24942492
const previewRect = preview.getBoundingClientRect();
24952493
const zeroPxRegex = /^0(px)?$/;
24962494

@@ -2512,23 +2510,18 @@ describe('CdkDrag', () => {
25122510
.withContext('Expected element to be removed from layout')
25132511
.toBe('-999em');
25142512
expect(item.style.opacity).withContext('Expected element to be invisible').toBe('0');
2515-
expect(previewContainer)
2516-
.withContext('Expected preview container to be in the DOM')
2517-
.toBeTruthy();
2518-
expect(previewContainer.style.color)
2519-
.withContext('Expected preview container to reset user agent color')
2520-
.toBe('inherit');
2521-
expect(previewContainer.style.margin)
2522-
.withContext('Expected preview container to reset user agent margin')
2523-
.toMatch(zeroPxRegex);
2524-
expect(previewContainer.style.padding)
2525-
.withContext('Expected preview container to reset user agent padding')
2513+
expect(preview).withContext('Expected preview to be in the DOM').toBeTruthy();
2514+
expect(preview.getAttribute('popover'))
2515+
.withContext('Expected preview to be a popover')
2516+
.toBe('manual');
2517+
expect(preview.style.margin)
2518+
.withContext('Expected preview to reset the margin')
25262519
.toMatch(zeroPxRegex);
25272520
expect(preview.textContent!.trim())
25282521
.withContext('Expected preview content to match element')
25292522
.toContain('One');
2530-
expect(previewContainer.getAttribute('dir'))
2531-
.withContext('Expected preview container element to inherit the directionality.')
2523+
expect(preview.getAttribute('dir'))
2524+
.withContext('Expected preview element to inherit the directionality.')
25322525
.toBe('ltr');
25332526
expect(previewRect.width)
25342527
.withContext('Expected preview width to match element')
@@ -2539,8 +2532,8 @@ describe('CdkDrag', () => {
25392532
expect(preview.style.pointerEvents)
25402533
.withContext('Expected pointer events to be disabled on the preview')
25412534
.toBe('none');
2542-
expect(previewContainer.style.zIndex)
2543-
.withContext('Expected preview container to have a high default zIndex.')
2535+
expect(preview.style.zIndex)
2536+
.withContext('Expected preview to have a high default zIndex.')
25442537
.toBe('1000');
25452538
// Use a regex here since some browsers normalize 0 to 0px, but others don't.
25462539
// Use a regex here since some browsers normalize 0 to 0px, but others don't.
@@ -2561,8 +2554,8 @@ describe('CdkDrag', () => {
25612554
expect(item.style.top).withContext('Expected element to be within the layout').toBeFalsy();
25622555
expect(item.style.left).withContext('Expected element to be within the layout').toBeFalsy();
25632556
expect(item.style.opacity).withContext('Expected element to be visible').toBeFalsy();
2564-
expect(previewContainer.parentNode)
2565-
.withContext('Expected preview container to be removed from the DOM')
2557+
expect(preview.parentNode)
2558+
.withContext('Expected preview to be removed from the DOM')
25662559
.toBeFalsy();
25672560
}));
25682561

@@ -2580,59 +2573,10 @@ describe('CdkDrag', () => {
25802573
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
25812574
startDraggingViaMouse(fixture, item);
25822575

2583-
const preview = document.querySelector('.cdk-drag-preview-container')! as HTMLElement;
2576+
const preview = document.querySelector('.cdk-drag-preview')! as HTMLElement;
25842577
expect(preview.style.zIndex).toBe('3000');
25852578
}));
25862579

2587-
it('should create the preview inside the fullscreen element when in fullscreen mode', fakeAsync(() => {
2588-
// Provide a limited stub of the document since we can't trigger fullscreen
2589-
// mode in unit tests and there are some issues with doing it in e2e tests.
2590-
const fakeDocument = {
2591-
body: document.body,
2592-
documentElement: document.documentElement,
2593-
fullscreenElement: document.createElement('div'),
2594-
ELEMENT_NODE: Node.ELEMENT_NODE,
2595-
querySelectorAll: (...args: [string]) => document.querySelectorAll(...args),
2596-
querySelector: (...args: [string]) => document.querySelector(...args),
2597-
createElement: (...args: [string]) => document.createElement(...args),
2598-
createTextNode: (...args: [string]) => document.createTextNode(...args),
2599-
addEventListener: (
2600-
...args: [
2601-
string,
2602-
EventListenerOrEventListenerObject,
2603-
(boolean | AddEventListenerOptions | undefined)?,
2604-
]
2605-
) => document.addEventListener(...args),
2606-
removeEventListener: (
2607-
...args: [
2608-
string,
2609-
EventListenerOrEventListenerObject,
2610-
(boolean | AddEventListenerOptions | undefined)?,
2611-
]
2612-
) => document.addEventListener(...args),
2613-
createComment: (text: string) => document.createComment(text),
2614-
};
2615-
const fixture = createComponent(DraggableInDropZone, [
2616-
{
2617-
provide: DOCUMENT,
2618-
useFactory: () => fakeDocument,
2619-
},
2620-
]);
2621-
fixture.detectChanges();
2622-
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
2623-
2624-
document.body.appendChild(fakeDocument.fullscreenElement);
2625-
startDraggingViaMouse(fixture, item);
2626-
flush();
2627-
2628-
const previewContainer = document.querySelector(
2629-
'.cdk-drag-preview-container',
2630-
)! as HTMLElement;
2631-
2632-
expect(previewContainer.parentNode).toBe(fakeDocument.fullscreenElement);
2633-
fakeDocument.fullscreenElement.remove();
2634-
}));
2635-
26362580
it('should be able to constrain the preview position', fakeAsync(() => {
26372581
const fixture = createComponent(DraggableInDropZone);
26382582
fixture.componentInstance.boundarySelector = '.cdk-drop-list';
@@ -2928,8 +2872,8 @@ describe('CdkDrag', () => {
29282872
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
29292873
startDraggingViaMouse(fixture, item);
29302874

2931-
expect(document.querySelector('.cdk-drag-preview-container')!.getAttribute('dir'))
2932-
.withContext('Expected preview container to inherit the directionality.')
2875+
expect(document.querySelector('.cdk-drag-preview')!.getAttribute('dir'))
2876+
.withContext('Expected preview to inherit the directionality.')
29332877
.toBe('rtl');
29342878
}));
29352879

@@ -2941,7 +2885,6 @@ describe('CdkDrag', () => {
29412885
startDraggingViaMouse(fixture, item);
29422886

29432887
const preview = document.querySelector('.cdk-drag-preview') as HTMLElement;
2944-
const previewContainer = document.querySelector('.cdk-drag-preview-container') as HTMLElement;
29452888

29462889
// Add a duration since the tests won't include one.
29472890
preview.style.transitionDuration = '500ms';
@@ -2954,13 +2897,13 @@ describe('CdkDrag', () => {
29542897
fixture.detectChanges();
29552898
tick(250);
29562899

2957-
expect(previewContainer.parentNode)
2900+
expect(preview.parentNode)
29582901
.withContext('Expected preview to be in the DOM mid-way through the transition')
29592902
.toBeTruthy();
29602903

29612904
tick(500);
29622905

2963-
expect(previewContainer.parentNode)
2906+
expect(preview.parentNode)
29642907
.withContext('Expected preview to be removed from the DOM if the transition timed out')
29652908
.toBeFalsy();
29662909
}));
@@ -3064,7 +3007,6 @@ describe('CdkDrag', () => {
30643007
startDraggingViaMouse(fixture, item);
30653008

30663009
const preview = document.querySelector('.cdk-drag-preview')! as HTMLElement;
3067-
const previewContainer = document.querySelector('.cdk-drag-preview-container') as HTMLElement;
30683010
preview.style.transition = 'opacity 500ms ease';
30693011

30703012
dispatchMouseEvent(document, 'mousemove', 50, 50);
@@ -3074,8 +3016,8 @@ describe('CdkDrag', () => {
30743016
fixture.detectChanges();
30753017
tick(0);
30763018

3077-
expect(previewContainer.parentNode)
3078-
.withContext('Expected preview container to be removed from the DOM immediately')
3019+
expect(preview.parentNode)
3020+
.withContext('Expected preview to be removed from the DOM immediately')
30793021
.toBeFalsy();
30803022
}));
30813023

@@ -3087,7 +3029,6 @@ describe('CdkDrag', () => {
30873029
startDraggingViaMouse(fixture, item);
30883030

30893031
const preview = document.querySelector('.cdk-drag-preview')! as HTMLElement;
3090-
const previewContainer = document.querySelector('.cdk-drag-preview-container') as HTMLElement;
30913032
preview.style.transition = 'opacity 500ms ease, transform 1000ms ease';
30923033

30933034
dispatchMouseEvent(document, 'mousemove', 50, 50);
@@ -3097,17 +3038,15 @@ describe('CdkDrag', () => {
30973038
fixture.detectChanges();
30983039
tick(500);
30993040

3100-
expect(previewContainer.parentNode)
3101-
.withContext(
3102-
'Expected preview container to be in the DOM at the end of the opacity transition',
3103-
)
3041+
expect(preview.parentNode)
3042+
.withContext('Expected preview to be in the DOM at the end of the opacity transition')
31043043
.toBeTruthy();
31053044

31063045
tick(1000);
31073046

3108-
expect(previewContainer.parentNode)
3047+
expect(preview.parentNode)
31093048
.withContext(
3110-
'Expected preview container to be removed from the DOM at the end of the transform transition',
3049+
'Expected preview to be removed from the DOM at the end of the transform transition',
31113050
)
31123051
.toBeFalsy();
31133052
}));
@@ -3149,8 +3088,8 @@ describe('CdkDrag', () => {
31493088
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
31503089

31513090
startDraggingViaMouse(fixture, item);
3152-
const previewContainer = document.querySelector('.cdk-drag-preview-container') as HTMLElement;
3153-
expect(previewContainer.parentNode).toBe(document.body);
3091+
const preview = document.querySelector('.cdk-drag-preview') as HTMLElement;
3092+
expect(preview.parentNode).toBe(document.body);
31543093
}));
31553094

31563095
it('should insert the preview into the parent node if previewContainer is set to `parent`', fakeAsync(() => {
@@ -3161,9 +3100,9 @@ describe('CdkDrag', () => {
31613100
const list = fixture.nativeElement.querySelector('.drop-list');
31623101

31633102
startDraggingViaMouse(fixture, item);
3164-
const previewContainer = document.querySelector('.cdk-drag-preview-container') as HTMLElement;
3103+
const preview = document.querySelector('.cdk-drag-preview') as HTMLElement;
31653104
expect(list).toBeTruthy();
3166-
expect(previewContainer.parentNode).toBe(list);
3105+
expect(preview.parentNode).toBe(list);
31673106
}));
31683107

31693108
it('should insert the preview into a particular element, if specified', fakeAsync(() => {
@@ -3176,25 +3115,9 @@ describe('CdkDrag', () => {
31763115
fixture.componentInstance.previewContainer = previewContainer;
31773116
fixture.detectChanges();
31783117

3179-
startDraggingViaMouse(fixture, item);
3180-
const previewContainerElement = document.querySelector(
3181-
'.cdk-drag-preview-container',
3182-
) as HTMLElement;
3183-
expect(previewContainerElement.parentNode).toBe(previewContainer.nativeElement);
3184-
}));
3185-
3186-
it('should not create a popover wrapper if disablePreviewPopover is enabled', fakeAsync(() => {
3187-
const fixture = createComponent(DraggableInDropZone);
3188-
fixture.componentInstance.previewContainer = 'global';
3189-
fixture.componentInstance.disablePreviewPopover = true;
3190-
fixture.detectChanges();
3191-
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
3192-
31933118
startDraggingViaMouse(fixture, item);
31943119
const preview = document.querySelector('.cdk-drag-preview') as HTMLElement;
3195-
expect(document.querySelector('.cdk-drag-preview-container')).toBeFalsy();
3196-
expect(preview).toBeTruthy();
3197-
expect(preview.parentElement).toBe(document.body);
3120+
expect(preview.parentNode).toBe(previewContainer.nativeElement);
31983121
}));
31993122

32003123
it('should remove the id from the placeholder', fakeAsync(() => {
@@ -3706,17 +3629,15 @@ describe('CdkDrag', () => {
37063629

37073630
startDraggingViaMouse(fixture, item);
37083631

3709-
const previewContainer = document.querySelector('.cdk-drag-preview-container') as HTMLElement;
3632+
const preview = document.querySelector('.cdk-drag-preview') as HTMLElement;
37103633

3711-
expect(previewContainer.parentNode)
3712-
.withContext('Expected preview container to be in the DOM')
3713-
.toBeTruthy();
3634+
expect(preview.parentNode).withContext('Expected preview to be in the DOM').toBeTruthy();
37143635
expect(item.parentNode).withContext('Expected drag item to be in the DOM').toBeTruthy();
37153636

37163637
fixture.destroy();
37173638

3718-
expect(previewContainer.parentNode)
3719-
.withContext('Expected preview container to be removed from the DOM')
3639+
expect(preview.parentNode)
3640+
.withContext('Expected preview to be removed from the DOM')
37203641
.toBeFalsy();
37213642
expect(item.parentNode)
37223643
.withContext('Expected drag item to be removed from the DOM')
@@ -6950,7 +6871,6 @@ const DROP_ZONE_FIXTURE_TEMPLATE = `
69506871
[cdkDragBoundary]="boundarySelector"
69516872
[cdkDragPreviewClass]="previewClass"
69526873
[cdkDragPreviewContainer]="previewContainer"
6953-
[cdkDragDisablePreviewPopover]="disablePreviewPopover"
69546874
[style.height.px]="item.height"
69556875
[style.margin-bottom.px]="item.margin"
69566876
(cdkDragStarted)="startedSpy($event)"
@@ -6980,7 +6900,6 @@ class DraggableInDropZone implements AfterViewInit {
69806900
});
69816901
startedSpy = jasmine.createSpy('started spy');
69826902
previewContainer: PreviewContainer = 'global';
6983-
disablePreviewPopover = false;
69846903

69856904
constructor(protected _elementRef: ElementRef) {}
69866905

src/cdk/drag-drop/directives/drag.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -159,20 +159,6 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
159159
*/
160160
@Input('cdkDragPreviewContainer') previewContainer: PreviewContainer;
161161

162-
/**
163-
* By default the preview element is wrapped in a native popover in order to be compatible
164-
* with other native popovers and to avoid issues with `overflow: hidden`. In some edge cases
165-
* this can interfere with styling (e.g. CSS selectors targeting direct descendants). Enable
166-
* this option to remove the wrapper around the preview, but note that it can cause the following
167-
* issues when used with `cdkDragPreviewContainer` set to `parent` or a specific DOM node:
168-
* - The preview may be clipped by a parent with `overflow: hidden`.
169-
* - The preview isn't guaranteed to be on top of other elements, despite its `z-index`.
170-
* - Transforms on the parent of the preview can affect its positioning.
171-
* - The preview may be positioned under native `<dialog>` or popover elements.
172-
*/
173-
@Input({alias: 'cdkDragDisablePreviewPopover', transform: booleanAttribute})
174-
disablePreviewPopover: boolean;
175-
176162
/** Emits when the user starts dragging the item. */
177163
@Output('cdkDragStarted') readonly started: EventEmitter<CdkDragStart> =
178164
new EventEmitter<CdkDragStart>();
@@ -472,7 +458,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
472458
.withBoundaryElement(this._getBoundaryElement())
473459
.withPlaceholderTemplate(placeholder)
474460
.withPreviewTemplate(preview)
475-
.withPreviewContainer(this.previewContainer || 'global', this.disablePreviewPopover);
461+
.withPreviewContainer(this.previewContainer || 'global');
476462

477463
if (dir) {
478464
ref.withDirection(dir.value);
@@ -573,12 +559,10 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
573559
draggingDisabled,
574560
rootElementSelector,
575561
previewContainer,
576-
disablePreviewPopover,
577562
} = config;
578563

579564
this.disabled = draggingDisabled == null ? false : draggingDisabled;
580565
this.dragStartDelay = dragStartDelay || 0;
581-
this.disablePreviewPopover = disablePreviewPopover || false;
582566

583567
if (lockAxis) {
584568
this.lockAxis = lockAxis;

0 commit comments

Comments
 (0)