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

ClickAwayListener triggers on clicking browser scrollbar #15724

Closed
2 tasks done
Umerbhat opened this issue May 16, 2019 · 18 comments · Fixed by #15743
Closed
2 tasks done

ClickAwayListener triggers on clicking browser scrollbar #15724

Umerbhat opened this issue May 16, 2019 · 18 comments · Fixed by #15743
Labels
component: ClickAwayListener The React component good first issue Great for first contributions. Enable to learn the contribution process. new feature New feature or request

Comments

@Umerbhat
Copy link
Contributor

Umerbhat commented May 16, 2019

  • This is not a v0.x issue.
  • I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior 🤔

When Menu is opened, if the user tries to scroll using a browser scrollbar. The Menu shouldn't close.

Current Behavior 😯

When Menu is opened, if the user tries to scroll using a browser scrollbar. The Menu closes.

Steps to Reproduce 🕹

Link: https://material-ui.com/demos/menus/#menulist-composition

  1. Navigate to mentioned link, Click on TOGGLE MENU GROW button
  2. The menu opens up under the button
  3. When the menu is open, try to scroll by clicking browser scrollbar
  4. As soon u leave click from the scrollbar, Menu closes itself

Context 🔦

When the menu has too many items if the user wants to select the last item, he needs to scroll. But this issue blocks a user from selecting the last options of the menu.

@eps1lon eps1lon added component: menu This is the name of the generic UI component, not the React module! discussion labels May 16, 2019
@eps1lon
Copy link
Member

eps1lon commented May 16, 2019

We would need to detect in the ClickAwayListener if the click happened on the scrollbars. I don't know if this is possible.

Could be so kind and create a playground with a Menu with just many items so that we can verify whether this is an issue with this particular demo or the base Menu.

@Umerbhat
Copy link
Contributor Author

We would need to detect in the ClickAwayListener if the click happened on the scrollbars. I don't know if this is possible.

Could be so kind and create a playground with a Menu with just many items so that we can verify whether this is an issue with this particular demo or the base Menu.

I have checked code in ClickAwayListener. No condition is written to handle scrollbar click

@oliviertassinari oliviertassinari changed the title Menu gets closed on clicking browser scrollbar ClickAwayListener triggers on clicking browser scrollbar May 16, 2019
@oliviertassinari oliviertassinari added component: ClickAwayListener The React component and removed component: menu This is the name of the generic UI component, not the React module! labels May 16, 2019
@oliviertassinari
Copy link
Member

oliviertassinari commented May 16, 2019

@Umerbhat I have found a similar thread on kentor/react-click-outside#9. It's something Bootstrap handles well. What do you think of this change?

diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
index 604c02a75..f4347e60c 100644
--- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
+++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
@@ -40,6 +40,11 @@ function ClickAwayListener(props) {

   const handleClickAway = React.useCallback(
     event => {
+      // Ignore click events on the scrollbar.
+      if (event.offsetX > event.target.clientWidth || event.offsetY > event.target.clientHeight) {
+        return;
+      }
+
       // Ignore events that have been `event.preventDefault()` marked.
       if (event.defaultPrevented) {
         return;

Used https://stackoverflow.com/questions/10045423/determine-whether-user-clicking-scrollbar-or-content-onclick-for-native-scroll

@Umerbhat
Copy link
Contributor Author

@Umerbhat I have found a similar thread on kentor/react-click-outside#9. It's something Bootstrap handles well. What do you think of this change?

diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
index 604c02a75..f4347e60c 100644
--- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
+++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
@@ -40,6 +40,11 @@ function ClickAwayListener(props) {

   const handleClickAway = React.useCallback(
     event => {
+      // Ignore click events on the scrollbar.
+      if (event.offsetX > event.target.clientWidth || event.offsetY > event.target.clientHeight) {
+        return;
+      }
+
       // Ignore events that have been `event.preventDefault()` marked.
       if (event.defaultPrevented) {
         return;

Used https://stackoverflow.com/questions/10045423/determine-whether-user-clicking-scrollbar-or-content-onclick-for-native-scroll

Yes, I had checked it before that bootstrap handles it well. It is a great solution to find whether we clicked on the scrollbar. However, it works only for inner scrollbars. For outer scrollbars like in macOS, it fails

@oliviertassinari oliviertassinari added new feature New feature or request and removed discussion labels May 16, 2019
@oliviertassinari
Copy link
Member

oliviertassinari commented May 16, 2019

@Umerbhat Correct, this would work:

diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
index 604c02a75..b1273b25a 100644
--- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
+++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
@@ -64,6 +64,15 @@ function ClickAwayListener(props) {

       const doc = ownerDocument(node);

+      // Ignore click events on the scrollbar.
+      if (
+        event.offsetX > event.target.clientWidth ||
+        event.offsetY > event.target.clientHeight ||
+        event.target === doc.querySelector('html')
+      ) {
+        return;
+      }
+
       if (
         doc.documentElement &&
         doc.documentElement.contains(event.target) &&

But something is off, I can't figure out how bootstrap, reactstrap and react-bootstrap have a correct behavior without any explicit code 😮.

@oliviertassinari
Copy link
Member

Found it 💡 ! It has to do with using onClick (OK) vs onMouseDown / onMouseUp (KO).

@oliviertassinari oliviertassinari added the good first issue Great for first contributions. Enable to learn the contribution process. label May 16, 2019
@Umerbhat
Copy link
Contributor Author

Found it 💡 ! It has to do with using onClick (OK) vs onMouseDown / onMouseUp (KO).

Got it! it differs on Event. Well, I had a solution. It works for chrome macOS

const rect = event.target.getBoundingClientRect()
      // Do not act if user clicks any scrollbar
      if (rect.width > event.target.clientWidth) {
        return;
      }
  // Do not act if user clicks on body inner scrollbar - like in chrome mac OS
  if(rect.left + rect.right === window.innerWidth){
    return;
  }

@oliviertassinari
Copy link
Member

@Umerbhat The fewer layout math we can do, the better.

@oliviertassinari
Copy link
Member

@Umerbhat Do you want to move forward with the event.target === doc.querySelector('html') check in a pull request?
It's going to help in the upcoming dropdown effort of @ryancogswell.

@Umerbhat
Copy link
Contributor Author

get === doc.querySelector('html')

yes, it is fine to do for document's scrollbar. In my case dropdown is inside scrollable divisions, we need to handle it. I think these two will work fine together.

event.target === doc.querySelector('html')
const rect = event.target.getBoundingClientRect() // Do not act if user clicks any scrollbar if (rect.width > event.target.clientWidth) { return; }

Thus, handling click on all visible scrollbars.

@Umerbhat
Copy link
Contributor Author

@Umerbhat The fewer layout math we can do, the better.

and the best

@oliviertassinari
Copy link
Member

Ok, so maybe the best option is to start by changing the default event listener?

@eps1lon
Copy link
Member

eps1lon commented May 17, 2019

One thing I noticed is that no onClick is fired if I click on a scrollbar. Only mousedown + mouseup. So the easiest solution is to just use mouseEvent="onClick" it seems.

@oliviertassinari
Copy link
Member

Yeah, exactly, change it to use onClick +1.

@eps1lon
Copy link
Member

eps1lon commented May 17, 2019

Though I have a hard time finding this in the spec. We need to verify if this works on all browsers.

@Umerbhat
Copy link
Contributor Author

One thing I noticed is that no onClick is fired if I click on a scrollbar. Only mousedown + mouseup. So the easiest solution is to just use mouseEvent="onClick" it seems.

I am glad to know this, the only difference in bootstrap. Great Work!

@oliviertassinari
Copy link
Member

oliviertassinari commented May 18, 2019

@Umerbhat Do you want to submit a pull request here to fix the default value?

@Umerbhat
Copy link
Contributor Author

@Umerbhat Do you want to submit a pull request here to fix the default value?

@oliviertassinari Sure. Thanks for the honour. Default props onMouseUp to onClick

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: ClickAwayListener The React component good first issue Great for first contributions. Enable to learn the contribution process. new feature New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants