Skip to content

fix(datetime): prevent hidden-state observer from tearing down ready class on initial entry - #31108

Merged
ShaneK merged 1 commit into
mainfrom
FW-7284
May 1, 2026
Merged

fix(datetime): prevent hidden-state observer from tearing down ready class on initial entry#31108
ShaneK merged 1 commit into
mainfrom
FW-7284

Conversation

@ShaneK

@ShaneK ShaneK commented Apr 30, 2026

Copy link
Copy Markdown
Member

Issue number: internal


What is the current behavior?

ion-datetime runs two IntersectionObservers: one to detect when the host becomes visible (which adds datetime-ready) and one to detect when it becomes hidden (which removes the class and tears down listeners). When the host mounts offscreen, both observers receive an initial "not intersecting" entry on observe(). The hidden-state observer treats that initial entry as a real visible-to-hidden transition, queues a writeTask to remove datetime-ready, and races the layout-based fallback (ensureReadyIfVisible) that adds the class after 100ms. On WebKit the remove wins often enough that the e2e test for the fallback (added in #30793 to fix #30706) had to be skipped on Mobile Safari. Anything in production that adds datetime-ready outside of a real isIntersecting: true event is exposed to the same race.

What is the new behavior?

A hasBeenIntersecting flag is set true only when visibleCallback observes isIntersecting: true. The hidden-state observer's teardown is gated on this flag, so the synthetic initial "not intersecting" entry is ignored. The flag is reset when the host actually transitions to hidden and on disconnectedCallback. The previously duplicated init-listeners + ready-class block is consolidated into a single markReady helper. The WebKit skip on the IO-fallback e2e test has been removed.

Does this introduce a breaking change?

  • Yes
  • No

Other information

The asymmetry where ensureReadyIfVisible (the layout fallback) deliberately does NOT set hasBeenIntersecting is load-bearing: the flag must reflect a real observer signal, not a fallback-driven write, otherwise the bug returns. This is called out at the guard site so future cleanups don't undo it.

This test was most likely to fail in docker testing Linux Webkit with --repeat-each=20 because it was pretty flaky. I was able to force it to fail under these conditions and, after fixing it, it no longer failed.

Relevant Preview Link:

@ShaneK
ShaneK requested a review from a team as a code owner April 30, 2026 19:51
@ShaneK
ShaneK requested a review from thetaPC April 30, 2026 19:51
@vercel

vercel Bot commented Apr 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ionic-framework Ready Ready Preview, Comment Apr 30, 2026 7:52pm

Request Review

@github-actions github-actions Bot added the package: core @ionic/core package label Apr 30, 2026

@thetaPC thetaPC left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ShaneK
ShaneK added this pull request to the merge queue May 1, 2026
Merged via the queue into main with commit 30b479a May 1, 2026
52 checks passed
@ShaneK
ShaneK deleted the FW-7284 branch May 1, 2026 16:38
pull Bot pushed a commit to LoadsAForks/ionic-framework that referenced this pull request Aug 7, 2026
…-team#31335)

Issue number: resolves ionic-team#31155, resolves ionic-team#31143

---------

## What is the current behavior?

Currently, an `ion-datetime` inside a modal or popover shows the wrong
month once the overlay is reopened. The selected day isn't visible, the
previous month button does nothing, and picking a day from the grid
lands on an unrelated date.

Overlays move their host element into `ion-app` when presenting and back
to its original position when dismissing, which disconnects and
reconnects the datetime. `disconnectedCallback` reset
`hasBeenIntersecting` during that move, so by the time the hidden-state
`IntersectionObserver` entry arrived, `hiddenCallback` mistook the
dismissal for the synthetic initial entry and returned early. That left
`datetime-ready` on the host, so on the next present `markReady` saw the
class and returned without re-centering the calendar on the working
month, and the browser had already reset `scrollLeft` to 0 while the
overlay was hidden. `scrollLeft: 0` renders the previous month's grid
while the header still names the working month, which is what produces
all three symptoms.

## What is the new behavior?

With this change, `disconnectedCallback` no longer resets
`hasBeenIntersecting`. That flag tracks the observers, and the observers
are only created in `componentDidLoad` and never re-created on
reconnect, so a DOM move has no business clearing it. `hiddenCallback`
now sees the real hidden transition on dismiss and tears down as it did
before ionic-team#31108, which lets `markReady` run again on the next present and
re-center the calendar.

## Does this introduce a breaking change?

- [ ] Yes
- [X] No

## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

This regression was introduced in ionic-team#31108. That PR needed the flag to
make `hiddenCallback` ignore the synthetic initial entry, but the
`disconnectedCallback` reset it also added had no job and broke the
overlay case. Before ionic-team#31108, `hiddenCallback` had no guard at all and
always removed `datetime-ready` on dismiss, so this restores the
behavior that shipped for all of v8..

- [Relevant test screen -
iOS](https://ionic-framework-git-fix-31155-ionic1.vercel.app/src/components/datetime-button/test/overlays?ionic:mode=ios)
- [Relevant test screen -
MD](https://ionic-framework-git-fix-31155-ionic1.vercel.app/src/components/datetime-button/test/overlays?ionic:mode=md)

To reproduce: open the "Modal - Default" picker, dismiss it, then open
it again. On `main` the grid shows February while the header reads March
2022.

---------

Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: core @ionic/core package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: IonDateTime sometimes show invisible calendar

2 participants