Skip to content

Commit

Permalink
[Flare] Remove delay props from Hover (#16248)
Browse files Browse the repository at this point in the history
Moving working with delays into user-space.
  • Loading branch information
necolas authored Jul 30, 2019
1 parent 1912b4a commit e276a5e
Show file tree
Hide file tree
Showing 3 changed files with 5 additions and 259 deletions.
20 changes: 3 additions & 17 deletions packages/react-events/docs/Hover.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,6 @@ type HoverEvent = {

## Props

### delayHoverEnd: number

The duration of the delay between when hover ends and when `onHoverEnd` is
called.

### delayHoverStart: number

The duration of the delay between when hover starts and when `onHoverStart` is
called.

### disabled: boolean

Disables all `Hover` events.
Expand All @@ -58,19 +48,15 @@ Called when the element changes hover state (i.e., after `onHoverStart` and

### onHoverEnd: (e: HoverEvent) => void

Called once the element is no longer hovered. It will be cancelled if the
pointer leaves the element before the `delayHoverStart` threshold is exceeded.
Called once the element is no longer hovered.

### onHoverMove: (e: HoverEvent) => void

Called when the pointer moves within the hit bounds of the element. `onHoverMove` is
called immediately and doesn't wait for delayed `onHoverStart`.
Called when the pointer moves within the hit bounds of the element.

### onHoverStart: (e: HoverEvent) => void

Called once the element is hovered. It will not be called if the pointer leaves
the element before the `delayHoverStart` threshold is exceeded. And it will not
be called more than once before `onHoverEnd` is called.
Called once the element is hovered.

### preventDefault: boolean = true

Expand Down
45 changes: 2 additions & 43 deletions packages/react-events/src/dom/Hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ type HoverListenerProps = {|

type HoverProps = {
disabled: boolean,
delayHoverEnd: number,
delayHoverStart: number,
preventDefault: boolean,
};

Expand Down Expand Up @@ -55,9 +53,6 @@ type HoverEvent = {|
y: null | number,
|};

const DEFAULT_HOVER_END_DELAY_MS = 0;
const DEFAULT_HOVER_START_DELAY_MS = 0;

const targetEventTypes = [
'pointerover',
'pointermove',
Expand Down Expand Up @@ -136,7 +131,7 @@ function dispatchHoverStartEvents(
state.hoverEndTimeout = null;
}

const activate = () => {
if (!state.isActiveHovered) {
state.isActiveHovered = true;
const syntheticEvent = createHoverEvent(
event,
Expand All @@ -146,22 +141,6 @@ function dispatchHoverStartEvents(
);
context.dispatchEvent('onHoverStart', syntheticEvent, UserBlockingEvent);
dispatchHoverChangeEvent(event, context, props, state);
};

if (!state.isActiveHovered) {
const delayHoverStart = calculateDelayMS(
props.delayHoverStart,
0,
DEFAULT_HOVER_START_DELAY_MS,
);
if (delayHoverStart > 0) {
state.hoverStartTimeout = context.setTimeout(() => {
state.hoverStartTimeout = null;
activate();
}, delayHoverStart);
} else {
activate();
}
}
}

Expand All @@ -188,7 +167,7 @@ function dispatchHoverEndEvents(
state.hoverStartTimeout = null;
}

const deactivate = () => {
if (state.isActiveHovered) {
state.isActiveHovered = false;

const syntheticEvent = createHoverEvent(
Expand All @@ -202,29 +181,9 @@ function dispatchHoverEndEvents(
state.hoverTarget = null;
state.ignoreEmulatedMouseEvents = false;
state.isTouched = false;
};

if (state.isActiveHovered) {
const delayHoverEnd = calculateDelayMS(
props.delayHoverEnd,
0,
DEFAULT_HOVER_END_DELAY_MS,
);
if (delayHoverEnd > 0) {
state.hoverEndTimeout = context.setTimeout(() => {
deactivate();
}, delayHoverEnd);
} else {
deactivate();
}
}
}

function calculateDelayMS(delay: ?number, min = 0, fallback = 0) {
const maybeNumber = delay == null ? null : delay;
return Math.max(min, maybeNumber != null ? maybeNumber : fallback);
}

function unmountResponder(
context: ReactDOMResponderContext,
props: HoverProps,
Expand Down
199 changes: 0 additions & 199 deletions packages/react-events/src/dom/__tests__/Hover-test.internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,97 +167,6 @@ describe('Hover event responder', () => {
);
expect(onHoverStart).not.toBeCalled();
});

describe('delayHoverStart', () => {
it('can be configured', () => {
const Component = () => {
useHoverListener({
onHoverStart: onHoverStart,
});
return (
<div
ref={ref}
responders={<HoverResponder delayHoverStart={2000} />}
/>
);
};
ReactDOM.render(<Component />, container);

ref.current.dispatchEvent(createEvent('pointerover'));
jest.advanceTimersByTime(1999);
expect(onHoverStart).not.toBeCalled();
jest.advanceTimersByTime(1);
expect(onHoverStart).toHaveBeenCalledTimes(1);
});

it('is reset if "pointerout" is dispatched during a delay', () => {
const Component = () => {
useHoverListener({
onHoverStart: onHoverStart,
});
return (
<div
ref={ref}
responders={<HoverResponder delayHoverStart={500} />}
/>
);
};
ReactDOM.render(<Component />, container);

ref.current.dispatchEvent(createEvent('pointerover'));
jest.advanceTimersByTime(499);
ref.current.dispatchEvent(createEvent('pointerout'));
jest.advanceTimersByTime(1);
expect(onHoverStart).not.toBeCalled();
ref.current.dispatchEvent(createEvent('pointerover'));
jest.runAllTimers();
expect(onHoverStart).toHaveBeenCalledTimes(1);
});

it('onHoverStart is called synchronously if delay is 0ms', () => {
const Component = () => {
useHoverListener({
onHoverStart: onHoverStart,
});
return (
<div
ref={ref}
responders={<HoverResponder delayHoverStart={0} />}
/>
);
};
ReactDOM.render(<Component />, container);

ref.current.dispatchEvent(createEvent('pointerover'));
expect(onHoverStart).toHaveBeenCalledTimes(1);
});

it('onHoverStart is only called once per active hover', () => {
const Component = () => {
useHoverListener({
onHoverStart: onHoverStart,
});
return (
<div
ref={ref}
responders={
<HoverResponder delayHoverStart={500} delayHoverEnd={100} />
}
/>
);
};
ReactDOM.render(<Component />, container);

ref.current.dispatchEvent(createEvent('pointerover'));
jest.advanceTimersByTime(500);
expect(onHoverStart).toHaveBeenCalledTimes(1);
ref.current.dispatchEvent(createEvent('pointerout'));
jest.advanceTimersByTime(10);
ref.current.dispatchEvent(createEvent('pointerover'));
jest.runAllTimers();
expect(onHoverStart).toHaveBeenCalledTimes(1);
});
});
});

describe('onHoverChange', () => {
Expand Down Expand Up @@ -399,114 +308,6 @@ describe('Hover event responder', () => {
ref.current.dispatchEvent(createEvent('mouseout'));
expect(onHoverEnd).not.toBeCalled();
});

describe('delayHoverEnd', () => {
it('can be configured', () => {
const Component = () => {
useHoverListener({
onHoverEnd,
});
return (
<div
ref={ref}
responders={<HoverResponder delayHoverEnd={2000} />}
/>
);
};
ReactDOM.render(<Component />, container);

ref.current.dispatchEvent(createEvent('pointerover'));
ref.current.dispatchEvent(createEvent('pointerout'));
jest.advanceTimersByTime(1999);
expect(onHoverEnd).not.toBeCalled();
jest.advanceTimersByTime(1);
expect(onHoverEnd).toHaveBeenCalledTimes(1);
});

it('delayHoverEnd is called synchronously if delay is 0ms', () => {
const Component = () => {
useHoverListener({
onHoverEnd,
});
return (
<div ref={ref} responders={<HoverResponder delayHoverEnd={0} />} />
);
};
ReactDOM.render(<Component />, container);

ref.current.dispatchEvent(createEvent('pointerover'));
ref.current.dispatchEvent(createEvent('pointerout'));
expect(onHoverEnd).toHaveBeenCalledTimes(1);
});

it('onHoverEnd is only called once per active hover', () => {
const Component = () => {
useHoverListener({
onHoverEnd,
});
return (
<div
ref={ref}
responders={<HoverResponder delayHoverEnd={500} />}
/>
);
};
ReactDOM.render(<Component />, container);

ref.current.dispatchEvent(createEvent('pointerover'));
ref.current.dispatchEvent(createEvent('pointerout'));
jest.advanceTimersByTime(499);
ref.current.dispatchEvent(createEvent('pointerover'));
jest.advanceTimersByTime(100);
ref.current.dispatchEvent(createEvent('pointerout'));
jest.runAllTimers();
expect(onHoverEnd).toHaveBeenCalledTimes(1);
});

it('onHoverEnd is not called if "pointerover" is dispatched during a delay', () => {
const Component = () => {
useHoverListener({
onHoverEnd,
});
return (
<div
ref={ref}
responders={<HoverResponder delayHoverEnd={500} />}
/>
);
};
ReactDOM.render(<Component />, container);

ref.current.dispatchEvent(createEvent('pointerover'));
ref.current.dispatchEvent(createEvent('pointerout'));
jest.advanceTimersByTime(499);
ref.current.dispatchEvent(createEvent('pointerover'));
jest.advanceTimersByTime(1);
expect(onHoverEnd).not.toBeCalled();
});

it('onHoverEnd is not called if there was no active hover', () => {
const Component = () => {
useHoverListener({
onHoverEnd,
});
return (
<div
ref={ref}
responders={
<HoverResponder delayHoverStart={500} delayHoverEnd={100} />
}
/>
);
};
ReactDOM.render(<Component />, container);

ref.current.dispatchEvent(createEvent('pointerover'));
ref.current.dispatchEvent(createEvent('pointerout'));
jest.runAllTimers();
expect(onHoverEnd).not.toBeCalled();
});
});
});

describe('onHoverMove', () => {
Expand Down

0 comments on commit e276a5e

Please sign in to comment.