Skip to content

Commit f6a00ea

Browse files
authoredDec 6, 2021
fix(popover): handle scrolling in content so header can be sticky (#24294)
1 parent b083ae4 commit f6a00ea

File tree

5 files changed

+108
-2
lines changed

5 files changed

+108
-2
lines changed
 

‎core/src/components/content/content.scss

+27
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,37 @@
140140
}
141141

142142
:host(.content-sizing) {
143+
display: flex;
144+
145+
flex-direction: column;
146+
147+
/**
148+
* This resolves a sizing issue in popovers where extra long content
149+
* would overflow the popover's height, preventing scrolling. It's a
150+
* quirk of flexbox that forces the content to shrink to fit.
151+
*
152+
* overflow: hidden can't be used here because it prevents the visual
153+
* effect from showing on translucent headers.
154+
*/
155+
min-height: 0;
156+
143157
contain: none;
144158
}
145159
:host(.content-sizing) .inner-scroll {
146160
position: relative;
161+
162+
/**
163+
* Because the outer content has display: flex here (to help enable
164+
* scrolling in a popover), offsetting via `top` (such as when using
165+
* a translucent header) creates white space under the content. Use
166+
* a negative margin instead to keep the bottom in place. (A similar
167+
* thing happens with `bottom` and footers.)
168+
*/
169+
top: 0;
170+
bottom: 0;
171+
172+
margin-top: calc(var(--offset-top) * -1);
173+
margin-bottom: calc(var(--offset-bottom) * -1);
147174
}
148175

149176
.transition-effect {

‎core/src/components/content/content.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -374,10 +374,17 @@ const getPageElement = (el: HTMLElement) => {
374374
if (tabs) {
375375
return tabs;
376376
}
377-
const page = el.closest('ion-app,ion-page,.ion-page,page-inner');
377+
378+
/**
379+
* If we're in a popover, we need to use its wrapper so we can account for space
380+
* between the popover and the edges of the screen. But if the popover contains
381+
* its own page element, we should use that instead.
382+
*/
383+
const page = el.closest('ion-app, ion-page, .ion-page, page-inner, .popover-content');
378384
if (page) {
379385
return page;
380386
}
387+
381388
return getParentElement(el);
382389
};
383390

‎core/src/components/popover/popover.scss

+5-1
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,9 @@
7575
--ion-safe-area-right: 0px;
7676
--ion-safe-area-bottom: 0px;
7777
--ion-safe-area-left: 0px;
78-
}
78+
display: flex;
7979

80+
flex-direction: column;
81+
82+
overflow: hidden;
83+
}

‎core/src/components/popover/test/basic/e2e.ts

+16
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ test('popover: custom class', async () => {
5757
await testPopover(DIRECTORY, '#custom-class-popover');
5858
});
5959

60+
test('popover: header', async () => {
61+
await testPopover(DIRECTORY, '#header-popover');
62+
});
63+
64+
test('popover: translucent header', async () => {
65+
await testPopover(DIRECTORY, '#translucent-header-popover');
66+
});
67+
6068
/**
6169
* RTL Tests
6270
*/
@@ -81,6 +89,14 @@ test('popover:rtl: custom class', async () => {
8189
await testPopover(DIRECTORY, '#custom-class-popover', true);
8290
});
8391

92+
test('popover:rtl: header', async () => {
93+
await testPopover(DIRECTORY, '#header-popover', true);
94+
});
95+
96+
test('popover:rtl: translucent header', async () => {
97+
await testPopover(DIRECTORY, '#translucent-header-popover', true);
98+
});
99+
84100
test('popover: htmlAttributes', async () => {
85101
const page = await newE2EPage({ url: '/src/components/popover/test/basic?ionic:_testing=true' });
86102

‎core/src/components/popover/test/basic/index.html

+52
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
<ion-button id="long-list-popover" expand="block" color="secondary" onclick="presentPopover({ component: 'list-page', event: event })">Show Long List Popover</ion-button>
3535
<ion-button id="no-event-popover" expand="block" color="danger" onclick="presentPopover({ component: 'profile-page' })">No Event Popover</ion-button>
3636
<ion-button id="custom-class-popover" expand="block" color="tertiary" onclick="presentPopover({ component: 'translucent-page', event: event, cssClass: 'my-custom-class' })">Custom Class Popover</ion-button>
37+
<ion-button id="header-popover" expand="block" onclick="presentPopover({ component: 'header-page' })">Popover With Header</ion-button>
38+
<ion-button id="translucent-header-popover" expand="block" onclick="presentPopover({ component: 'translucent-header-page' })">Popover With Translucent Header</ion-button>
3739
</ion-content>
3840

3941
<ion-footer>
@@ -126,6 +128,56 @@ <h1>Translucent Popover</h1>
126128
}
127129

128130
customElements.define('translucent-page', TranslucentPage);
131+
132+
class HeaderPage extends HTMLElement {
133+
constructor() {
134+
super();
135+
}
136+
137+
connectedCallback() {
138+
this.innerHTML = `
139+
<ion-header>
140+
<ion-toolbar>
141+
<ion-title>Header</ion-title>
142+
</ion-toolbar>
143+
</ion-header>
144+
145+
<ion-content class="ion-padding" color="primary">
146+
Lorem ipsum dolor sit amet, consectetur adipiscing elit.In rutrum tortor lacus, ac interdum ipsum bibendum vel.Aenean non nibh gravida, ullamcorper mi at, tempor nulla.Proin malesuada tellus ut ullamcorper accumsan.Donec semper justo vulputate neque tempus ultricies.Proin non aliquet ipsum.Praesent mauris sem, facilisis eu justo nec, euismod imperdiet tellus.Duis eget justo congue, lacinia orci sed, fermentum urna.Quisque sed massa faucibus, interdum dolor rhoncus, molestie erat.Proin suscipit ante non mauris volutpat egestas.Donec a ultrices ligula.Mauris in felis vel dui consectetur viverra.Nam vitae quam in arcu aliquam aliquam.Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Cras non velit nisl.Donec viverra, magna quis vestibulum volutpat, metus ante tincidunt augue, non porta nisi mi sit amet neque.Proin dapibus eros vitae nibh tincidunt, blandit rhoncus est porttitor.
147+
</ion-content>
148+
`;
149+
}
150+
}
151+
152+
customElements.define('header-page', HeaderPage);
153+
154+
class TranslucentHeaderPage extends HTMLElement {
155+
constructor() {
156+
super();
157+
}
158+
159+
connectedCallback() {
160+
this.innerHTML = `
161+
<ion-header translucent>
162+
<ion-toolbar>
163+
<ion-title>Header</ion-title>
164+
</ion-toolbar>
165+
</ion-header>
166+
167+
<ion-content class="ion-padding" fullscreen color="primary">
168+
Lorem ipsum dolor sit amet, consectetur adipiscing elit.In rutrum tortor lacus, ac interdum ipsum bibendum vel.Aenean non nibh gravida, ullamcorper mi at, tempor nulla.Proin malesuada tellus ut ullamcorper accumsan.Donec semper justo vulputate neque tempus ultricies.Proin non aliquet ipsum.Praesent mauris sem, facilisis eu justo nec, euismod imperdiet tellus.Duis eget justo congue, lacinia orci sed, fermentum urna.Quisque sed massa faucibus, interdum dolor rhoncus, molestie erat.Proin suscipit ante non mauris volutpat egestas.Donec a ultrices ligula.Mauris in felis vel dui consectetur viverra.Nam vitae quam in arcu aliquam aliquam.Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Cras non velit nisl.Donec viverra, magna quis vestibulum volutpat, metus ante tincidunt augue, non porta nisi mi sit amet neque.Proin dapibus eros vitae nibh tincidunt, blandit rhoncus est porttitor.
169+
</ion-content>
170+
171+
<ion-footer translucent>
172+
<ion-toolbar>
173+
<ion-title>Footer</ion-title>
174+
</ion-toolbar>
175+
</ion-footer>
176+
`;
177+
}
178+
}
179+
180+
customElements.define('translucent-header-page', TranslucentHeaderPage);
129181
</script>
130182
</body>
131183

0 commit comments

Comments
 (0)
Please sign in to comment.