Skip to content

Commit 6926538

Browse files
authored
fix(popover): ensure popover does not go offscreen when adjusting top position (#25350)
resolves #25349
1 parent efe9e92 commit 6926538

File tree

3 files changed

+102
-1
lines changed

3 files changed

+102
-1
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<!DOCTYPE html>
2+
<html lang="en" dir="ltr">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Popover - Adjustment</title>
6+
<meta
7+
name="viewport"
8+
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
9+
/>
10+
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
11+
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
12+
<script src="../../../../../scripts/testing/scripts.js"></script>
13+
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
14+
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
15+
<script type="module">
16+
import { popoverController } from '../../../../dist/ionic/index.esm.js';
17+
window.popoverController = popoverController;
18+
</script>
19+
</head>
20+
<body>
21+
<ion-app>
22+
<ion-content>
23+
<p style="text-align: center">Click everywhere to open the popover.</p>
24+
</ion-content>
25+
</ion-app>
26+
27+
<script>
28+
document.querySelector('ion-content').addEventListener('click', handleButtonClick);
29+
30+
async function handleButtonClick(ev) {
31+
const popover = await popoverController.create({
32+
component: 'popover-example-page',
33+
event: ev,
34+
reference: 'event',
35+
});
36+
37+
popover.present();
38+
}
39+
40+
customElements.define(
41+
'popover-example-page',
42+
class PopoverContent extends HTMLElement {
43+
connectedCallback() {
44+
this.innerHTML = `
45+
<ion-list>
46+
<ion-list-header>Ionic</ion-list-header>
47+
<ion-item button>Learn Ionic</ion-item>
48+
<ion-item button>Documentation</ion-item>
49+
<ion-item button>Showcase</ion-item>
50+
<ion-item button>GitHub Repo</ion-item>
51+
<ion-item button>Another Item</ion-item>
52+
</ion-list>
53+
`;
54+
}
55+
}
56+
);
57+
</script>
58+
</body>
59+
</html>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { expect } from '@playwright/test';
2+
import { test } from '@utils/test/playwright';
3+
4+
test.describe('popover: adjustment', async () => {
5+
test('should not render the popover offscreen', async ({ page }) => {
6+
await page.goto('/src/components/popover/test/adjustment');
7+
8+
/**
9+
* We need to click in an area where
10+
* there is not enough room to show the popover
11+
* below the click coordinates but not enough
12+
* room above the click coordinates that we
13+
* can just move the popover to without it going
14+
* offscreen.
15+
*/
16+
await page.setViewportSize({
17+
width: 500,
18+
height: 400,
19+
});
20+
21+
const ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent');
22+
23+
await page.mouse.click(300, 300);
24+
25+
await ionPopoverDidPresent.next();
26+
27+
const popoverContent = page.locator('ion-popover .popover-content');
28+
const box = (await popoverContent.boundingBox())!;
29+
30+
expect(box.y > 0).toBe(true);
31+
});
32+
});

core/src/components/popover/utils.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,17 @@ export const calculateWindowAdjustment = (
863863
*/
864864
if (triggerTop + triggerHeight + contentHeight > bodyHeight && (side === 'top' || side === 'bottom')) {
865865
if (triggerTop - contentHeight > 0) {
866-
top = triggerTop - contentHeight - triggerHeight - (arrowHeight - 1);
866+
/**
867+
* While we strive to align the popover with the trigger
868+
* on smaller screens this is not always possible. As a result,
869+
* we adjust the popover up so that it does not hang
870+
* off the bottom of the screen. However, we do not want to move
871+
* the popover up so much that it goes off the top of the screen.
872+
*
873+
* We chose 12 here so that the popover position looks a bit nicer as
874+
* it is not right up against the edge of the screen.
875+
*/
876+
top = Math.max(12, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
867877
arrowTop = top + contentHeight;
868878
originY = 'bottom';
869879
addPopoverBottomClass = true;

0 commit comments

Comments
 (0)