Skip to content

Commit

Permalink
[EuiToolTip] Improve render performance (#3596)
Browse files Browse the repository at this point in the history
* use enqueueStateChange to prevent DOM churn

* signature update; remove unnecessary position update

* delay subcomponent render; move delay from css to js

* clean up

* move to unmount

* css clean up

* CL
  • Loading branch information
thompsongl authored Jun 15, 2020
1 parent f8c64e7 commit b67f644
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 21 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**

Expand All @@ -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**

Expand Down
5 changes: 0 additions & 5 deletions src/components/tool_tip/_tool_tip.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@
position: absolute;
opacity: 0;

// Animation delays
&.euiToolTip--delayLong {
animation-delay: $euiAnimSpeedNormal * 5;
}

// Custom sizing
$arrowSize: $euiSizeM;
$arrowPlusSize: (($arrowSize/2) + 1px) * -1; /* 1 */
Expand Down
4 changes: 3 additions & 1 deletion src/components/tool_tip/tool_tip.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
requiredProps,
findTestSubject,
takeMountedSnapshot,
sleep,
} from '../../test';
import { EuiToolTip } from './tool_tip';

Expand All @@ -41,7 +42,7 @@ describe('EuiToolTip', () => {
expect(component).toMatchSnapshot();
});

test('shows tooltip on focus', () => {
test('shows tooltip on focus', async () => {
const component = mount(
<EuiToolTip title="title" id="id" content="content" {...requiredProps}>
<button data-test-subj="trigger">Trigger</button>
Expand All @@ -50,6 +51,7 @@ describe('EuiToolTip', () => {

const trigger = findTestSubject(component, 'trigger');
trigger.simulate('focus');
await sleep(260); // wait for showToolTip setTimout
expect(takeMountedSnapshot(component)).toMatchSnapshot();
});
});
27 changes: 19 additions & 8 deletions src/components/tool_tip/tool_tip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -48,13 +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';
Expand Down Expand Up @@ -126,6 +125,7 @@ export class EuiToolTip extends Component<Props, State> {
_isMounted = false;
anchor: null | HTMLElement = null;
popover: null | HTMLElement = null;
private timeoutId?: ReturnType<typeof setTimeout>;

state: State = {
visible: false,
Expand All @@ -140,11 +140,18 @@ export class EuiToolTip extends Component<Props, State> {
delay: 'regular',
};

clearAnimationTimeout = () => {
if (this.timeoutId) {
this.timeoutId = clearTimeout(this.timeoutId) as undefined;
}
};

componentDidMount() {
this._isMounted = true;
}

componentWillUnmount() {
this.clearAnimationTimeout();
this._isMounted = false;
window.removeEventListener('mousemove', this.hasFocusMouseMoveListener);
}
Expand Down Expand Up @@ -184,7 +191,11 @@ export class EuiToolTip extends Component<Props, State> {
};

showToolTip = () => {
this.setState({ visible: true });
if (!this.timeoutId) {
this.timeoutId = setTimeout(() => {
enqueueStateChange(() => this.setState({ visible: true }));
}, delayToMsMap[this.props.delay]);
}
};

positionToolTip = () => {
Expand Down Expand Up @@ -232,8 +243,9 @@ export class EuiToolTip extends Component<Props, State> {
};

hideToolTip = () => {
this.clearAnimationTimeout();
if (this._isMounted) {
this.setState({ visible: false });
enqueueStateChange(() => this.setState({ visible: false }));
}
};

Expand Down Expand Up @@ -280,7 +292,6 @@ export class EuiToolTip extends Component<Props, State> {
const classes = classNames(
'euiToolTip',
positionsToClassNameMap[this.state.calculatedPosition],
delayToClassNameMap[delay],
className
);

Expand Down
6 changes: 2 additions & 4 deletions src/components/tool_tip/tool_tip_popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { CommonProps } from '../common';

type Props = CommonProps &
Omit<HTMLAttributes<HTMLDivElement>, 'title'> & {
positionToolTip: (rect: ClientRect | DOMRect) => void;
positionToolTip: () => void;
children?: ReactNode;
title?: ReactNode;
popoverRef?: (ref: HTMLDivElement) => void;
Expand All @@ -36,7 +36,7 @@ export class EuiToolTipPopover extends Component<Props> {
requestAnimationFrame(() => {
// Because of this delay, sometimes `positionToolTip` becomes unavailable.
if (this.popover) {
this.props.positionToolTip(this.popover.getBoundingClientRect());
this.props.positionToolTip();
}
});
};
Expand All @@ -50,8 +50,6 @@ export class EuiToolTipPopover extends Component<Props> {

componentDidMount() {
document.body.classList.add('euiBody-hasPortalContent');

this.updateDimensions();
window.addEventListener('resize', this.updateDimensions);
}

Expand Down
4 changes: 2 additions & 2 deletions src/global_styling/mixins/_tool_tip.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@
margin-bottom: $euiSizeXS;
}

@mixin euiToolTipAnimation($side: 'top', $delay: $euiAnimSpeedNormal) {
animation: #{map-get($euiTooltipAnimations, $side)} $euiAnimSpeedSlow ease-out $euiAnimSpeedNormal forwards;
@mixin euiToolTipAnimation($side: 'top') {
animation: #{map-get($euiTooltipAnimations, $side)} $euiAnimSpeedSlow ease-out 0s forwards;
}

0 comments on commit b67f644

Please sign in to comment.