diff --git a/core/src/components/datetime/datetime.tsx b/core/src/components/datetime/datetime.tsx index e73cd55e0b8..f36cd66718d 100644 --- a/core/src/components/datetime/datetime.tsx +++ b/core/src/components/datetime/datetime.tsx @@ -135,6 +135,12 @@ export class Datetime implements ComponentInterface { private todayParts!: DatetimeParts; private defaultParts!: DatetimeParts; private loadTimeout: ReturnType | undefined; + /** + * Set true only by `visibleCallback`. Lets `hiddenCallback` ignore the + * synthetic "not intersecting" entry IntersectionObserver fires on + * `observe()` when the host mounts offscreen. + */ + private hasBeenIntersecting = false; private prevPresentation: string | null = null; @@ -1097,6 +1103,7 @@ export class Datetime implements ComponentInterface { this.clearFocusVisible = undefined; } this.loadTimeoutCleanup(); + this.hasBeenIntersecting = false; } /** @@ -1140,8 +1147,23 @@ export class Datetime implements ComponentInterface { return; } + this.markReady(); + }; + + private markReady = () => { + if (this.el.classList.contains('datetime-ready')) { + return; + } this.initializeListeners(); + /** + * TODO FW-2793: Datetime needs a frame to ensure that it + * can properly scroll contents into view. As a result + * we hide the scrollable content until after that frame + * so users do not see the content quickly shifting. The downside + * is that the content will pop into view a frame after. Maybe there + * is a better way to handle this? + */ writeTask(() => { this.el.classList.add('datetime-ready'); }); @@ -1170,19 +1192,8 @@ export class Datetime implements ComponentInterface { return; } - this.initializeListeners(); - - /** - * TODO FW-2793: Datetime needs a frame to ensure that it - * can properly scroll contents into view. As a result - * we hide the scrollable content until after that frame - * so users do not see the content quickly shifting. The downside - * is that the content will pop into view a frame after. Maybe there - * is a better way to handle this? - */ - writeTask(() => { - this.el.classList.add('datetime-ready'); - }); + this.hasBeenIntersecting = true; + this.markReady(); }; const visibleIO = new IntersectionObserver(visibleCallback, { threshold: 0.01, root: el }); @@ -1222,6 +1233,12 @@ export class Datetime implements ComponentInterface { return; } + // Ignore the initial "not intersecting" entry IntersectionObserver fires on observe(). + if (!this.hasBeenIntersecting) { + return; + } + this.hasBeenIntersecting = false; + this.destroyInteractionListeners(); /** diff --git a/core/src/components/datetime/test/basic/datetime.e2e.ts b/core/src/components/datetime/test/basic/datetime.e2e.ts index 4865e243092..bbc6033374f 100644 --- a/core/src/components/datetime/test/basic/datetime.e2e.ts +++ b/core/src/components/datetime/test/basic/datetime.e2e.ts @@ -440,10 +440,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => */ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test.describe(title('datetime: IO fallback'), () => { - test('should become ready even if IntersectionObserver never reports visible', async ({ page, skip }, testInfo) => { - // TODO(FW-7284): Re-enable on WebKit after determining why it fails - skip.browser('webkit', 'Wheel is not available in WebKit'); - + test('should become ready even if IntersectionObserver never reports visible', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/30706',