diff --git a/core/src/components/content/content.tsx b/core/src/components/content/content.tsx index 947c4104d5..29d246bc61 100644 --- a/core/src/components/content/content.tsx +++ b/core/src/components/content/content.tsx @@ -47,6 +47,9 @@ export class Content implements ComponentInterface { /** Watches for dynamic header/footer changes in parent element */ private parentMutationObserver?: MutationObserver; + /** Watches for dynamic tab bar changes in ion-tabs */ + private tabsMutationObserver?: MutationObserver; + private tabsElement: HTMLElement | null = null; private tabsLoadCallback?: () => void; @@ -213,6 +216,20 @@ export class Content implements ComponentInterface { }); this.parentMutationObserver.observe(parent, { childList: true }); } + + // Watch for dynamic tab bar changes in ion-tabs (common in Angular conditional rendering) + const tabs = this.el.closest('ion-tabs'); + if (tabs && !this.tabsMutationObserver && win !== undefined && 'MutationObserver' in win) { + this.tabsMutationObserver = new MutationObserver(() => { + const prevHasFooter = this.hasFooter; + this.updateSiblingDetection(); + // Only trigger re-render if footer detection actually changed + if (prevHasFooter !== this.hasFooter) { + forceUpdate(this); + } + }); + this.tabsMutationObserver.observe(tabs, { childList: true }); + } } /** @@ -264,9 +281,11 @@ export class Content implements ComponentInterface { disconnectedCallback() { this.onScrollEnd(); - // Clean up mutation observer to prevent memory leaks + // Clean up mutation observers to prevent memory leaks this.parentMutationObserver?.disconnect(); this.parentMutationObserver = undefined; + this.tabsMutationObserver?.disconnect(); + this.tabsMutationObserver = undefined; if (hasLazyBuild(this.el)) { /** diff --git a/core/src/components/content/test/safe-area/content.e2e.ts b/core/src/components/content/test/safe-area/content.e2e.ts index de884aad1b..51a818471b 100644 --- a/core/src/components/content/test/safe-area/content.e2e.ts +++ b/core/src/components/content/test/safe-area/content.e2e.ts @@ -164,5 +164,53 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { // Should have safe-area-top again await expect(content).toHaveClass(/safe-area-top/, { timeout: 1000 }); }); + + test('content inside ion-tabs with tab bar should not have safe-area-bottom', async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/30900', + }); + + const content = page.locator('#content-dynamic-tabs'); + // Tab bar is present, so content should not have safe-area-bottom + await expect(content).not.toHaveClass(/safe-area-bottom/); + }); + + test('dynamic tab bar removal should update safe-area classes', async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/30900', + }); + + const content = page.locator('#content-dynamic-tabs'); + + // Initially tab bar is present, so no safe-area-bottom + await expect(content).not.toHaveClass(/safe-area-bottom/); + + // Remove tab bar + await page.evaluate(() => (window as any).removeTabBar()); + + // Should have safe-area-bottom now + await expect(content).toHaveClass(/safe-area-bottom/, { timeout: 1000 }); + }); + + test('dynamic tab bar addition should update safe-area classes', async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/30900', + }); + + const content = page.locator('#content-dynamic-tabs'); + + // Remove tab bar first + await page.evaluate(() => (window as any).removeTabBar()); + await expect(content).toHaveClass(/safe-area-bottom/, { timeout: 1000 }); + + // Add tab bar back + await page.evaluate(() => (window as any).addTabBar()); + + // Should not have safe-area-bottom anymore + await expect(content).not.toHaveClass(/safe-area-bottom/, { timeout: 1000 }); + }); }); }); diff --git a/core/src/components/content/test/safe-area/index.html b/core/src/components/content/test/safe-area/index.html index 1390ac9e9c..6d9520b379 100644 --- a/core/src/components/content/test/safe-area/index.html +++ b/core/src/components/content/test/safe-area/index.html @@ -163,6 +163,22 @@ + +
Content with dynamic tab bar
+