diff --git a/src/addons/Portal/Portal.js b/src/addons/Portal/Portal.js index 4f49874189..eeac3f9710 100644 --- a/src/addons/Portal/Portal.js +++ b/src/addons/Portal/Portal.js @@ -215,7 +215,7 @@ function Portal(props) { } const handleTriggerMouseLeave = (e, ...rest) => { - clearTimeout(mouseEnterTimer) + clearTimeout(mouseEnterTimer.current) // Call original event handler _.invoke(trigger, 'props.onMouseLeave', e, ...rest) @@ -229,7 +229,7 @@ function Portal(props) { } const handleTriggerMouseEnter = (e, ...rest) => { - clearTimeout(mouseLeaveTimer) + clearTimeout(mouseLeaveTimer.current) // Call original event handler _.invoke(trigger, 'props.onMouseEnter', e, ...rest) diff --git a/test/specs/addons/Portal/Portal-test.js b/test/specs/addons/Portal/Portal-test.js index 05cf68a1ae..9d6900b1b2 100644 --- a/test/specs/addons/Portal/Portal-test.js +++ b/test/specs/addons/Portal/Portal-test.js @@ -7,6 +7,7 @@ import * as common from 'test/specs/commonTests' import { domEvent, sandbox } from 'test/utils' import Portal from 'src/addons/Portal/Portal' import PortalInner from 'src/addons/Portal/PortalInner' +import wait from 'test/utils/wait' let wrapper @@ -371,6 +372,38 @@ describe('Portal', () => { done() }, 1) }) + + /** + * e--l--d--v + * ^: mouseenter + * ^: BEFORE_DELAY: mouseleave + * ^: expected DELAY + * ^: final validation + */ + it('does not open the portal when leave before delay', async () => { + const DELAY = 20 + const BEFORE_DELAY = 10 + + wrapperMount( + } openOnTriggerMouseEnter mouseEnterDelay={DELAY}> +

+ , + ) + + wrapper.should.not.have.descendants(PortalInner) + wrapper.find('button').simulate('mouseenter') + + await wait(BEFORE_DELAY) + + wrapper.update() + wrapper.should.not.have.descendants(PortalInner) + wrapper.find('button').simulate('mouseleave') + + await wait(DELAY) + + wrapper.update() + wrapper.should.not.have.descendants(PortalInner) + }) }) describe('closeOnTriggerMouseLeave', () => { @@ -407,6 +440,50 @@ describe('Portal', () => { done() }, 1) }) + + /** + * e--l--e--d--v + * ^: mouseenter + * ^: mouseleave + * ^: BEFORE_DELAY: reenter + * ^: expected DELAY + * ^: final validation + */ + it('does not close the portal when reenter before delay', async () => { + const DELAY = 20 + const BEFORE_DELAY = 10 + + wrapperMount( + } + openOnTriggerMouseEnter + closeOnTriggerMouseLeave + mouseLeaveDelay={DELAY} + > +

+ , + ) + + wrapper.should.not.have.descendants(PortalInner) + wrapper.find('button').simulate('mouseenter') + + await wait(BEFORE_DELAY) + + wrapper.update() + wrapper.should.have.descendants(PortalInner) + wrapper.find('button').simulate('mouseleave') + + await wait(BEFORE_DELAY) + + wrapper.update() + wrapper.should.have.descendants(PortalInner) + wrapper.find('button').simulate('mouseenter') + + await wait(DELAY) + + wrapper.update() + wrapper.should.have.descendants(PortalInner) + }) }) describe('closeOnPortalMouseLeave', () => { diff --git a/test/utils/wait.js b/test/utils/wait.js new file mode 100644 index 0000000000..e1f1bb2fff --- /dev/null +++ b/test/utils/wait.js @@ -0,0 +1,3 @@ +export default function wait(timeout) { + return new Promise((resolve) => setTimeout(resolve, timeout)) +}