diff --git a/core/src/components/content/content.tsx b/core/src/components/content/content.tsx index 361939f7c27..cd36ea154bb 100644 --- a/core/src/components/content/content.tsx +++ b/core/src/components/content/content.tsx @@ -25,6 +25,8 @@ import type { ScrollBaseDetail, ScrollDetail } from './content-interface'; }) export class Content implements ComponentInterface { private watchDog: ReturnType | null = null; + private mutationObserver: MutationObserver | null = null; + private resizeObserver: ResizeObserver | null = null; private isScrolling = false; private lastScroll = 0; private queued = false; @@ -168,10 +170,12 @@ export class Content implements ComponentInterface { closestTabs.addEventListener('ionTabBarLoaded', this.tabsLoadCallback); } } + this.connectObservers(); } disconnectedCallback() { this.onScrollEnd(); + this.disconnectObservers(); if (hasLazyBuild(this.el)) { /** @@ -420,6 +424,85 @@ export class Content implements ComponentInterface { return promise; } + /** + * We need to observe the parent element to detect when + * or elements are added/removed + * or resized. This ensures the content offset is recalculated + * dynamically. + */ + private connectObservers() { + if (!Build.isBrowser) { + return; + } + + const parent = this.el.parentElement; + if (!parent) { + return; + } + + if ('ResizeObserver' in window) { + this.resizeObserver = new ResizeObserver(() => { + this.resize(); + }); + } + + if ('MutationObserver' in window) { + this.mutationObserver = new MutationObserver((mutations) => { + let shouldUpdate = false; + + for (const mutation of mutations) { + if (mutation.type === 'childList') { + mutation.addedNodes.forEach((node: any) => { + if (node.tagName === 'ION-HEADER' || node.tagName === 'ION-FOOTER') { + shouldUpdate = true; + } + }); + + mutation.removedNodes.forEach((node: any) => { + if (node.tagName === 'ION-HEADER' || node.tagName === 'ION-FOOTER') { + shouldUpdate = true; + } + }); + } + } + + if (shouldUpdate) { + this.refreshResizeObserver(); + this.resize(); + } + }); + + this.mutationObserver.observe(parent, { childList: true }); + } + + this.refreshResizeObserver(); + } + + private disconnectObservers() { + if (this.mutationObserver) { + this.mutationObserver.disconnect(); + this.mutationObserver = null; + } + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + this.resizeObserver = null; + } + } + + private refreshResizeObserver() { + if (!this.resizeObserver || !this.el.parentElement) { + return; + } + + this.resizeObserver.disconnect(); + + const headers = this.el.parentElement.querySelectorAll('ion-header'); + const footers = this.el.parentElement.querySelectorAll('ion-footer'); + + headers.forEach((header) => this.resizeObserver!.observe(header)); + footers.forEach((footer) => this.resizeObserver!.observe(footer)); + } + private onScrollStart() { this.isScrolling = true; this.ionScrollStart.emit({ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts b/core/src/components/content/test/auto-offset/content.e2e.ts new file mode 100644 index 00000000000..0580e3e7a12 --- /dev/null +++ b/core/src/components/content/test/auto-offset/content.e2e.ts @@ -0,0 +1,48 @@ +import { expect } from '@playwright/test'; +import { test, configs } from '@utils/test/playwright'; + +configs({ modes: ['ios'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('content: auto offset'), () => { + test('should not have visual regressions', async ({ page }) => { + await page.goto(`/src/components/content/test/auto-offset`, config); + await page.setIonViewport(); + await expect(page).toHaveScreenshot(screenshot(`content-auto-offset-initial`)); + }); + + test('should update offsets when header height changes', async ({ page }) => { + await page.goto(`/src/components/content/test/auto-offset`, config); + await page.setIonViewport(); + + const content = page.locator('ion-content'); + const before = await content.evaluate((el: HTMLElement) => getComputedStyle(el).getPropertyValue('--offset-top')); + + await page.click('#expand-header-btn'); + + await expect(content).not.toHaveCSS('--offset-top', before); + + await expect(page).toHaveScreenshot(screenshot(`content-auto-offset-header-updated`)); + }); + + test('should update offsets when footer height changes', async ({ page }) => { + await page.goto(`/src/components/content/test/auto-offset`, config); + await page.setIonViewport(); + + const content = page.locator('ion-content'); + const before = await content.evaluate((el: HTMLElement) => + getComputedStyle(el).getPropertyValue('--offset-bottom') + ); + + await page.click('#expand-footer-btn'); + + await expect(content).not.toHaveCSS('--offset-bottom', before); + + const after = await content.evaluate((el: HTMLElement) => + getComputedStyle(el).getPropertyValue('--offset-bottom') + ); + + expect(after).not.toBe(before); + + await expect(page).toHaveScreenshot(screenshot(`content-auto-offset-footer-updated`)); + }); + }); +}); diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-ltr-Mobile-Chrome-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-ltr-Mobile-Chrome-win32.png new file mode 100644 index 00000000000..7e2f14801d8 Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-ltr-Mobile-Chrome-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-ltr-Mobile-Firefox-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-ltr-Mobile-Firefox-win32.png new file mode 100644 index 00000000000..4d41e4c04be Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-ltr-Mobile-Firefox-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-ltr-Mobile-Safari-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-ltr-Mobile-Safari-win32.png new file mode 100644 index 00000000000..b8a4889e287 Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-ltr-Mobile-Safari-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-rtl-Mobile-Chrome-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-rtl-Mobile-Chrome-win32.png new file mode 100644 index 00000000000..8a580c84e55 Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-rtl-Mobile-Chrome-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-rtl-Mobile-Firefox-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-rtl-Mobile-Firefox-win32.png new file mode 100644 index 00000000000..a3d957bccec Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-rtl-Mobile-Firefox-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-rtl-Mobile-Safari-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-rtl-Mobile-Safari-win32.png new file mode 100644 index 00000000000..16f082d3e1f Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-footer-updated-ios-rtl-Mobile-Safari-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-ltr-Mobile-Chrome-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-ltr-Mobile-Chrome-win32.png new file mode 100644 index 00000000000..27802fc0b87 Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-ltr-Mobile-Chrome-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-ltr-Mobile-Firefox-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-ltr-Mobile-Firefox-win32.png new file mode 100644 index 00000000000..45e49a4349d Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-ltr-Mobile-Firefox-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-ltr-Mobile-Safari-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-ltr-Mobile-Safari-win32.png new file mode 100644 index 00000000000..55fefac2ffb Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-ltr-Mobile-Safari-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-rtl-Mobile-Chrome-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-rtl-Mobile-Chrome-win32.png new file mode 100644 index 00000000000..70329b76d11 Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-rtl-Mobile-Chrome-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-rtl-Mobile-Firefox-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-rtl-Mobile-Firefox-win32.png new file mode 100644 index 00000000000..65ec1aaff2c Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-rtl-Mobile-Firefox-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-rtl-Mobile-Safari-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-rtl-Mobile-Safari-win32.png new file mode 100644 index 00000000000..af5e5f54c02 Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-header-updated-ios-rtl-Mobile-Safari-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-ltr-Mobile-Chrome-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-ltr-Mobile-Chrome-win32.png new file mode 100644 index 00000000000..7dcaf00bd28 Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-ltr-Mobile-Chrome-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-ltr-Mobile-Firefox-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-ltr-Mobile-Firefox-win32.png new file mode 100644 index 00000000000..d8fc34653d1 Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-ltr-Mobile-Firefox-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-ltr-Mobile-Safari-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-ltr-Mobile-Safari-win32.png new file mode 100644 index 00000000000..a811b80e3f1 Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-ltr-Mobile-Safari-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-rtl-Mobile-Chrome-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-rtl-Mobile-Chrome-win32.png new file mode 100644 index 00000000000..879032432ff Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-rtl-Mobile-Chrome-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-rtl-Mobile-Firefox-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-rtl-Mobile-Firefox-win32.png new file mode 100644 index 00000000000..9abf3a4a940 Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-rtl-Mobile-Firefox-win32.png differ diff --git a/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-rtl-Mobile-Safari-win32.png b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-rtl-Mobile-Safari-win32.png new file mode 100644 index 00000000000..71933cb0939 Binary files /dev/null and b/core/src/components/content/test/auto-offset/content.e2e.ts-snapshots/content-auto-offset-initial-ios-rtl-Mobile-Safari-win32.png differ diff --git a/core/src/components/content/test/auto-offset/index.html b/core/src/components/content/test/auto-offset/index.html new file mode 100644 index 00000000000..6c5727f5d75 --- /dev/null +++ b/core/src/components/content/test/auto-offset/index.html @@ -0,0 +1,58 @@ + + + + + Content - Auto Offset + + + + + + + + + + + + + Auto Offset Test + + + + + + +

+ +
+ + + + + Footer + + +
+ + +