Repository: mui/material-ui (TypeScript)
Date: 2019-12-04 18:40:25+00:00
Task type: Bug Fix

Issue:
[Slider] Interactive tooltips on range slider has inconsistent behavior
When you add custom Tooltip components to a slider, and you use a range, with the tooltips set to `interactive: true, leaveDelay: 100`, when you take your cursor off the second handle, it briefly shows the first handle's tooltip.

Here's a simple reproduction: 
https://codesandbox.io/s/mystifying-sun-uodok

- [x] The issue is present in the latest release.
- [x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.


| Tech        | Version |
| ----------- | ------- |
| Material-UI | v4.7.1  |
| React       |     16.12    |
| Browser     |   Chrome      |



Issue discussion:
@Floriferous Why do you set `interactive` in the first place?
Well because I want my users to be able to interact with the tooltip, and copy paste the text for example, if I decide to display more information inside the tooltip based on the slider's value for example. 
I had a look at the problem. In the slider, `event.currentTarget` for the mouse over event is an element with `role="tooltip"`, it shouldn't be the case, it should be the thumb element. Looking deeper, it happens that we forward the onMouseOver even, from the tooltip to the element the tooltip wraps when, that's bad. I would propose the following patch:

```diff
diff --git a/packages/material-ui/src/Tooltip/Tooltip.js b/packages/material-ui/src/Tooltip/Tooltip.js
index 5cbf56f44..d643b7afd 100644
--- a/packages/material-ui/src/Tooltip/Tooltip.js
+++ b/packages/material-ui/src/Tooltip/Tooltip.js
@@ -280,7 +280,11 @@ const Tooltip = React.forwardRef(function Tooltip(props, ref) {
   const handleEnter = event => {
     const childrenProps = children.props;

-    if (event.type === 'mouseover' && childrenProps.onMouseOver) {
+    if (
+      event.type === 'mouseover' &&
+      childrenProps.onMouseOver &&
+      event.currentTarget === childNode
+    ) {
       childrenProps.onMouseOver(event);
     }
```

Solution patch:
diff --git a/packages/material-ui/src/Tooltip/Tooltip.js b/packages/material-ui/src/Tooltip/Tooltip.js
--- a/packages/material-ui/src/Tooltip/Tooltip.js
+++ b/packages/material-ui/src/Tooltip/Tooltip.js
@@ -280,7 +280,11 @@ const Tooltip = React.forwardRef(function Tooltip(props, ref) {
   const handleEnter = event => {
     const childrenProps = children.props;
 
-    if (event.type === 'mouseover' && childrenProps.onMouseOver) {
+    if (
+      event.type === 'mouseover' &&
+      childrenProps.onMouseOver &&
+      event.currentTarget === childNode
+    ) {
       childrenProps.onMouseOver(event);
     }
 
@@ -330,7 +334,7 @@ const Tooltip = React.forwardRef(function Tooltip(props, ref) {
     }
 
     const childrenProps = children.props;
-    if (childrenProps.onFocus) {
+    if (childrenProps.onFocus && event.currentTarget === childNode) {
       childrenProps.onFocus(event);
     }
   };
@@ -360,13 +364,17 @@ const Tooltip = React.forwardRef(function Tooltip(props, ref) {
     const childrenProps = children.props;
 
     if (event.type === 'blur') {
-      if (childrenProps.onBlur) {
+      if (childrenProps.onBlur && event.currentTarget === childNode) {
         childrenProps.onBlur(event);
       }
       handleBlur(event);
     }
 
-    if (event.type === 'mouseleave' && childrenProps.onMouseLeave) {
+    if (
+      event.type === 'mouseleave' &&
+      childrenProps.onMouseLeave &&
+      event.currentTarget === childNode
+    ) {
       childrenProps.onMouseLeave(event);
     }
 


Test patch:
diff --git a/packages/material-ui/src/Tooltip/Tooltip.test.js b/packages/material-ui/src/Tooltip/Tooltip.test.js
--- a/packages/material-ui/src/Tooltip/Tooltip.test.js
+++ b/packages/material-ui/src/Tooltip/Tooltip.test.js
@@ -1,5 +1,6 @@
+/* eslint-disable jsx-a11y/mouse-events-have-key-events */
 import React from 'react';
-import { assert } from 'chai';
+import { assert, expect } from 'chai';
 import PropTypes from 'prop-types';
 import { spy, useFakeTimers } from 'sinon';
 import consoleErrorMock from 'test/utils/consoleErrorMock';
@@ -227,17 +228,17 @@ describe('<Tooltip />', () => {
       );
       const children = container.querySelector('#testChild');
       focusVisible(children);
-      assert.strictEqual(document.body.querySelectorAll('[role="tooltip"]').length, 0);
+      expect(document.body.querySelectorAll('[role="tooltip"]').length).to.equal(0);
       clock.tick(111);
-      assert.strictEqual(document.body.querySelectorAll('[role="tooltip"]').length, 1);
+      expect(document.body.querySelectorAll('[role="tooltip"]').length).to.equal(1);
       document.activeElement.blur();
       clock.tick(5);
       clock.tick(6);
-      assert.strictEqual(document.body.querySelectorAll('[role="tooltip"]').length, 0);
+      expect(document.body.querySelectorAll('[role="tooltip"]').length).to.equal(0);
 
       focusVisible(children);
       // Bypass `enterDelay` wait, instant display.
-      assert.strictEqual(document.body.querySelectorAll('[role="tooltip"]').length, 1);
+      expect(document.body.querySelectorAll('[role="tooltip"]').length).to.equal(1);
     });
 
     it('should take the leaveDelay into account', () => {
@@ -285,6 +286,19 @@ describe('<Tooltip />', () => {
         assert.strictEqual(handler.callCount, 1);
       });
     });
+
+    it('should ignore event from the tooltip', () => {
+      const handleMouseOver = spy();
+      const { getByRole } = render(
+        <Tooltip {...defaultProps} open interactive>
+          <button type="submit" onMouseOver={handleMouseOver}>
+            Hello World
+          </button>
+        </Tooltip>,
+      );
+      fireEvent.mouseOver(getByRole('tooltip'));
+      expect(handleMouseOver.callCount).to.equal(0);
+    });
   });
 
   describe('disabled button warning', () => {