ionic-framework icon indicating copy to clipboard operation
ionic-framework copied to clipboard

bug: react, mixing nested controller and inline overlays causes event handlers to not get bound

Open jonesdhtx opened this issue 1 year ago • 5 comments

Prerequisites

Ionic Framework Version

v7.x

Current Behavior

  • Create and present a modal using controller method
const [present, dismiss] = useIonModal(ModalExample)
...
  function openModal() {
    present();
  }
...  
  • From within the modal have a button that triggers an IonPopover
...
  <IonButton id="click-trigger-2">Trigger Popover</IonButton>
  <IonPopover dismissOnSelect trigger="click-trigger-2" triggerAction="click">
    <IonContent class="ion-padding">
      Hello World!
      <IonButton expand="block" onClick={onClick}>I'm clickable</IonButton>
    </IonContent>
  </IonPopover>
...
  • For any element within that IonPopover (eg IonItem, IonButton, etc) any onClick handler will not get invoked. NOTE: However it's interesting that the click event is received by the IonPopover itself b/c setting dismissOnSelect does in fact dismiss the popover upon click.
...
  function onClick (ev: React.MouseEvent) {
    console.log('onPopoverItemClick From Modal!!')  // <===== this is never called
  }
...

Expected Behavior

For any element within an IonPopover (eg IonItem, IonButton, etc) it's onClick handler should be invoked when clicked.

Steps to Reproduce

Source code for demonstrating the issue: src/pagesHome.tsx

To Reproduce Issue:

  1. Get and run the code reproduction project from git hub using provided link
  2. Click on the "Open Modal" button
  3. Click on the "Trigger Popover" button
  4. Click on the "I'm Clickable" button inside the popover - note the onClick handler is not invoked and the resulting console log is not displayed

NOTE: From the main page (outside of the modal) you can click the "Trigger Popover" button and the Button within it works as expected.

Code Reproduction URL

https://github.com/jonesdhtx/ionic-test-case-1

Ionic Info

Ionic:

Ionic CLI : 7.2.0 Ionic Framework : @ionic/react 7.6.4

Capacitor:

Capacitor CLI : 5.6.0 @capacitor/android : not installed @capacitor/core : 5.6.0 @capacitor/ios : not installed

Utility:

cordova-res : not installed globally native-run : 2.0.0

System:

NodeJS : v16.14.0 npm : 8.12.1 OS : macOS Unknown

Additional Information

No response

jonesdhtx avatar Jan 12 '24 02:01 jonesdhtx

I managed to work around this by using a controller modal inside the controller modal, see https://github.com/ionic-team/ionic-framework/issues/28840

Maybe you could use useIonPopover, to remedy the issue?

In the documentation on the popover I read: "In addition, nested popovers are not compatible with the controller approach because the popover is automatically added to the root of your application when the create method is called."

I am not sure whether this applies to nested popovers nested in a popover or to nested popovers nested in ANY controller component (like modals).

HTH

ldikmans avatar Jan 17 '24 10:01 ldikmans

Thanks for sharing that @ldikmans . A workaround similar to what you did in the link below gets me functional while framework issue is being found/fixed: https://github.com/ldikmans/ionic-react-nested-modal-bug/blob/main/src/components/InlineModal2.tsx#L31-L39

jonesdhtx avatar Jan 17 '24 14:01 jonesdhtx

Hi everyone, I can reproduce this. The problem here appears to be due to mixing controller vs. inline usage. In particular, presenting an inline popover from a controller modal causes events to not be bound to the contents of the popover.

Using both an inline modal and inline popover avoids this issue. Similarly, using both a controller modal and controller popover avoids the issue.

This issue only reproduces in React. Both Angular and Vue allow you to mix nested controller and inline overlays as expected.

edit: This also applies to any element inside of the nested popover in this case. For example click handlers for regular button elements are not bound either.

liamdebeasi avatar Feb 14 '24 13:02 liamdebeasi

It appears that React's synthetic event dispatch system is returning early here when clicking the button in the popover. The root container is a parent of a Portal. It expects that the event will bubble through the Portal. However, it looks like the controller/hook overlay uses a Portal, but the inline overlay does not. This could be contributing to the problem.

liamdebeasi avatar Feb 14 '24 14:02 liamdebeasi

@liamdebeasi thanks for clarifying 👍

nicolae536 avatar Feb 27 '24 12:02 nicolae536