diff --git a/packages/@react-spectrum/tooltip/stories/TooltipTrigger.stories.tsx b/packages/@react-spectrum/tooltip/stories/TooltipTrigger.stories.tsx index bcbdebd4b56..f3eec79b022 100644 --- a/packages/@react-spectrum/tooltip/stories/TooltipTrigger.stories.tsx +++ b/packages/@react-spectrum/tooltip/stories/TooltipTrigger.stories.tsx @@ -45,6 +45,12 @@ const argTypes = { max: 50000, step: 500 }, + closeDelay: { + control: 'number', + min: 0, + max: 50000, + step: 500 + }, offset: { control: 'number', min: -500, diff --git a/packages/@react-stately/tooltip/src/useTooltipTriggerState.ts b/packages/@react-stately/tooltip/src/useTooltipTriggerState.ts index 32227e8ba60..3bd6ae2121c 100644 --- a/packages/@react-stately/tooltip/src/useTooltipTriggerState.ts +++ b/packages/@react-stately/tooltip/src/useTooltipTriggerState.ts @@ -113,7 +113,10 @@ export function useTooltipTriggerState(props: TooltipTriggerProps = {}): Tooltip let warmupTooltip = () => { closeOpenTooltips(); ensureTooltipEntry(); - if (!isOpen && !globalWarmUpTimeout && !globalWarmedUp) { + if (!isOpen && !globalWarmedUp) { + if (globalWarmUpTimeout) { + return; + } globalWarmUpTimeout = setTimeout(() => { globalWarmUpTimeout = null; globalWarmedUp = true; diff --git a/packages/@react-stately/tooltip/test/useTooltipTriggerState.test.js b/packages/@react-stately/tooltip/test/useTooltipTriggerState.test.js index 6e84dd425d0..af8b4997197 100644 --- a/packages/@react-stately/tooltip/test/useTooltipTriggerState.test.js +++ b/packages/@react-stately/tooltip/test/useTooltipTriggerState.test.js @@ -214,6 +214,57 @@ describe('useTooltipTriggerState', function () { }); }); + describe('warmup delay', () => { + function TooltipTriggerWithDoubleOpen(props) { + let state = useTooltipTriggerState(props); + let ref = React.useRef(); + + let {triggerProps, tooltipProps} = useTooltipTrigger(props, state, ref); + + let onMouseEnter = (e) => { + triggerProps.onMouseEnter?.(e); + state.open(false); + }; + + return ( + + + {state.isOpen && + {props.tooltip}} + + ); + } + + it('does not open immediately when open() is called twice during warmup', () => { + let delay = 1000; + + let {queryByRole, getByRole} = render( + + Trigger + + ); + fireEvent.mouseDown(document.body); + fireEvent.mouseUp(document.body); + + let button = getByRole('button'); + + fireEvent.mouseEnter(button); + fireEvent.mouseMove(button); + + expect(onOpenChange).not.toHaveBeenCalled(); + expect(queryByRole('tooltip')).toBeNull(); + + // run halfway through the delay timer and confirm that it is still closed + act(() => jest.advanceTimersByTime(delay / 2)); + expect(queryByRole('tooltip')).toBeNull(); + + // run through the rest of the delay timer and confirm that it has opened + act(() => jest.advanceTimersByTime(delay / 2)); + expect(onOpenChange).toHaveBeenCalledWith(true); + expect(getByRole('tooltip')).toBeVisible(); + }); + }); + describe('multiple controlled tooltips', () => { it('closes previus tooltip when opening a new one', () => { let secondOnOpenChange = jest.fn();