UNPKG

@atlaskit/tooltip

Version:

A tooltip briefly describes an interactive element on mouse hover or keyboard focus.

778 lines (747 loc) 29 kB
/* tooltip.tsx generated by @compiled/babel-plugin v3.0.2 */ import _extends from "@babel/runtime/helpers/extends"; import "./tooltip.compiled.css"; import { ax, ix } from "@compiled/react/runtime"; import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'; import { bind } from 'bind-event-listener'; import { usePlatformLeafSyntheticEventHandler } from '@atlaskit/analytics-next/usePlatformLeafSyntheticEventHandler'; import noop from '@atlaskit/ds-lib/noop'; import useCloseOnEscapePress from '@atlaskit/ds-lib/use-close-on-escape-press'; import useStableRef from '@atlaskit/ds-lib/use-stable-ref'; import { useNotifyOpenLayerObserver } from '@atlaskit/layering/use-notify-open-layer-observer'; import ExitingPersistence from '@atlaskit/motion/exiting-persistence'; import FadeIn from '@atlaskit/motion/fade-in'; import { fg } from '@atlaskit/platform-feature-flags/fg'; import { Popper } from '@atlaskit/popper/main'; import Portal from '@atlaskit/portal/portal'; import { layers } from '@atlaskit/theme/constants'; import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map/index'; import { Popover } from '@atlaskit/top-layer/popover/popover'; import { useAnchoredPopover } from '@atlaskit/top-layer/use-anchored-popover'; import { useAnchoredPopoverAtPoint } from '@atlaskit/top-layer/use-anchored-popover-at-point'; import { register } from './internal/drag-manager'; import { getAnchorPoint } from './internal/get-anchor-point'; import { getVirtualElementFromMousePos } from './internal/get-virtual-element-from-mouse-pos'; import { show } from './internal/tooltip-manager'; import useUniqueId from './internal/use-unique-id'; import TooltipContainer from './tooltip-container'; const tooltipZIndex = layers.tooltip(); const analyticsAttributes = { componentName: 'tooltip', packageName: "@atlaskit/tooltip", packageVersion: "24.3.3" }; // Inverts motion direction const invertedDirection = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' }; /** * Converts a Popper placement to it's general direction. * * @param placement - Popper Placement value, e.g. 'top-start' * @returns Popper Direction, e.g. 'top' */ const getDirectionFromPlacement = placement => placement.split('-')[0]; /** * For the `platform-dst-top-layer-tooltip` path only. Always `false` when that * gate is off, so this cannot affect the legacy path even if it gains a caller * there. * * `mouseover` also fires when the pointer crosses boundaries between elements * _inside_ the trigger. A `relatedTarget` the trigger does not contain is what * distinguishes a real re-entry from one of those. */ function isTopLayerPointerReEntry({ trigger, relatedTarget }) { if (!fg('platform-dst-top-layer-tooltip')) { return false; } // Missing trigger, or pointer from outside the document. Treat both as an // entry so we cannot get stuck suppressed. if (!trigger || !(relatedTarget instanceof Node)) { return true; } return !trigger.contains(relatedTarget); } /** * __Tooltip__ * * A tooltip is a floating, non-actionable label used to explain a user interface element or feature. */ function Tooltip({ children, position = 'bottom', mousePosition = 'bottom', content, truncate = false, component: Container = TooltipContainer, tag: TargetContainer = 'div', testId, delay = 300, onShow = noop, onHide = noop, canAppear, hideTooltipOnClick = false, hideTooltipOnMouseDown = false, analyticsContext, strategy = 'fixed', ignoreTooltipPointerEvents = false, isScreenReaderAnnouncementDisabled = false, shortcut, shouldAlwaysFadeIn = false, shouldRenderToParent = false }) { // Not using a gate for this check. When the gate is disabled `mouse-y` and `mouse-x` are treated as `mouse`. const isMousePosition = position === 'mouse' || position === 'mouse-y' || position === 'mouse-x'; const tooltipPosition = isMousePosition ? mousePosition : position; const onShowHandler = usePlatformLeafSyntheticEventHandler({ fn: onShow, action: 'displayed', analyticsData: analyticsContext, ...analyticsAttributes }); const onHideHandler = usePlatformLeafSyntheticEventHandler({ fn: onHide, action: 'hidden', analyticsData: analyticsContext, ...analyticsAttributes }); const apiRef = useRef(null); const [state, setState] = useState('hide'); const targetRef = useRef(null); const containerRef = useRef(null); // This function is deliberately _not_ memoized as it needs to re-run every render // to pick up any child ref changes. If you use render props you don't have this issue. const setImplicitRefFromChildren = node => { containerRef.current = node; targetRef.current = node ? node.firstElementChild : null; }; // This is memoized and passed into the render props callback. const setDirectRef = useCallback(node => { targetRef.current = node; }, []); // Putting a few things into refs so that we don't have to break memoization const stableState = useStableRef(state); // These props are placed in separate refs instead of a single object to reduce memory usage. // Placing them in the same object previously caused an increase in the number of JavaScript event listeners // before garbage collection. const onShowHandlerStable = useStableRef(onShowHandler); const onHideHandlerStable = useStableRef(onHideHandler); const delayStable = useStableRef(delay); const canAppearStable = useStableRef(canAppear); const hasCalledShowHandler = useRef(false); const shouldAlwaysFadeInStable = useStableRef(shouldAlwaysFadeIn); const start = useCallback(api => { apiRef.current = api; hasCalledShowHandler.current = false; }, []); const done = useCallback(() => { if (!apiRef.current) { return; } // Only call onHideHandler if we have called onShowHandler if (hasCalledShowHandler.current) { onHideHandlerStable.current(); } apiRef.current = null; hasCalledShowHandler.current = false; // just in case setState('hide'); }, [onHideHandlerStable]); const abort = useCallback(() => { if (!apiRef.current) { return; } apiRef.current.abort(); // Only call onHideHandler if we have called onShowHandler if (hasCalledShowHandler.current) { onHideHandlerStable.current(); } apiRef.current = null; }, [onHideHandlerStable]); useEffect(function mount() { return function unmount() { if (apiRef.current) { abort(); } }; }, [abort]); const isDraggingRef = useRef(false); useEffect(() => { return register({ onRegister({ isDragging }) { isDraggingRef.current = isDragging; }, onDragStart() { var _apiRef$current; /** * Hiding any visible tooltips when a drag starts because otherwise it * looks janky (disappears and reappears), and is not required. */ (_apiRef$current = apiRef.current) === null || _apiRef$current === void 0 ? void 0 : _apiRef$current.requestHide({ isImmediate: true }); isDraggingRef.current = true; }, onDragEnd() { isDraggingRef.current = false; } }); }, []); // Set while a pointer press has dismissed the tooltip. Every read and write is // behind `platform-dst-top-layer-tooltip`, where `popover="hint"` owns pointer // dismissal, so the legacy path never sees this. const isTopLayerPointerDismissedRef = useRef(false); const tryShowTooltip = useCallback(source => { var _canAppearStable$curr; /** * Prevent tooltips from being shown during a drag. This can occur with * the native drag and drop API, where some pointer events can fire * when they should not and lead to jank with tooltips. */ if (isDraggingRef.current) { return; } // Another tooltip is has been active but we still have the old `api` // around. We need to finish up the last usage. // Note: just being safe - this should not happen if (apiRef.current && !apiRef.current.isActive()) { abort(); } // This tooltip is already active, we can exit if (apiRef.current && apiRef.current.isActive()) { apiRef.current.keep(); return; } /** * Stay dismissed until the trigger is re-entered or blurred. Otherwise * light dismiss hides on pointerup and the next `mouseover` from a * boundary crossing inside the trigger re-shows it. Deliberately after * the "already active" branch, so a held press stays visible. */ if (isTopLayerPointerDismissedRef.current && fg('platform-dst-top-layer-tooltip')) { return; } /** * Check if tooltip is allowed to show. * * Once a tooltip has started, or has scheduled to start * we won't be checking `canAppear` again. * * - We don't want tooltips to disappear once they are shown * - For consistency, we start after a single positive `canAppear`. * Otherwise the amount of times we ask consumers would depend on * how many times we get a "mousemove", which _could_ lead to situations * where moving the mouse could result in a different outcome to if * the mouse was not moved. */ if (canAppearStable.current && !((_canAppearStable$curr = canAppearStable.current) !== null && _canAppearStable$curr !== void 0 && _canAppearStable$curr.call(canAppearStable))) { return; } const entry = { source, delay: delayStable.current, show: ({ isImmediate }) => { // Call the onShow handler if it hasn't been called yet if (!hasCalledShowHandler.current) { hasCalledShowHandler.current = true; onShowHandlerStable.current(); } setState(!isImmediate ? 'fade-in' : 'show-immediate'); }, hide: ({ isImmediate }) => { if (isImmediate) { setState('hide'); } else if (fg('platform-dst-top-layer-tooltip')) { // Top-layer path: set state to 'top-layer-exit'. The component // stays mounted and Popover's isOpen prop transitions to // false, triggering the CSS exit animation internally. // finishHideAnimation is called after a brief delay matching // the CSS exit animation duration. setState('top-layer-exit'); } else { setState('before-fade-out'); } }, done, shouldAlwaysFadeIn: shouldAlwaysFadeInStable.current }; const api = show(entry); start(api); }, [canAppearStable, delayStable, done, start, abort, onShowHandlerStable, shouldAlwaysFadeInStable]); const hideTooltipOnEsc = useCallback(() => { var _apiRef$current2; (_apiRef$current2 = apiRef.current) === null || _apiRef$current2 === void 0 ? void 0 : _apiRef$current2.requestHide({ isImmediate: true }); }, [apiRef]); // When using top-layer, popover="auto" handles Escape natively useCloseOnEscapePress({ onClose: hideTooltipOnEsc, isDisabled: state === 'hide' || state === 'fade-out' || state === 'top-layer-exit' || fg('platform-dst-top-layer-tooltip') }); // ── Top-layer exit animation lifecycle ── // When state is 'top-layer-exit', Popover's isOpen transitions to false and // the CSS exit animation plays. The Popover's built-in `transitionend` // detection (with timeout fallback) calls `onExitFinish` when the exit // animation completes, which triggers the tooltip-manager lifecycle // (finishHideAnimation → done → onHide, setState('hide'), cleanup). const handleExitFinish = useCallback(() => { var _apiRef$current3; (_apiRef$current3 = apiRef.current) === null || _apiRef$current3 === void 0 ? void 0 : _apiRef$current3.finishHideAnimation(); }, []); // Browser dismiss (light dismiss on pointerup, or Escape). Recorded so we stay // hidden until the trigger is re-entered. Only wired up on the top-layer path, // but gated anyway to keep every use of the ref flag-checked. const handlePopoverClose = useCallback(() => { var _apiRef$current4; if (fg('platform-dst-top-layer-tooltip')) { isTopLayerPointerDismissedRef.current = true; } (_apiRef$current4 = apiRef.current) === null || _apiRef$current4 === void 0 ? void 0 : _apiRef$current4.requestHide({ isImmediate: true }); }, []); useEffect(() => { if (state === 'hide') { return noop; } if (state === 'before-fade-out') { setState('fade-out'); } const unbind = bind(window, { type: 'scroll', listener: () => { if (apiRef.current) { apiRef.current.requestHide({ isImmediate: true }); } }, options: { capture: true, passive: true, once: true } }); return unbind; }, [state]); const onMouseDown = useCallback(() => { // Native light dismiss hides on pointerup, not here. Recorded now so the // tooltip stays dismissed afterwards. if (fg('platform-dst-top-layer-tooltip')) { isTopLayerPointerDismissedRef.current = true; } // A press inside the show delay never reaches light dismiss, because no // popover is open on pointerup. Cancel the pending show so a quick click // does not surface a tooltip over content the press just changed. const shouldCancelPendingShow = stableState.current === 'hide' && fg('platform-dst-top-layer-tooltip'); if ((hideTooltipOnMouseDown || shouldCancelPendingShow) && apiRef.current) { apiRef.current.requestHide({ isImmediate: true }); } }, [hideTooltipOnMouseDown, stableState]); const onClick = useCallback(() => { if (hideTooltipOnClick && apiRef.current) { apiRef.current.requestHide({ isImmediate: true }); } }, [hideTooltipOnClick]); // Ideally we would be using onMouseEnter here, but // because we are binding the event to the target parent // we need to listen for the mouseover of all sub elements // This means when moving along a tooltip we are quickly toggling // between api.requestHide and api.keep. This it not ideal const onMouseOver = useCallback(event => { // Ignoring events from the container ref if (containerRef.current && event.target === containerRef.current) { return; } // Using prevent default as a signal that parent tooltips if (event.defaultPrevented) { return; } event.preventDefault(); // Re-arm on a real re-entry, not on a boundary crossing inside the trigger. if (isTopLayerPointerReEntry({ trigger: targetRef.current, relatedTarget: event.relatedTarget }) && fg('platform-dst-top-layer-tooltip')) { isTopLayerPointerDismissedRef.current = false; } const source = isMousePosition ? { type: 'mouse', clientX: event.clientX, clientY: event.clientY } : { type: 'keyboard' }; tryShowTooltip(source); }, [isMousePosition, tryShowTooltip]); // Ideally we would be using onMouseEnter here, but // because we are binding the event to the target parent // we need to listen for the mouseout of all sub elements // This means when moving along a tooltip we are quickly toggling // between api.requestHide and api.keep. This it not ideal const onMouseOut = useCallback(event => { // Ignoring events from the container ref if (containerRef.current && event.target === containerRef.current) { return; } // Using prevent default as a signal that parent tooltips if (event.defaultPrevented) { return; } event.preventDefault(); if (apiRef.current) { apiRef.current.requestHide({ isImmediate: false }); } }, []); const onMouseMove = isMousePosition ? event => { var _apiRef$current5; if ((_apiRef$current5 = apiRef.current) !== null && _apiRef$current5 !== void 0 && _apiRef$current5.isActive()) { apiRef.current.mousePos = { clientX: event.clientX, clientY: event.clientY }; } } : undefined; const onMouseOverTooltip = useCallback(() => { if (apiRef.current && apiRef.current.isActive()) { apiRef.current.keep(); return; } }, []); const onFocus = useCallback(e => { // Check if focus-visible // Prevents tooltips from showing when focus is not visible, // i.e., when focus is moved onto tooltip trigger inside a popup on open try { if (!e.target.matches(':focus-visible')) { return; } } catch { // Ignore errors from environments that don't support :focus-visible } // TODO: this does not play well with `hideTooltipOnMouseDown` // as "focus" will occur after the "mousedown". tryShowTooltip({ type: 'keyboard' }); }, [tryShowTooltip]); const onBlur = useCallback(() => { // Focus leaving ends the dismissal, so focus coming back can show the tooltip. if (fg('platform-dst-top-layer-tooltip')) { isTopLayerPointerDismissedRef.current = false; } if (apiRef.current) { apiRef.current.requestHide({ isImmediate: false }); } }, []); const onAnimationFinished = useCallback(transition => { // Using lastState here because motion is not picking up the latest value if (transition === 'exiting' && stableState.current === 'fade-out' && apiRef.current) { apiRef.current.finishHideAnimation(); } }, [stableState]); // Doing a cast because typescript is struggling to narrow the type const CastTargetContainer = TargetContainer; const shouldRenderTooltipPopup = state !== 'hide' && Boolean(content); const shouldRenderHiddenContent = !isScreenReaderAnnouncementDisabled && shouldRenderTooltipPopup; const shouldRenderTooltipChildren = state !== 'hide' && state !== 'fade-out' && state !== 'top-layer-exit'; const handleOpenLayerObserverCloseSignal = useCallback(() => { var _apiRef$current6; (_apiRef$current6 = apiRef.current) === null || _apiRef$current6 === void 0 ? void 0 : _apiRef$current6.requestHide({ isImmediate: true }); }, []); // Registered unconditionally so the hook order never depends on the feature // flag value. On the top-layer path the Popover primitive (used by // TopLayerTooltipPopup) registers with the observer directly, so we pass // isOpen: false to avoid double-counting (the hook is a no-op while the // layer is not open). useNotifyOpenLayerObserver({ // Layer is only visually open if both the tooltip popup (container) and children are rendered. isOpen: fg('platform-dst-top-layer-tooltip') ? false : shouldRenderTooltipPopup && shouldRenderTooltipChildren, /** * We don't strictly need to provide an onClose callback at this time, as there is * already code that handles hiding the tooltip when a drag is started (and the only * usage right now is closing all layers when the user resizes the side nav). * * However, for future-proofing and semantic reasons, it makes sense to close the tooltip * whenever the open layer observer requests a close. */ onClose: handleOpenLayerObserverCloseSignal }); const getReferenceElement = () => { var _apiRef$current7; if (isMousePosition && (_apiRef$current7 = apiRef.current) !== null && _apiRef$current7 !== void 0 && _apiRef$current7.mousePos && targetRef.current) { return getVirtualElementFromMousePos(apiRef.current.mousePos, { targetElement: targetRef.current, tooltipPosition: position }); } return targetRef.current || undefined; }; const tooltipIdForHiddenContent = useUniqueId('tooltip', shouldRenderHiddenContent); const tooltipTriggerProps = { onMouseOver, onMouseOut, onMouseMove, onMouseDown, onClick, onFocus, onBlur }; // This useEffect is purely for managing the aria attribute when using the // wrapped children approach. const isChildrenAFunction = typeof children === 'function'; useEffect(() => { if (isChildrenAFunction) { return; } // If `children` is _not_ a function, we are stepping outside of the public // API to add a `aria-describedby` attribute. const target = targetRef.current; if (!target || !tooltipIdForHiddenContent) { return; } target.setAttribute('aria-describedby', tooltipIdForHiddenContent); return () => target.removeAttribute('aria-describedby'); }, [isChildrenAFunction, tooltipIdForHiddenContent]); const hiddenContent = shouldRenderHiddenContent ? /*#__PURE__*/React.createElement("span", { "data-testid": testId ? `${testId}-hidden` : undefined, hidden: true, id: tooltipIdForHiddenContent }, typeof content === 'function' ? content({}) : content) : null; const PopperWrapper = shouldRenderToParent ? Fragment : TooltipPortal; const trigger = typeof children === 'function' ? /*#__PURE__*/ // once we deprecate the wrapped approach, we can put the aria // attribute back into the tooltipTriggerProps and make it required // instead of optional in `types` React.createElement(Fragment, null, children({ ...tooltipTriggerProps, // `testId` propagates to the trigger element so `data-testid` lands in the // rendered DOM. Required because `@atlaskit/button/new` (and other Pressable- // backed primitives) overwrite `data-testid` from spread; passing a typed // `testId` lets their own destructure pick it up directly. ...(testId ? { testId: `${testId}--container` } : {}), 'aria-describedby': tooltipIdForHiddenContent, ref: setDirectRef }), hiddenContent) : /*#__PURE__*/React.createElement(CastTargetContainer, _extends({}, tooltipTriggerProps, testId ? { 'data-testid': `${testId}--container` } : undefined, { ref: setImplicitRefFromChildren /** * TODO: Why is role="presentation" added? * - Is it only to "remove" the `Container` from screen readers? * - Why is it added only to the `Container` but not to `tooltipTriggerProps`? * - Should `role="presentation"` only be used if `shouldRenderHiddenContent == false`? */, role: "presentation" }), children, hiddenContent); if (fg('platform-dst-top-layer-tooltip')) { var _apiRef$current$mouse, _apiRef$current8; return /*#__PURE__*/React.createElement(Fragment, null, trigger, shouldRenderTooltipPopup ? /*#__PURE__*/React.createElement(TopLayerTooltipPopup, { targetRef: targetRef, tooltipPosition: tooltipPosition, mousePos: (_apiRef$current$mouse = (_apiRef$current8 = apiRef.current) === null || _apiRef$current8 === void 0 ? void 0 : _apiRef$current8.mousePos) !== null && _apiRef$current$mouse !== void 0 ? _apiRef$current$mouse : undefined, position: position, onMouseOut: onMouseOut, onMouseOverTooltip: onMouseOverTooltip, ignoreTooltipPointerEvents: ignoreTooltipPointerEvents, truncate: truncate, testId: testId, shortcut: shortcut, content: content, Container: Container, onClose: handlePopoverClose, onExitFinish: handleExitFinish, isOpen: state !== 'hide' && state !== 'top-layer-exit' }) : null); } return /*#__PURE__*/React.createElement(Fragment, null, trigger, shouldRenderTooltipPopup ? /*#__PURE__*/React.createElement(PopperWrapper, null, /*#__PURE__*/React.createElement(Popper, { placement: tooltipPosition, referenceElement: getReferenceElement(), strategy: strategy }, ({ ref, style, update, placement }) => { const direction = isMousePosition ? undefined : invertedDirection[getDirectionFromPlacement(placement)]; return /*#__PURE__*/React.createElement(ExitingPersistence, { appear: true }, shouldRenderTooltipChildren && /*#__PURE__*/React.createElement(FadeIn, { distance: "constant", entranceDirection: direction, exitDirection: direction, onFinish: onAnimationFinished, duration: state !== 'show-immediate' ? 'medium' : 'none' }, ({ className }) => /*#__PURE__*/React.createElement(Container, { ref: ref /** * "Tooltip" classname is a hook used by tests to manipulate * and hide tooltips, including in VR snapshots */ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: `Tooltip ${className}`, style: { // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766 ...style, // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766 ...(ignoreTooltipPointerEvents && { pointerEvents: 'none' }) }, truncate: truncate, placement: tooltipPosition, testId: getReferenceElement() ? testId : testId && `${testId}--unresolved`, onMouseOut: onMouseOut, onMouseOver: onMouseOverTooltip, shortcut: shortcut }, typeof content === 'function' ? content({ update }) : content))); })) : null); } export default Tooltip; const TooltipPortal = ({ children }) => { return /*#__PURE__*/React.createElement(Portal, { zIndex: tooltipZIndex }, children); }; const tooltipMouseAnimationStyles = { enter: "_j7hq1bym", exit: "_j7hq1a5s" }; /** * Top-layer tooltip popup component. * * Composes `Popover` (top-layer visibility + animation) with two positioning * hooks, of which EXACTLY ONE is enabled - two writing to the same popover would * fight for ownership, so `isEnabled` is derived from one boolean and negated: * * - `useAnchoredPopoverAtPoint` for cursor-tracking positions (`mouse`, * `mouse-x`, `mouse-y`) activated by a pointer. * - `useAnchoredPopover` for everything else, including a cursor position * activated via keyboard focus, where there is no cursor to track and * anchoring to the trigger is what keeps the tooltip beside the target. * * Exit animation is handled by `Popover`'s `isOpen` prop. When `isOpen` * transitions to `false`, the primitive calls `hidePopover()` internally and * the CSS exit animation plays via `allow-discrete`. No glue code needed. */ function TopLayerTooltipPopup({ targetRef, tooltipPosition, mousePos, position, onMouseOut, onMouseOverTooltip, ignoreTooltipPointerEvents, truncate, testId, shortcut, content, Container, onClose, onExitFinish, isOpen }) { const popoverRef = useRef(null); // Translate the legacy Popper-style placement string ("right", // "bottom-start", etc.) once and pass the same object to the hook and // to `Popover`. const placement = fromLegacyPlacement({ legacy: tooltipPosition }); const isMousePosition = position === 'mouse' || position === 'mouse-x' || position === 'mouse-y'; const isMouseStrategyActive = isMousePosition && Boolean(mousePos); // One object so the two calls cannot drift on `placement` or `isOpen`. const sharedPositioning = { popoverRef, placement, isOpen }; useAnchoredPopover({ ...sharedPositioning, anchorRef: targetRef, isEnabled: !isMouseStrategyActive }); // `getPoint()` is latched once per activation, which is per-show here because // `TopLayerTooltipPopup` mounts fresh on every show. `null` before the trigger // has mounted applies no positioning at all. useAnchoredPopoverAtPoint({ ...sharedPositioning, isEnabled: isMouseStrategyActive, getPoint: () => { if (!mousePos || !targetRef.current || !isMousePosition) { return null; } return getAnchorPoint({ cursor: mousePos, triggerRect: targetRef.current.getBoundingClientRect(), tooltipPosition: position, placement }); } }); return /*#__PURE__*/React.createElement(Popover, { ref: popoverRef, role: "tooltip", mode: "hint", isOpen: isOpen, onClose: onClose, onExitFinish: onExitFinish, testId: testId ? `${testId}--popover` : undefined, shouldAnimate: true, enteringAnimationXcss: isMouseStrategyActive && tooltipMouseAnimationStyles.enter, exitingAnimationXcss: isMouseStrategyActive && tooltipMouseAnimationStyles.exit, placement: placement }, /*#__PURE__*/React.createElement(Container // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- top-layer spike , { className: "Tooltip" // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- top-layer spike , style: ignoreTooltipPointerEvents ? { pointerEvents: 'none' } : undefined, truncate: truncate, placement: tooltipPosition, testId: testId, onMouseOut: onMouseOut, onMouseOver: onMouseOverTooltip, shortcut: shortcut, role: "presentation" }, typeof content === 'function' ? content({ update: noop }) : content)); }