]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11216 fix hiding tooltips (#679)
authorStas Vilchik <stas.vilchik@sonarsource.com>
Tue, 4 Sep 2018 07:24:33 +0000 (09:24 +0200)
committerSonarTech <sonartech@sonarsource.com>
Tue, 4 Sep 2018 18:20:56 +0000 (20:20 +0200)
server/sonar-web/src/main/js/components/controls/Tooltip.tsx
server/sonar-web/src/main/js/components/controls/__tests__/Tooltip-test.tsx
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Tooltip-test.tsx.snap

index 9e25efbdb5cc1d9dcc12a56b31bee7149df86e5c..802be846f8b0077d469c42c6fa493bfda289116a 100644 (file)
@@ -67,8 +67,8 @@ export default function Tooltip(props: Props) {
 
 export class TooltipInner extends React.Component<Props, State> {
   throttledPositionTooltip: (() => void);
-  mouseEnterInterval?: number;
-  mouseLeaveInterval?: number;
+  mouseEnterTimeout?: number;
+  mouseLeaveTimeout?: number;
   tooltipNode?: HTMLElement | null;
   mounted = false;
   mouseIn = false;
@@ -118,7 +118,7 @@ export class TooltipInner extends React.Component<Props, State> {
   componentWillUnmount() {
     this.mounted = false;
     this.removeEventListeners();
-    this.clearIntervals();
+    this.clearTimeouts();
   }
 
   addEventListeners = () => {
@@ -131,9 +131,9 @@ export class TooltipInner extends React.Component<Props, State> {
     window.removeEventListener('scroll', this.throttledPositionTooltip);
   };
 
-  clearIntervals = () => {
-    window.clearInterval(this.mouseEnterInterval);
-    window.clearInterval(this.mouseLeaveInterval);
+  clearTimeouts = () => {
+    window.clearTimeout(this.mouseEnterTimeout);
+    window.clearTimeout(this.mouseLeaveTimeout);
   };
 
   isVisible = () => {
@@ -206,9 +206,12 @@ export class TooltipInner extends React.Component<Props, State> {
   };
 
   handleMouseEnter = () => {
-    this.mouseEnterInterval = window.setTimeout(() => {
+    this.mouseEnterTimeout = window.setTimeout(() => {
       if (this.mounted) {
-        if (this.props.visible === undefined) {
+        // for some reason even after the `this.mouseEnterTimeout` is cleared, it still triggers
+        // to workaround this issue, check that its value is not `undefined`
+        // (if it's `undefined`, it means the timer has been reset)
+        if (this.props.visible === undefined && this.mouseEnterTimeout !== undefined) {
           this.setState({ visible: true });
         }
       }
@@ -220,13 +223,13 @@ export class TooltipInner extends React.Component<Props, State> {
   };
 
   handleMouseLeave = () => {
-    if (this.mouseEnterInterval !== undefined) {
-      window.clearInterval(this.mouseEnterInterval);
-      this.mouseEnterInterval = undefined;
+    if (this.mouseEnterTimeout !== undefined) {
+      window.clearTimeout(this.mouseEnterTimeout);
+      this.mouseEnterTimeout = undefined;
     }
 
     if (!this.mouseIn) {
-      this.mouseLeaveInterval = window.setTimeout(() => {
+      this.mouseLeaveTimeout = window.setTimeout(() => {
         if (this.mounted) {
           if (this.props.visible === undefined && !this.mouseIn) {
             this.setState({ visible: false });
index 139a4c7b1be3a9055262c74186b764c03d099288..a6c2a759e88faab2e930328c96f3817500064d25 100644 (file)
@@ -52,16 +52,33 @@ it('should open & close', () => {
   wrapper.find('#tooltip').simulate('mouseenter');
   jest.runOnlyPendingTimers();
   wrapper.update();
-  expect(wrapper).toMatchSnapshot();
+  expect(wrapper.find('TooltipPortal').exists()).toBe(true);
   expect(onShow).toBeCalled();
 
   wrapper.find('#tooltip').simulate('mouseleave');
   jest.runOnlyPendingTimers();
   wrapper.update();
-  expect(wrapper).toMatchSnapshot();
+  expect(wrapper.find('TooltipPortal').exists()).toBe(false);
   expect(onHide).toBeCalled();
 });
 
+it('should not open when mouse goes away quickly', () => {
+  const onShow = jest.fn();
+  const onHide = jest.fn();
+  const wrapper = shallow(
+    <TooltipInner onHide={onHide} onShow={onShow} overlay={<span id="overlay" />}>
+      <div id="tooltip" />
+    </TooltipInner>
+  );
+
+  wrapper.find('#tooltip').simulate('mouseenter');
+  wrapper.find('#tooltip').simulate('mouseleave');
+  jest.runOnlyPendingTimers();
+  wrapper.update();
+
+  expect(wrapper.find('TooltipPortal').exists()).toBe(false);
+});
+
 it('should not render tooltip without overlay', () => {
   const wrapper = shallow(
     <Tooltip overlay={undefined}>
index 6c0f293b850a3235746fa0a5394bc0017701350b..cec95565950a1bbe3b61718adfd08890ac43b9a6 100644 (file)
@@ -12,31 +12,6 @@ exports[`should not render empty tooltips 2`] = `
 />
 `;
 
-exports[`should open & close 1`] = `
-<React.Fragment>
-  <div
-    id="tooltip"
-    onMouseEnter={[Function]}
-    onMouseLeave={[Function]}
-  />
-  <TooltipPortal>
-    <ScreenPositionFixer
-      ready={false}
-    />
-  </TooltipPortal>
-</React.Fragment>
-`;
-
-exports[`should open & close 2`] = `
-<React.Fragment>
-  <div
-    id="tooltip"
-    onMouseEnter={[Function]}
-    onMouseLeave={[Function]}
-  />
-</React.Fragment>
-`;
-
 exports[`should render 1`] = `
 <React.Fragment>
   <div