Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Touch events do not work when <Select /> is mounted in a shadow root #5824

Open
mbeckem opened this issue Dec 6, 2023 · 5 comments
Open
Labels
issue/bug-unconfirmed Issues that describe a bug that hasn't been confirmed by a maintainer yet

Comments

@mbeckem
Copy link

mbeckem commented Dec 6, 2023

Hi,

the current approach to touch event handling does not work well when <Select /> is used with a shadow root.

Reproduction

Notes

  • The menu actually opens internally, but it is hidden immediately because the onTouchEnd event handler categorizes the touch event as outside the select control (this results in a blurInput() call)
  • When inside a shadow root, the event handler registered on the document will not observe the original .target, but the entire shadow dom. This is obviously not contained in the original node, which causes the behavior in this case.
  • A more stable alternative to .contains(...) is to use event.composedPath() instead. See also this blog post.

I've had trouble creating a unit test for this bug (I could not get touch events inside shadow DOM to work with jest). I hope that the sample above will be sufficient.
I think a fix would look as simple as replacing node.contains(event.target) with event.composedPath().includes(node). I'll gladly create a PR for this.

Kind regards

@mbeckem mbeckem added the issue/bug-unconfirmed Issues that describe a bug that hasn't been confirmed by a maintainer yet label Dec 6, 2023
@mbeckem
Copy link
Author

mbeckem commented Dec 6, 2023

Related: PR #5737 attempts to fix this issue, but I think the approach taken is not optimal

@vpenchev-noteris
Copy link

+1

@shotasenga
Copy link

Tho it's hacky, there is a workaround to this. If you cancel the touch events before they reach the react-select components, you can enforce it to use the mouse events instead.

<div onTouchEndCapture={(e) => e.stopPropagation()}>
  <Select ..../>
</div>

@mbeckem
Copy link
Author

mbeckem commented Jun 14, 2024

@shotasenga nice idea, i didn't think of that. We fixed that on our side by patching the select component via pnpm:

diff --git a/dist/Select-49a62830.esm.js b/dist/Select-49a62830.esm.js
index bed08498732b023f350d24a62728371af6a4dace..23a5fd969db4d0190cc4b6f05417c9594ddc2931 100644
--- a/dist/Select-49a62830.esm.js
+++ b/dist/Select-49a62830.esm.js
@@ -1498,7 +1498,7 @@ var Select = /*#__PURE__*/function (_Component) {
       // close the menu if the user taps outside
       // we're checking on event.target here instead of event.currentTarget, because we want to assert information
       // on events on child elements, not the document (which we've attached this handler to).
-      if (_this.controlRef && !_this.controlRef.contains(event.target) && _this.menuListRef && !_this.menuListRef.contains(event.target)) {
+      if (_this.controlRef && !event.composedPath().includes(_this.controlRef) && _this.menuListRef && !event.composedPath().includes(_this.menuListRef)) {
         _this.blurInput();
       }

@dsternlicht
Copy link

Tho it's hacky, there is a workaround to this. If you cancel the touch events before they reach the react-select components, you can enforce it to use the mouse events instead.

<div onTouchEndCapture={(e) => e.stopPropagation()}>
  <Select ..../>
</div>

You are my hero!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
issue/bug-unconfirmed Issues that describe a bug that hasn't been confirmed by a maintainer yet
Projects
None yet
Development

No branches or pull requests

4 participants