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();