Skip to content

Commit 4a25aa1

Browse files
test(header,footer,tab-bar): add scroll-effect-hide e2e tests and enforce footer priority
1 parent 07a60ca commit 4a25aa1

6 files changed

Lines changed: 216 additions & 3 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { expect } from '@playwright/test';
2+
import { configs, test } from '@utils/test/playwright';
3+
4+
configs({ modes: ['ios', 'md', 'ionic-ios', 'ionic-md'], directions: ['ltr'] }).forEach(({ title, config }) => {
5+
test.describe(title('footer: scroll-effect-hide'), () => {
6+
test('should have the scroll-effect-hide class when scrollEffect is set to hide', async ({ page }) => {
7+
await page.goto('/src/components/footer/test/scroll-effect-hide', config);
8+
9+
const footer = page.locator('ion-footer');
10+
await expect(footer).toHaveClass(/footer-scroll-effect-hide/);
11+
});
12+
13+
test('should hide the footer when scrolling down', async ({ page }) => {
14+
await page.goto('/src/components/footer/test/scroll-effect-hide', config);
15+
16+
const footer = page.locator('ion-footer');
17+
const content = page.locator('ion-content');
18+
19+
await expect(footer).not.toHaveClass(/footer-scroll-hidden/);
20+
21+
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
22+
await page.locator('ion-footer.footer-scroll-hidden').waitFor();
23+
24+
await expect(footer).toHaveClass(/footer-scroll-hidden/);
25+
await expect(footer).toHaveAttribute('inert', '');
26+
});
27+
28+
test('should show the footer again when scrolling back to the top', async ({ page }) => {
29+
await page.goto('/src/components/footer/test/scroll-effect-hide', config);
30+
31+
const footer = page.locator('ion-footer');
32+
const content = page.locator('ion-content');
33+
34+
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
35+
await page.locator('ion-footer.footer-scroll-hidden').waitFor();
36+
37+
await content.evaluate((el: HTMLIonContentElement) => el.scrollToTop(0));
38+
await page.locator('ion-footer:not(.footer-scroll-hidden)').waitFor();
39+
40+
await expect(footer).not.toHaveClass(/footer-scroll-hidden/);
41+
await expect(footer).not.toHaveAttribute('inert');
42+
});
43+
});
44+
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { expect } from '@playwright/test';
2+
import { configs, test } from '@utils/test/playwright';
3+
4+
configs({ modes: ['ios', 'md', 'ionic-ios', 'ionic-md'], directions: ['ltr'] }).forEach(({ title, config }) => {
5+
test.describe(title('header: scroll-effect-hide'), () => {
6+
test('should have the scroll-effect-hide class when scrollEffect is set to hide', async ({ page }) => {
7+
await page.goto('/src/components/header/test/scroll-effect-hide', config);
8+
9+
const header = page.locator('ion-header');
10+
await expect(header).toHaveClass(/header-scroll-effect-hide/);
11+
});
12+
13+
test('should hide the header when scrolling down', async ({ page }) => {
14+
await page.goto('/src/components/header/test/scroll-effect-hide', config);
15+
16+
const header = page.locator('ion-header');
17+
const content = page.locator('ion-content');
18+
19+
await expect(header).not.toHaveClass(/header-scroll-hidden/);
20+
21+
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
22+
await page.locator('ion-header.header-scroll-hidden').waitFor();
23+
24+
await expect(header).toHaveClass(/header-scroll-hidden/);
25+
await expect(header).toHaveAttribute('inert', '');
26+
});
27+
28+
test('should show the header again when scrolling back to the top', async ({ page }) => {
29+
await page.goto('/src/components/header/test/scroll-effect-hide', config);
30+
31+
const header = page.locator('ion-header');
32+
const content = page.locator('ion-content');
33+
34+
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
35+
await page.locator('ion-header.header-scroll-hidden').waitFor();
36+
37+
await content.evaluate((el: HTMLIonContentElement) => el.scrollToTop(0));
38+
await page.locator('ion-header:not(.header-scroll-hidden)').waitFor();
39+
40+
await expect(header).not.toHaveClass(/header-scroll-hidden/);
41+
await expect(header).not.toHaveAttribute('inert');
42+
});
43+
});
44+
});

core/src/components/tab-bar/tab-bar.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,8 @@ export class TabBar implements ComponentInterface {
187187
}
188188

189189
private setupScrollEffect = async () => {
190-
// If parent ion-footer also has scrollEffect="hide", defer to the footer's animation
191-
const footerEl = this.el.closest('ion-footer') as (HTMLIonFooterElement & { scrollEffect?: string }) | null;
192-
if (footerEl?.scrollEffect === 'hide') {
190+
// When nested inside ion-footer, the footer owns the hide animation.
191+
if (this.el.closest('ion-footer')) {
193192
return;
194193
}
195194

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<!DOCTYPE html>
2+
<html lang="en" dir="ltr">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Tab Bar - Scroll Effect Hide (Footer Priority)</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+
</head>
16+
17+
<body>
18+
<ion-app>
19+
<div class="ion-page">
20+
<ion-header>
21+
<ion-toolbar>
22+
<ion-title>Header</ion-title>
23+
</ion-toolbar>
24+
</ion-header>
25+
<ion-content>
26+
<div class="ion-padding" style="height: 3000px">
27+
<h1>Content</h1>
28+
<p>Scroll down. The tab bar should NOT hide because it is nested inside ion-footer which has no scrollEffect. The footer controls hide behavior — the tab bar defers to it.</p>
29+
</div>
30+
</ion-content>
31+
<!--
32+
The footer has no scrollEffect prop.
33+
The tab bar has scrollEffect="hide" but should be ignored
34+
because the parent footer takes priority.
35+
-->
36+
<ion-footer id="footer">
37+
<ion-tab-bar id="tabBar" slot="bottom" scroll-effect="hide">
38+
<ion-tab-button>
39+
<ion-icon name="home-outline"></ion-icon>
40+
<ion-label>Home</ion-label>
41+
</ion-tab-button>
42+
<ion-tab-button>
43+
<ion-icon name="heart-outline"></ion-icon>
44+
<ion-label>Favorites</ion-label>
45+
</ion-tab-button>
46+
<ion-tab-button>
47+
<ion-icon name="search-outline"></ion-icon>
48+
<ion-label>Search</ion-label>
49+
</ion-tab-button>
50+
</ion-tab-bar>
51+
</ion-footer>
52+
</div>
53+
</ion-app>
54+
</body>
55+
</html>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { expect } from '@playwright/test';
2+
import { configs, test } from '@utils/test/playwright';
3+
4+
/**
5+
* When ion-tab-bar is nested inside ion-footer, the footer controls
6+
* hide behavior. The tab bar's scrollEffect prop is ignored regardless
7+
* of whether the parent footer has a scrollEffect set.
8+
*/
9+
configs({ modes: ['ios', 'md', 'ionic-ios', 'ionic-md'], directions: ['ltr'] }).forEach(({ title, config }) => {
10+
test.describe(title('tab-bar: scroll-effect-hide footer priority'), () => {
11+
test('should not hide the tab bar when nested inside a footer without scrollEffect', async ({ page }) => {
12+
await page.goto('/src/components/tab-bar/test/scroll-effect-hide-footer-priority', config);
13+
14+
const tabBar = page.locator('#tabBar');
15+
const content = page.locator('ion-content');
16+
17+
await expect(tabBar).not.toHaveClass(/tab-bar-scroll-hidden/);
18+
19+
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
20+
await page.waitForChanges();
21+
22+
// Tab bar should still not be hidden — footer takes priority
23+
await expect(tabBar).not.toHaveClass(/tab-bar-scroll-hidden/);
24+
await expect(tabBar).not.toHaveAttribute('inert');
25+
});
26+
});
27+
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { expect } from '@playwright/test';
2+
import { configs, test } from '@utils/test/playwright';
3+
4+
configs({ modes: ['ios', 'md', 'ionic-ios', 'ionic-md'], directions: ['ltr'] }).forEach(({ title, config }) => {
5+
test.describe(title('tab-bar: scroll-effect-hide'), () => {
6+
test('should have the scroll-effect-hide class when scrollEffect is set to hide', async ({ page }) => {
7+
await page.goto('/src/components/tab-bar/test/scroll-effect-hide', config);
8+
9+
const tabBar = page.locator('ion-tab-bar');
10+
await expect(tabBar).toHaveClass(/tab-bar-scroll-effect-hide/);
11+
});
12+
13+
test('should hide the tab bar when scrolling down', async ({ page }) => {
14+
await page.goto('/src/components/tab-bar/test/scroll-effect-hide', config);
15+
16+
const tabBar = page.locator('ion-tab-bar');
17+
const content = page.locator('ion-content').first();
18+
19+
await expect(tabBar).not.toHaveClass(/tab-bar-scroll-hidden/);
20+
21+
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
22+
await page.locator('ion-tab-bar.tab-bar-scroll-hidden').waitFor();
23+
24+
await expect(tabBar).toHaveClass(/tab-bar-scroll-hidden/);
25+
await expect(tabBar).toHaveAttribute('inert', '');
26+
});
27+
28+
test('should show the tab bar again when scrolling back to the top', async ({ page }) => {
29+
await page.goto('/src/components/tab-bar/test/scroll-effect-hide', config);
30+
31+
const tabBar = page.locator('ion-tab-bar');
32+
const content = page.locator('ion-content').first();
33+
34+
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
35+
await page.locator('ion-tab-bar.tab-bar-scroll-hidden').waitFor();
36+
37+
await content.evaluate((el: HTMLIonContentElement) => el.scrollToTop(0));
38+
await page.locator('ion-tab-bar:not(.tab-bar-scroll-hidden)').waitFor();
39+
40+
await expect(tabBar).not.toHaveClass(/tab-bar-scroll-hidden/);
41+
await expect(tabBar).not.toHaveAttribute('inert');
42+
});
43+
});
44+
});

0 commit comments

Comments
 (0)