From 2f1b4711500dc9158e2c196448ffca6a1fad3480 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Thu, 11 Jun 2020 13:02:25 -0600 Subject: [PATCH 1/7] use enqueueStateChange to prevent DOM churn --- src/components/tool_tip/tool_tip.test.tsx | 4 +++- src/components/tool_tip/tool_tip.tsx | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/tool_tip/tool_tip.test.tsx b/src/components/tool_tip/tool_tip.test.tsx index bf861b2aa29..91923e9ce3d 100644 --- a/src/components/tool_tip/tool_tip.test.tsx +++ b/src/components/tool_tip/tool_tip.test.tsx @@ -23,6 +23,7 @@ import { requiredProps, findTestSubject, takeMountedSnapshot, + sleep, } from '../../test'; import { EuiToolTip } from './tool_tip'; @@ -41,7 +42,7 @@ describe('EuiToolTip', () => { expect(component).toMatchSnapshot(); }); - test('shows tooltip on focus', () => { + test('shows tooltip on focus', async () => { const component = mount( @@ -50,6 +51,7 @@ describe('EuiToolTip', () => { const trigger = findTestSubject(component, 'trigger'); trigger.simulate('focus'); + await sleep(10); // enquqeStateChange needs a tick expect(takeMountedSnapshot(component)).toMatchSnapshot(); }); }); diff --git a/src/components/tool_tip/tool_tip.tsx b/src/components/tool_tip/tool_tip.tsx index 885cf7ff7de..d6f25d9ce52 100644 --- a/src/components/tool_tip/tool_tip.tsx +++ b/src/components/tool_tip/tool_tip.tsx @@ -32,6 +32,7 @@ import { keysOf } from '../common'; import { EuiPortal } from '../portal'; import { EuiToolTipPopover } from './tool_tip_popover'; import { findPopoverPosition, htmlIdGenerator, keys } from '../../services'; +import { enqueueStateChange } from '../../services/react'; import { EuiResizeObserver } from '../observer/resize_observer'; @@ -184,7 +185,7 @@ export class EuiToolTip extends Component { }; showToolTip = () => { - this.setState({ visible: true }); + enqueueStateChange(() => this.setState({ visible: true })); }; positionToolTip = () => { @@ -233,7 +234,7 @@ export class EuiToolTip extends Component { hideToolTip = () => { if (this._isMounted) { - this.setState({ visible: false }); + enqueueStateChange(() => this.setState({ visible: false })); } }; From 148d2df01c828d9d342b1d76e5effa7bfb5b0c72 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Thu, 11 Jun 2020 13:04:23 -0600 Subject: [PATCH 2/7] signature update; remove unnecessary position update --- src/components/tool_tip/tool_tip_popover.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/tool_tip/tool_tip_popover.tsx b/src/components/tool_tip/tool_tip_popover.tsx index 663c9bb31d4..bc50e04661f 100644 --- a/src/components/tool_tip/tool_tip_popover.tsx +++ b/src/components/tool_tip/tool_tip_popover.tsx @@ -23,7 +23,7 @@ import { CommonProps } from '../common'; type Props = CommonProps & Omit, 'title'> & { - positionToolTip: (rect: ClientRect | DOMRect) => void; + positionToolTip: () => void; children?: ReactNode; title?: ReactNode; popoverRef?: (ref: HTMLDivElement) => void; @@ -36,7 +36,7 @@ export class EuiToolTipPopover extends Component { requestAnimationFrame(() => { // Because of this delay, sometimes `positionToolTip` becomes unavailable. if (this.popover) { - this.props.positionToolTip(this.popover.getBoundingClientRect()); + this.props.positionToolTip(); } }); }; @@ -50,8 +50,6 @@ export class EuiToolTipPopover extends Component { componentDidMount() { document.body.classList.add('euiBody-hasPortalContent'); - - this.updateDimensions(); window.addEventListener('resize', this.updateDimensions); } From 6e47351e0ce0c8c620146b099d1f9cea751226a1 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Thu, 11 Jun 2020 17:21:18 -0600 Subject: [PATCH 3/7] delay subcomponent render; move delay from css to js --- src/components/tool_tip/_tool_tip.scss | 6 +----- src/components/tool_tip/tool_tip.test.tsx | 2 +- src/components/tool_tip/tool_tip.tsx | 18 +++++++++++++++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/tool_tip/_tool_tip.scss b/src/components/tool_tip/_tool_tip.scss index 0a06935b378..7186d2340ff 100644 --- a/src/components/tool_tip/_tool_tip.scss +++ b/src/components/tool_tip/_tool_tip.scss @@ -7,11 +7,7 @@ @include euiToolTipAnimation('top'); position: absolute; opacity: 0; - - // Animation delays - &.euiToolTip--delayLong { - animation-delay: $euiAnimSpeedNormal * 5; - } + animation-delay: 0ms; // Custom sizing $arrowSize: $euiSizeM; diff --git a/src/components/tool_tip/tool_tip.test.tsx b/src/components/tool_tip/tool_tip.test.tsx index 91923e9ce3d..d55a0e6f26d 100644 --- a/src/components/tool_tip/tool_tip.test.tsx +++ b/src/components/tool_tip/tool_tip.test.tsx @@ -51,7 +51,7 @@ describe('EuiToolTip', () => { const trigger = findTestSubject(component, 'trigger'); trigger.simulate('focus'); - await sleep(10); // enquqeStateChange needs a tick + await sleep(260); // wait for showToolTip setTimout expect(takeMountedSnapshot(component)).toMatchSnapshot(); }); }); diff --git a/src/components/tool_tip/tool_tip.tsx b/src/components/tool_tip/tool_tip.tsx index d6f25d9ce52..0e38b8b74c0 100644 --- a/src/components/tool_tip/tool_tip.tsx +++ b/src/components/tool_tip/tool_tip.tsx @@ -54,6 +54,11 @@ const delayToClassNameMap: { [key in ToolTipDelay]: string | null } = { long: 'euiToolTip--delayLong', }; +const delayToMsMap: { [key in ToolTipDelay]: number } = { + regular: 250, + long: 250 * 5, +}; + export const DELAY = keysOf(delayToClassNameMap); interface ToolTipStyles { @@ -127,6 +132,7 @@ export class EuiToolTip extends Component { _isMounted = false; anchor: null | HTMLElement = null; popover: null | HTMLElement = null; + private timeoutId?: ReturnType; state: State = { visible: false, @@ -146,6 +152,9 @@ export class EuiToolTip extends Component { } componentWillUnmount() { + if (this.timeoutId) { + this.timeoutId = clearTimeout(this.timeoutId) as undefined; + } this._isMounted = false; window.removeEventListener('mousemove', this.hasFocusMouseMoveListener); } @@ -185,7 +194,11 @@ export class EuiToolTip extends Component { }; showToolTip = () => { - enqueueStateChange(() => this.setState({ visible: true })); + if (!this.timeoutId) { + this.timeoutId = setTimeout(() => { + enqueueStateChange(() => this.setState({ visible: true })); + }, delayToMsMap[this.props.delay]); + } }; positionToolTip = () => { @@ -233,6 +246,9 @@ export class EuiToolTip extends Component { }; hideToolTip = () => { + if (this.timeoutId) { + this.timeoutId = clearTimeout(this.timeoutId) as undefined; + } if (this._isMounted) { enqueueStateChange(() => this.setState({ visible: false })); } From 82a13ff523e0700fa63c70a164e01f2ded6ecbaf Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Mon, 15 Jun 2020 13:32:29 -0600 Subject: [PATCH 4/7] clean up --- src/components/tool_tip/_tool_tip.scss | 3 +-- src/components/tool_tip/tool_tip.tsx | 14 ++++++++------ src/global_styling/mixins/_tool_tip.scss | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/tool_tip/_tool_tip.scss b/src/components/tool_tip/_tool_tip.scss index 7186d2340ff..244db29d06f 100644 --- a/src/components/tool_tip/_tool_tip.scss +++ b/src/components/tool_tip/_tool_tip.scss @@ -4,10 +4,9 @@ .euiToolTip { @include euiToolTipStyle; - @include euiToolTipAnimation('top'); + @include euiToolTipAnimation('top', 0ms); position: absolute; opacity: 0; - animation-delay: 0ms; // Custom sizing $arrowSize: $euiSizeM; diff --git a/src/components/tool_tip/tool_tip.tsx b/src/components/tool_tip/tool_tip.tsx index 0e38b8b74c0..4a4a781aee6 100644 --- a/src/components/tool_tip/tool_tip.tsx +++ b/src/components/tool_tip/tool_tip.tsx @@ -147,14 +147,18 @@ export class EuiToolTip extends Component { delay: 'regular', }; + clearAnimationTimeout = () => { + if (this.timeoutId) { + this.timeoutId = clearTimeout(this.timeoutId) as undefined; + } + }; + componentDidMount() { + this.clearAnimationTimeout(); this._isMounted = true; } componentWillUnmount() { - if (this.timeoutId) { - this.timeoutId = clearTimeout(this.timeoutId) as undefined; - } this._isMounted = false; window.removeEventListener('mousemove', this.hasFocusMouseMoveListener); } @@ -246,9 +250,7 @@ export class EuiToolTip extends Component { }; hideToolTip = () => { - if (this.timeoutId) { - this.timeoutId = clearTimeout(this.timeoutId) as undefined; - } + this.clearAnimationTimeout(); if (this._isMounted) { enqueueStateChange(() => this.setState({ visible: false })); } diff --git a/src/global_styling/mixins/_tool_tip.scss b/src/global_styling/mixins/_tool_tip.scss index 710f58fe1b4..ff25b006838 100644 --- a/src/global_styling/mixins/_tool_tip.scss +++ b/src/global_styling/mixins/_tool_tip.scss @@ -24,5 +24,5 @@ } @mixin euiToolTipAnimation($side: 'top', $delay: $euiAnimSpeedNormal) { - animation: #{map-get($euiTooltipAnimations, $side)} $euiAnimSpeedSlow ease-out $euiAnimSpeedNormal forwards; + animation: #{map-get($euiTooltipAnimations, $side)} $euiAnimSpeedSlow ease-out $delay forwards; } From 92a40a4f5426da974d0c91e249875aa992cb7007 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Mon, 15 Jun 2020 14:02:52 -0600 Subject: [PATCH 5/7] move to unmount --- src/components/tool_tip/tool_tip.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/tool_tip/tool_tip.tsx b/src/components/tool_tip/tool_tip.tsx index 4a4a781aee6..273ac087946 100644 --- a/src/components/tool_tip/tool_tip.tsx +++ b/src/components/tool_tip/tool_tip.tsx @@ -154,11 +154,11 @@ export class EuiToolTip extends Component { }; componentDidMount() { - this.clearAnimationTimeout(); this._isMounted = true; } componentWillUnmount() { + this.clearAnimationTimeout(); this._isMounted = false; window.removeEventListener('mousemove', this.hasFocusMouseMoveListener); } From 7a7e8c2e9d399e3d249b5ac707dd34a732f37bb6 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Mon, 15 Jun 2020 15:56:22 -0600 Subject: [PATCH 6/7] css clean up --- src/components/tool_tip/_tool_tip.scss | 2 +- src/components/tool_tip/tool_tip.tsx | 8 -------- src/global_styling/mixins/_tool_tip.scss | 4 ++-- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/components/tool_tip/_tool_tip.scss b/src/components/tool_tip/_tool_tip.scss index 244db29d06f..eb6b1e038a3 100644 --- a/src/components/tool_tip/_tool_tip.scss +++ b/src/components/tool_tip/_tool_tip.scss @@ -4,7 +4,7 @@ .euiToolTip { @include euiToolTipStyle; - @include euiToolTipAnimation('top', 0ms); + @include euiToolTipAnimation('top'); position: absolute; opacity: 0; diff --git a/src/components/tool_tip/tool_tip.tsx b/src/components/tool_tip/tool_tip.tsx index 273ac087946..34ad953f67f 100644 --- a/src/components/tool_tip/tool_tip.tsx +++ b/src/components/tool_tip/tool_tip.tsx @@ -49,18 +49,11 @@ export const POSITIONS = keysOf(positionsToClassNameMap); export type ToolTipDelay = 'regular' | 'long'; -const delayToClassNameMap: { [key in ToolTipDelay]: string | null } = { - regular: null, - long: 'euiToolTip--delayLong', -}; - const delayToMsMap: { [key in ToolTipDelay]: number } = { regular: 250, long: 250 * 5, }; -export const DELAY = keysOf(delayToClassNameMap); - interface ToolTipStyles { top: number; left: number | 'auto'; @@ -299,7 +292,6 @@ export class EuiToolTip extends Component { const classes = classNames( 'euiToolTip', positionsToClassNameMap[this.state.calculatedPosition], - delayToClassNameMap[delay], className ); diff --git a/src/global_styling/mixins/_tool_tip.scss b/src/global_styling/mixins/_tool_tip.scss index ff25b006838..665b3bf0fec 100644 --- a/src/global_styling/mixins/_tool_tip.scss +++ b/src/global_styling/mixins/_tool_tip.scss @@ -23,6 +23,6 @@ margin-bottom: $euiSizeXS; } -@mixin euiToolTipAnimation($side: 'top', $delay: $euiAnimSpeedNormal) { - animation: #{map-get($euiTooltipAnimations, $side)} $euiAnimSpeedSlow ease-out $delay forwards; +@mixin euiToolTipAnimation($side: 'top') { + animation: #{map-get($euiTooltipAnimations, $side)} $euiAnimSpeedSlow ease-out 0s forwards; } From 01803a78ecbc9d37683e59485d1352fe735eb8ac Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Mon, 15 Jun 2020 15:59:49 -0600 Subject: [PATCH 7/7] CL --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 259ae4d28dc..7c8485c1d2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ - Moved all `EuiHeader` SASS variables to `global_styles` ([#3592](https://github.com/elastic/eui/pull/3592)) - Added `side` prop to `EuiGlobalToastList` for choosing which window side to display toasts ([#3600](https://github.com/elastic/eui/pull/3600)) - Default `titleSize` get's implicitly set to 'm' for `EuiEmptyPrompt` ([#3598](https://github.com/elastic/eui/pull/3598)) -- Updated `logoElastic` to meet brand guidelines ([#3613](https://github.com/elastic/eui/pull/3613)) +- Updated `logoElastic` to meet brand guidelines ([#3613](https://github.com/elastic/eui/pull/3613)) **Bug fixes** @@ -18,6 +18,7 @@ - Fixed `EuiComboBox`'s options list `zIndex` positioning when nested in other `zIndex` contexts ([#3551](https://github.com/elastic/eui/pull/3551)) - Fixed `euiHeaderAffordForFixed` mixin's use of header SASS variable ([#3592](https://github.com/elastic/eui/pull/3592)) - Included `onClick` as a valid prop for `EuiControlBar` **icon** controls ([#3581](https://github.com/elastic/eui/pull/3581)) +- Fixed poor performance of `EuiToolTip` during frequent mouesover/mouseout events ([#3596](https://github.com/elastic/eui/pull/3596)) **Breaking changes**