From 6ca44fed015ddcd45314280b5f9927208280c874 Mon Sep 17 00:00:00 2001 From: Julien Fontanet Date: Wed, 2 Oct 2024 10:17:04 +0200 Subject: [PATCH] fix(xo-web/Tooltip): don't hide on changing children prop Due to the way React works, `props.children` always change even if it will resolve to the same DOM node. The implementation is therefore updated to check equality on DOM nodes directly. --- CHANGELOG.unreleased.md | 3 + packages/xo-web/src/common/tooltip/index.js | 69 +++++++++++---------- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 74b4f53570c..5e05eff4234 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -15,6 +15,8 @@ > Users must be able to say: “I had this issue, happy to know it's fixed” +- [Tooltip] Fix randomly disappearing tooltips + ### Packages to release > When modifying a package, add it here with its release type. @@ -32,5 +34,6 @@ - @xen-orchestra/log minor +- xo-web patch diff --git a/packages/xo-web/src/common/tooltip/index.js b/packages/xo-web/src/common/tooltip/index.js index 1db75aba4ec..7e121212a3d 100644 --- a/packages/xo-web/src/common/tooltip/index.js +++ b/packages/xo-web/src/common/tooltip/index.js @@ -62,53 +62,51 @@ export default class Tooltip extends Component { tagName: PropTypes.string, } + _node = null + componentDidMount() { - this._addListeners() + this._updateListeners() } componentWillUnmount() { this._removeListeners() } - componentWillReceiveProps(props) { - if (props.children !== this.props.children) { - this._removeListeners() - } - } - componentDidUpdate(prevProps) { - if (prevProps.children !== this.props.children) { - this._addListeners() - } + this._updateListeners() } - _addListeners() { - const node = (this._node = ReactDOM.findDOMNode(this)) - - // 2020-06-15: Use pointer events instead of mouse events to workaround - // Chrome not firing any mouse event on disabled inputs. Pointer events - // should be correctly fired on most browsers and are similar to mouse - // events on mouse-controlled devices. - // https://github.com/reach/reach-ui/issues/564#issuecomment-620502842 - // https://caniuse.com/#feat=mdn-api_pointerevent - node.addEventListener('pointerenter', this._showTooltip) - node.addEventListener('pointerleave', this._hideTooltip) - node.addEventListener('pointermove', this._updateTooltip) + _updateListeners() { + // eslint-disable-next-line react/no-find-dom-node + const node = ReactDOM.findDOMNode(this) + + if (node !== this._node) { + this._removeListeners() + + this._node = node + if (node !== null) { + // 2020-06-15: Use pointer events instead of mouse events to workaround + // Chrome not firing any mouse event on disabled inputs. Pointer events + // should be correctly fired on most browsers and are similar to mouse + // events on mouse-controlled devices. + // https://github.com/reach/reach-ui/issues/564#issuecomment-620502842 + // https://caniuse.com/#feat=mdn-api_pointerevent + node.addEventListener('pointerenter', this._showTooltip) + node.addEventListener('pointerleave', this._hideTooltip) + node.addEventListener('pointermove', this._updateTooltip) + } + } } _removeListeners() { - const node = this._node this._hideTooltip() - if (!node) { - return + const node = this._node + if (node !== null) { + node.removeEventListener('pointerenter', this._showTooltip) + node.removeEventListener('pointerleave', this._hideTooltip) + node.removeEventListener('pointermove', this._updateTooltip) } - - node.removeEventListener('pointerenter', this._showTooltip) - node.removeEventListener('pointerleave', this._hideTooltip) - node.removeEventListener('pointermove', this._updateTooltip) - - this._node = null } _showTooltip = () => { @@ -123,10 +121,17 @@ export default class Tooltip extends Component { } _hideTooltip = () => { - instance.setState({ show: false }) + if (instance !== undefined) { + instance.setState({ show: false }) + } } _updateTooltip = event => { + if (instance === undefined) { + return + } + + // eslint-disable-next-line react/no-find-dom-node const node = ReactDOM.findDOMNode(instance) const result = getPosition(event, event.currentTarget, node, instance.state.place, 'solid', {})