Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ui5-popover): fix arrow position in RTL direction #10008

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions packages/main/src/Popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ class Popover extends Popup {
@property()
actualPlacement: `${PopoverPlacement}` = "End";

/**
* Special positioning is needed, when we have a popover inside iFrame in RTL.
* The scrollbar width should be considered in the positioning
* @private
*/
@property({ type: Boolean })
RTLAndIFramePositioning = false;

@property({ type: Number, noAttribute: true })
_maxHeight?: number;

Expand Down Expand Up @@ -403,6 +411,7 @@ class Popover extends Popup {

this._oldPlacement = placement;
this.actualPlacement = placement.placement;
this.RTLAndIFramePositioning = this.getRTLAndIFramePositioning();

let left = clamp(
this._left!,
Expand Down Expand Up @@ -504,6 +513,7 @@ class Popover extends Popup {
let left = Popover.VIEWPORT_MARGIN;
let top = 0;
const allowTargetOverlap = this.allowTargetOverlap;
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;

const clientWidth = document.documentElement.clientWidth;
const clientHeight = document.documentElement.clientHeight;
Expand Down Expand Up @@ -555,6 +565,11 @@ class Popover extends Popup {
if (!allowTargetOverlap) {
maxWidth = targetRect.left - arrowOffset;
}

if (this.getRTLAndIFramePositioning()) {
left -= scrollbarWidth;
}

break;
case PopoverPlacement.End:
left = targetRect.left + targetRect.width + arrowOffset;
Expand All @@ -565,6 +580,11 @@ class Popover extends Popup {
} else {
maxWidth = clientWidth - targetRect.right - arrowOffset;
}

if (this.getRTLAndIFramePositioning()) {
left -= scrollbarWidth;
}

break;
}

Expand Down Expand Up @@ -619,6 +639,7 @@ class Popover extends Popup {
getArrowPosition(targetRect: DOMRect, popoverSize: PopoverSize, left: number, top: number, isVertical: boolean, borderRadius: number): ArrowPosition {
const horizontalAlign = this._actualHorizontalAlign;
let arrowXCentered = horizontalAlign === PopoverHorizontalAlign.Center || horizontalAlign === PopoverHorizontalAlign.Stretch;
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;

if (horizontalAlign === PopoverHorizontalAlign.End && left <= targetRect.left) {
arrowXCentered = true;
Expand All @@ -631,6 +652,10 @@ class Popover extends Popup {
let arrowTranslateX = 0;
if (isVertical && arrowXCentered) {
arrowTranslateX = targetRect.left + targetRect.width / 2 - left - popoverSize.width / 2;

if (this.getRTLAndIFramePositioning()) {
arrowTranslateX -= scrollbarWidth;
}
}

let arrowTranslateY = 0;
Expand Down Expand Up @@ -717,14 +742,35 @@ class Popover extends Popup {
return actualPlacement;
}

isInsideIframe() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scrollbar is on the right side on Firefox.
Check why does it behave differently on Chromium based browsers only in iframes. Is this a known bug of Chromium?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately the Firefox behavior is the wrong one.
Here is open issue on the topic: https://bugzilla.mozilla.org/show_bug.cgi?id=619963
And also older report for Chromium: https://issues.chromium.org/issues/40322053

return window !== window.top;
}

isRTL() {
return document.documentElement.getAttribute("dir") === "rtl";
}

hasVerticalScrollbar() {
return document.documentElement.scrollHeight > document.documentElement.clientHeight;
}

getRTLAndIFramePositioning() {
return this.isInsideIframe() && this.isRTL() && this.hasVerticalScrollbar();
}

getVerticalLeft(targetRect: DOMRect, popoverSize: PopoverSize): number {
const horizontalAlign = this._actualHorizontalAlign;
let left = Popover.VIEWPORT_MARGIN;
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;

switch (horizontalAlign) {
case PopoverHorizontalAlign.Center:
case PopoverHorizontalAlign.Stretch:
left = targetRect.left - (popoverSize.width - targetRect.width) / 2;
if (this.getRTLAndIFramePositioning()) {
left -= scrollbarWidth;
}

break;
case PopoverHorizontalAlign.Start:
left = targetRect.left;
Expand Down
16 changes: 16 additions & 0 deletions packages/main/src/themes/Popover.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
margin: var(--_ui5_popover_upward_arrow_margin);
}

:host([actual-placement="Bottom"][special-positioning]) .ui5-popover-arrow:after {
margin: 4px 2px 0 0;
}

/* pointing right arrow */
:host([actual-placement="Start"]) .ui5-popover-arrow {
top: calc(50% - 0.5625rem);
Expand All @@ -38,6 +42,10 @@
margin: var(--_ui5_popover_right_arrow_margin);
}

:host([actual-placement="Start"][special-positioning]) .ui5-popover-arrow:after {
margin: 2px 4px 0 0;
}

/* pointing downward arrow */
:host([actual-placement="Top"]) .ui5-popover-arrow {
left: calc(50% - 0.5625rem);
Expand All @@ -49,6 +57,10 @@
margin: var(--_ui5_popover_downward_arrow_margin);
}

:host([actual-placement="Top"][special-positioning]) .ui5-popover-arrow:after {
margin: -7px 1px 0 0;
}

/* pointing left arrow */
:host(:not([actual-placement])) .ui5-popover-arrow,
:host([actual-placement="End"]) .ui5-popover-arrow {
Expand All @@ -63,6 +75,10 @@
margin: var(--_ui5_popover_left_arrow_margin);
}

:host([actual-placement="End"][special-positioning]) .ui5-popover-arrow:after {
margin: 2px -6px 0 0;
}

:host([hide-arrow]) .ui5-popover-arrow {
display: none;
}
Expand Down
85 changes: 85 additions & 0 deletions packages/main/test/pages/Popover.html
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,61 @@ <h2>Open and scroll</h2>
<br><br>
<br>

<div dir="rtl" style="height: 420px; overflow-y: scroll;">
<div style="height: 100%; width: 100%">
<span>RTL sample with scrollbar</span>
<br>
<span> Popover placement Top</span>
<div class="centered-button">
<ui5-button id="btn1">Open</ui5-button>
</div>
<br>

<span> Popover placement Bottom</span>
<div class="centered-button">
<ui5-button id="btn2">Open</ui5-button>
</div>
<br>

<span> Popover placement Start</span>
<ui5-button id="btn3">Open</ui5-button>
<br>

<span> Popover placement End</span>
<ui5-button id="btn4">Open</ui5-button>

<div style="height: 200px;"></div>

<ui5-popover id="respPopover1" opener="btn1" placement="Top">
<div class="popover-content">
<ui5-label for="emailInput" required show-colon>Email</ui5-label>
</div>
</ui5-popover>

<ui5-popover id="respPopover2" opener="btn2" placement="Bottom">
<div class="popover-content">
<ui5-label for="emailInput" required show-colon>Email</ui5-label>
</div>
</ui5-popover>

<ui5-popover id="respPopover3" opener="btn3" placement="Start">
<div class="popover-content">
<ui5-label for="emailInput" required show-colon>Email</ui5-label>
</div>
</ui5-popover>

<ui5-popover id="respPopover4" opener="btn4" placement="End">
<div class="popover-content">
<ui5-label for="emailInput" required show-colon>Email</ui5-label>
</div>
</ui5-popover>
</div>
</div>

<iframe src="./PopoverRTL.html"></iframe>

<div style="height: 100px;"></div>

<script>
btn.addEventListener("click", function (event) {
pop.opener = btn;
Expand Down Expand Up @@ -844,6 +899,36 @@ <h2>Open and scroll</h2>
document.getElementById("vertical").open = true;
});

const btn1 = document.getElementById("btn1");
const respPopover1 = document.getElementById("respPopover1");

const btn2 = document.getElementById("btn2");
const respPopover2 = document.getElementById("respPopover2");

const btn3 = document.getElementById("btn3");
const respPopover3 = document.getElementById("respPopover3");


const btn4 = document.getElementById("btn4");
const respPopover4 = document.getElementById("respPopover4");


btn1.addEventListener("click", () => {
respPopover1.open = !respPopover1.open;
});

btn2.addEventListener("click", () => {
respPopover2.open = !respPopover2.open;
});

btn3.addEventListener("click", () => {
respPopover3.open = !respPopover3.open;
});

btn4.addEventListener("click", () => {
respPopover4.open = !respPopover4.open;
});

</script>
</body>

Expand Down
122 changes: 122 additions & 0 deletions packages/main/test/pages/PopoverRTL.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<!DOCTYPE html>
<html dir="rtl">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Popover</title>

<script data-ui5-config type="application/json">
{
"language": "EN",
"libs": "sap.ui.webc.main",
"rtl": true
}
</script>


<script src="%VITE_BUNDLE_PATH%" type="module"></script>


<link rel="stylesheet" type="text/css" href="./styles/Popover.css">

<script>
// delete Document.prototype.adoptedStyleSheets
</script>
</head>

<body>
<style>

.center {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
}

body {
padding-top: 100px;
}
</style>

<span> Popover placement Top</span>
<div class="center">
<ui5-button id="btn1">Open</ui5-button>
</div>
<br>

<span> Popover placement Bottom</span>
<div class="center">
<ui5-button id="btn2">Open</ui5-button>
</div>
<br>

<span> Popover placement Start</span>
<ui5-button id="btn3">Open</ui5-button>
<br>

<ui5-button id="btn4">Open</ui5-button>
<span> Popover placement End</span>

<ui5-popover id="respPopover1" opener="btn1" placement="Top">
<div class="popover-content">
<ui5-label for="emailInput" required show-colon>Email</ui5-label>
</div>
</ui5-popover>

<ui5-popover id="respPopover2" opener="btn2" placement="Bottom">
<div class="popover-content">
<ui5-label for="emailInput" required show-colon>Email</ui5-label>
</div>
</ui5-popover>

<ui5-popover id="respPopover3" opener="btn3" placement="Start">
<div class="popover-content">
<ui5-label for="emailInput" required show-colon>Email</ui5-label>
</div>
</ui5-popover>

<ui5-popover id="respPopover4" opener="btn4" placement="End">
<div class="popover-content">
<ui5-label for="emailInput" required show-colon>Email</ui5-label>
</div>
</ui5-popover>

<div style="height: 200px;"></div>

<script>
const btn1 = document.getElementById("btn1");
const respPopover1 = document.getElementById("respPopover1");

const btn2 = document.getElementById("btn2");
const respPopover2 = document.getElementById("respPopover2");

const btn3 = document.getElementById("btn3");
const respPopover3 = document.getElementById("respPopover3");


const btn4 = document.getElementById("btn4");
const respPopover4 = document.getElementById("respPopover4");


btn1.addEventListener("click", () => {
respPopover1.open = !respPopover1.open;
});

btn2.addEventListener("click", () => {
respPopover2.open = !respPopover2.open;
});

btn3.addEventListener("click", () => {
respPopover3.open = !respPopover3.open;
});

btn4.addEventListener("click", () => {
respPopover4.open = !respPopover4.open;
});
</script>
</body>

</html>
7 changes: 7 additions & 0 deletions packages/main/test/pages/styles/Popover.css
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,11 @@ ui5-date-picker,

#divOpenAndScroll > div {
height: 2000px;
}

.centered-button {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
Loading