Repository: mui/material-ui (TypeScript)
Date: 2018-08-03 00:50:15+00:00
Task type: Bug Fix

Issue:
[Tooltip] Mousing over a tooltip that covers another tooltip element will prevent the second Tooltip from opening
If I mouse over an element that has a tooltip, and that tooltip appears over a second element that has a tooltip, then I mouse over the first element's tooltip, the first element's tooltip fades out, but the second element's tooltip never appears. Instead, an HTML title appears.

I believe the mouseover event is simply not firing due to the first tooltip blocking it. This might be solved with something as simple as `pointers-events: none`, but I'm not sure, so just posting here to log the issue.

## Expected Behavior
First tooltip to fade out, second tooltip to fade in.

## Current Behavior
First tooltip fades out, HTML title appears for second element.

## Steps to Reproduce
![1](https://user-images.githubusercontent.com/343837/43268684-e5d40e88-90b6-11e8-9cb7-e35534ab6b7a.gif)



Issue discussion:
@CharlesStover I have found a workaround. We need to use the `mouseover` event over the `mouseenter` event. 
https://github.com/mui-org/material-ui/blob/ca6919db8911cd27080135ea6d823e7c0df73ce9/packages/material-ui/src/Tooltip/Tooltip.js#L303
Is this something you want to work on? :)
@oliviertassinari sandbox here https://codesandbox.io/s/v0qj897xy3
When direction is column, this bug is more noticed.
![image](https://user-images.githubusercontent.com/12452003/43300321-89ff8300-912c-11e8-803e-c7452b79ffc4.png)

@oliviertassinari What exactly is wrong with setting 'pointer-events: none'?
My previous issue (https://github.com/mui-org/material-ui/issues/10735) got closed due to a (positive) side-effect of the implementation change; but it originally had the same fix.

Why would we juggle with different kinds of pointer-events, when what we actually want is for the tooltip to simply ignore the pointer all together.
Is there any reason to be able to catch pointer-events on a tooltip?
@freund17 Some people still want to copy and paste the content of the tooltip. I think that it's too opinionated to disable the interactions when we can avoid it.
#### How would you copy and paste the content of a tooltip?
It vanishes as soon as you leave the button the tooltip is attached to.
And you always have to leave the button, because as soon as the cursor is above the tooltip, it is no longer above the button. (because the tooltip is between the cursor and the button; btw: this is what caused the flickering in my issue)
And if you loosen that requirement from being "over and touching" the button to simply "being anywhere above" the button, you might run into a problem down the road where, for example, a tooltip-button in a dialog lies directly above an other tooltip-button.

#### Why would you expect to be able to copy and paste the content of a tooltip?
I have never encountered a way to directly interact with a tooltip. Never in Windows, and - as far as I got around - never in Linux (Ubuntu, Kubuntu).
The only times I was able to interact with what somewhat resembles a tooltip, was in some IDE, I guess...
But that kinda looked like a different component anyways...
If anything I find it distracting when the cursor switches into its "text-form" whenever I happen to pass over a tooltip.

#### Why would you copy and paste the content of a tooltip?
And lastly, I think it is bad UI design to have useful (copy-worthy) information hidden inside a tooltip. The stuff to put in a tooltip - at least as far as I'm concerned - are icon-button-lables and hotkeys. Both of which have no use on their own; And by that, don't need to be copied.
@freund17 See the last triggers demo: https://material-ui.com/demos/tooltips/#triggers. 

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
@@ -160,11 +160,11 @@ class Tooltip extends React.Component {
       }
     }
 
-    if (event.type === 'mouseenter') {
+    if (event.type === 'mouseover') {
       this.internalState.hover = true;
 
-      if (childrenProps.onMouseEnter) {
-        childrenProps.onMouseEnter(event);
+      if (childrenProps.onMouseOver) {
+        childrenProps.onMouseOver(event);
       }
     }
 
@@ -190,7 +190,10 @@ class Tooltip extends React.Component {
   };
 
   handleOpen = event => {
-    if (!this.isControlled) {
+    // The mouseover event will trigger for every nested element in the tooltip.
+    // We can skip rerendering when the tooltip is already open.
+    // We are using the mouseover event instead of the mouseenter event to fix a hide/show issue.
+    if (!this.isControlled && !this.state.open) {
       this.setState({ open: true });
     }
 
@@ -317,7 +320,7 @@ class Tooltip extends React.Component {
     }
 
     if (!disableHoverListener) {
-      childrenProps.onMouseEnter = this.handleEnter;
+      childrenProps.onMouseOver = this.handleEnter;
       childrenProps.onMouseLeave = this.handleLeave;
     }
 


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
@@ -64,7 +64,7 @@ describe('<Tooltip />', () => {
     wrapper.instance().childrenRef = document.createElement('div');
     const children = wrapper.childAt(0).childAt(0);
     assert.strictEqual(wrapper.state().open, false);
-    children.simulate('mouseEnter', { type: 'mouseenter' });
+    children.simulate('mouseOver', { type: 'mouseover' });
     assert.strictEqual(wrapper.state().open, true);
     children.simulate('mouseLeave', { type: 'mouseleave' });
     assert.strictEqual(wrapper.state().open, false);
@@ -81,7 +81,7 @@ describe('<Tooltip />', () => {
     const children = wrapper.childAt(0).childAt(0);
     assert.strictEqual(handleRequestOpen.callCount, 0);
     assert.strictEqual(handleClose.callCount, 0);
-    children.simulate('mouseEnter', { type: 'mouseenter' });
+    children.simulate('mouseOver', { type: 'mouseover' });
     assert.strictEqual(handleRequestOpen.callCount, 1);
     assert.strictEqual(handleClose.callCount, 0);
     children.simulate('mouseLeave', { type: 'mouseleave' });
@@ -94,7 +94,7 @@ describe('<Tooltip />', () => {
     wrapper.instance().childrenRef = document.createElement('div');
     const children = wrapper.childAt(0).childAt(0);
     assert.strictEqual(wrapper.state().open, false);
-    children.simulate('mouseEnter', { type: 'mouseenter' });
+    children.simulate('mouseOver', { type: 'mouseover' });
     children.simulate('focus', { type: 'focus', persist });
     clock.tick(0);
     assert.strictEqual(wrapper.state().open, true);
@@ -112,7 +112,6 @@ describe('<Tooltip />', () => {
       children.simulate('touchStart', { type: 'touchstart', persist });
       children.simulate('touchEnd', { type: 'touchend', persist });
       children.simulate('focus', { type: 'focus', persist });
-      children.simulate('mouseover', { type: 'mouseover' });
       assert.strictEqual(wrapper.state().open, false);
     });
 
@@ -122,7 +121,6 @@ describe('<Tooltip />', () => {
       const children = wrapper.childAt(0).childAt(0);
       children.simulate('touchStart', { type: 'touchstart', persist });
       children.simulate('focus', { type: 'focus', persist });
-      children.simulate('mouseover', { type: 'mouseover' });
       clock.tick(1e3);
       assert.strictEqual(wrapper.state().open, true);
       children.simulate('touchEnd', { type: 'touchend', persist });
@@ -164,26 +162,32 @@ describe('<Tooltip />', () => {
   });
 
   describe('prop: overrides', () => {
-    ['onTouchStart', 'onTouchEnd', 'onMouseEnter', 'onMouseLeave', 'onFocus', 'onBlur'].forEach(
-      name => {
-        it(`should be transparent for the ${name} event`, () => {
-          const handler = spy();
-          const wrapper = shallow(
-            <Tooltip title="Hello World">
-              <button type="submit" {...{ [name]: handler }}>
-                Hello World
-              </button>
-            </Tooltip>,
-          );
-          wrapper.instance().childrenRef = document.createElement('div');
-          const children = wrapper.childAt(0).childAt(0);
-          const type = name.slice(2).toLowerCase();
-          children.simulate(type, { type, persist });
-          clock.tick(0);
-          assert.strictEqual(handler.callCount, 1);
-        });
-      },
-    );
+    [
+      'onTouchStart',
+      'onTouchEnd',
+      'onMouseEnter',
+      'onMouseOver',
+      'onMouseLeave',
+      'onFocus',
+      'onBlur',
+    ].forEach(name => {
+      it(`should be transparent for the ${name} event`, () => {
+        const handler = spy();
+        const wrapper = shallow(
+          <Tooltip title="Hello World">
+            <button type="submit" {...{ [name]: handler }}>
+              Hello World
+            </button>
+          </Tooltip>,
+        );
+        wrapper.instance().childrenRef = document.createElement('div');
+        const children = wrapper.childAt(0).childAt(0);
+        const type = name.slice(2).toLowerCase();
+        children.simulate(type, { type, persist });
+        clock.tick(0);
+        assert.strictEqual(handler.callCount, 1);
+      });
+    });
   });
 
   describe('disabled button warning', () => {