JedWatson / react-select

The Select Component for React.js
https://react-select.com/
MIT License
27.44k stars 4.1k forks source link

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

Open mbeckem opened 7 months ago

mbeckem commented 7 months ago

Hi,

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

Reproduction

Notes

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 commented 7 months ago

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

vpenchev-noteris commented 7 months ago

+1

shotasenga commented 1 month ago

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 commented 1 month ago

@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();
       }