Skip to content

Commit 3a2d13e

Browse files
committed
fix(cdk/drag-drop): preview positioned incorrectly when RTL is set on the body (#29606)
As of #28945 we use a popover to display the preview so that it's always on top. To do so we need to push the popover from its default position at the center to the top/left which is done using `margin: auto`. Since we were setting `margin: 0`, the element was ending up at top/right in RTL, if `dir="rtl"` is set on the `html` or `body`. These changes fix the issue by pushing the element to the top/left using `margin-right: auto`. Fixes #29604. (cherry picked from commit 04ce4d2)
1 parent b2a32e9 commit 3a2d13e

File tree

2 files changed

+15
-9
lines changed

2 files changed

+15
-9
lines changed

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,6 @@ export function defineCommonDropListTests(config: {
834834

835835
const preview = document.querySelector('.cdk-drag-preview') as HTMLElement;
836836
const previewRect = preview.getBoundingClientRect();
837-
const zeroPxRegex = /^0(px)?$/;
838837

839838
expect(item.parentNode)
840839
.withContext('Expected element to be moved out into the body')
@@ -846,10 +845,9 @@ export function defineCommonDropListTests(config: {
846845
.withContext('Expect element position to be !important')
847846
.toBe('important');
848847
// Use a regex here since some browsers normalize 0 to 0px, but others don't.
849-
// Use a regex here since some browsers normalize 0 to 0px, but others don't.
850848
expect(item.style.top)
851849
.withContext('Expected element to be removed from layout')
852-
.toMatch(zeroPxRegex);
850+
.toMatch(/^0(px)?$/);
853851
expect(item.style.left)
854852
.withContext('Expected element to be removed from layout')
855853
.toBe('-999em');
@@ -860,7 +858,7 @@ export function defineCommonDropListTests(config: {
860858
.toBe('manual');
861859
expect(preview.style.margin)
862860
.withContext('Expected preview to reset the margin')
863-
.toMatch(zeroPxRegex);
861+
.toMatch(/^(0(px)? auto 0(px)? 0(px)?)|(0(px)?)$/);
864862
expect(preview.textContent!.trim())
865863
.withContext('Expected preview content to match element')
866864
.toContain('One');
@@ -880,10 +878,9 @@ export function defineCommonDropListTests(config: {
880878
.withContext('Expected preview to have a high default zIndex.')
881879
.toBe('1000');
882880
// Use a regex here since some browsers normalize 0 to 0px, but others don't.
883-
// Use a regex here since some browsers normalize 0 to 0px, but others don't.
884881
expect(preview.style.margin)
885882
.withContext('Expected the preview margin to be reset.')
886-
.toMatch(zeroPxRegex);
883+
.toMatch(/^(0(px)? auto 0(px)? 0(px)?)|(0(px)?)$/);
887884

888885
dispatchMouseEvent(document, 'mouseup');
889886
fixture.detectChanges();

src/cdk/drag-drop/preview-ref.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export class PreviewRef {
6464

6565
// The null check is necessary for browsers that don't support the popover API.
6666
// Note that we use a string access for compatibility with Closure.
67-
if ('showPopover' in this._preview) {
67+
if (supportsPopover(this._preview)) {
6868
this._preview['showPopover']();
6969
}
7070
}
@@ -139,8 +139,12 @@ export class PreviewRef {
139139
// It's important that we disable the pointer events on the preview, because
140140
// it can throw off the `document.elementFromPoint` calls in the `CdkDropList`.
141141
'pointer-events': 'none',
142-
// We have to reset the margin, because it can throw off positioning relative to the viewport.
143-
'margin': '0',
142+
// If the preview has a margin, it can throw off our positioning so we reset it. The reset
143+
// value for `margin-right` needs to be `auto` when opened as a popover, because our
144+
// positioning is always top/left based, but native popover seems to position itself
145+
// to the top/right if `<html>` or `<body>` have `dir="rtl"` (see #29604). Setting it
146+
// to `auto` pushed it to the top/left corner in RTL and is a noop in LTR.
147+
'margin': supportsPopover(preview) ? '0 auto 0 0' : '0',
144148
'position': 'fixed',
145149
'top': '0',
146150
'left': '0',
@@ -165,3 +169,8 @@ export class PreviewRef {
165169
return preview;
166170
}
167171
}
172+
173+
/** Checks whether a specific element supports the popover API. */
174+
function supportsPopover(element: HTMLElement): boolean {
175+
return 'showPopover' in element;
176+
}

0 commit comments

Comments
 (0)