Repository: ionic-team/ionic-framework
File: core/src/components/datetime/datetime.tsx (TypeScript)

Code before:

  /**
   * When the presentation is changed, all calendar content is recreated,
   * so we need to re-init behavior with the new elements.
   */
  componentDidRender() {
    const { presentation, prevPresentation } = this;

    if (prevPresentation === null) {
      this.prevPresentation = presentation;
      return;
    }

    if (presentation === prevPresentation) {
      return;
    }
    this.prevPresentation = presentation;

    this.destroyInteractionListeners();
    if (this.destroyOverlayListener !== undefined) {
      this.destroyOverlayListener();
    }

    this.initializeListeners();

    requestAnimationFrame(() => {
      this.ionRender.emit();
    });
  }

  /**
   * When doing subsequent presentations of an inline
   * overlay, the IO callback will fire again causing
   * the calendar to go back one month. We need to listen
   * for the presentation of the overlay so we can properly
   * cancel that IO callback.
   */
  private initializeOverlayListener = () => {
    const overlay = this.el.closest('ion-popover, ion-modal');
    if (overlay === null) {
      return;
    }

    const overlayListener = () => {
      this.overlayIsPresenting = true;
    };

    overlay.addEventListener('willPresent', overlayListener);

    this.destroyOverlayListener = () => {
      overlay.removeEventListener('willPresent', overlayListener);

Review comment (suggestion):
Personally I don't love the idea of adding a private event just to capture when a component re-renders, but I was not able to think of any other solutions.

Example of the issue:

1. Datetime in a popover starts with `presentation="date"` (calendar grid).
2. User clicks the time portion of the datetime button.
3. Datetime updates `presentation` to `"time"`.
4. `popoverEl.present()` is called. 
5. At this point the datetime component has not fully re-rendered to the time wheel. As a result, the following code gets the dimensions of the calendar grid, not the wheel picker: https://github.com/ionic-team/ionic-framework/blob/b7afcb0f0c36d84f3b4d65844df28e6293bc1ea5/core/src/components/popover/utils.ts#L58
6. The datetime re-renders and is displayed as a time wheel, but the popover position is not correct.

I did look into doing something similar to this: https://github.com/ionic-team/ionic-framework/blob/5a606de14b088316905bf3ece478ae8ab5ac7211/core/src/components/datetime/test/utils/month-did-change-event.js
But that did not seem to work either. I listened for when the `datetime-presentation-time` class was added, but that did not mean that the component was visually updated yet. Open to other ideas!

Code after:

  /**
   * When the presentation is changed, all calendar content is recreated,
   * so we need to re-init behavior with the new elements.
   */
  componentDidRender() {
    const { presentation, prevPresentation } = this;

    if (prevPresentation === null) {
      this.prevPresentation = presentation;
      return;
    }

    if (presentation === prevPresentation) {
      return;
    }
    this.prevPresentation = presentation;

    this.destroyInteractionListeners();
    if (this.destroyOverlayListener !== undefined) {
      this.destroyOverlayListener();
    }

    this.initializeListeners();

    raf(() => {
      this.ionRender.emit();
    });
  }

  /**
   * When doing subsequent presentations of an inline
   * overlay, the IO callback will fire again causing
   * the calendar to go back one month. We need to listen
   * for the presentation of the overlay so we can properly
   * cancel that IO callback.
   */
  private initializeOverlayListener = () => {
    const overlay = this.el.closest('ion-popover, ion-modal');
    if (overlay === null) {
      return;
    }

    const overlayListener = () => {
      this.overlayIsPresenting = true;
    };

    overlay.addEventListener('willPresent', overlayListener);

    this.destroyOverlayListener = () => {
      overlay.removeEventListener('willPresent', overlayListener);