diff --git a/packages/visx-event/src/getXAndYFromEvent.ts b/packages/visx-event/src/getXAndYFromEvent.ts index ef2c186ed..aa8443d92 100644 --- a/packages/visx-event/src/getXAndYFromEvent.ts +++ b/packages/visx-event/src/getXAndYFromEvent.ts @@ -1,5 +1,5 @@ import { EventType } from './types'; -import { isTouchEvent } from './typeGuards'; +import { isMouseEvent, isTouchEvent } from './typeGuards'; const DEFAULT_POINT = { x: 0, y: 0 }; @@ -15,8 +15,22 @@ export default function getXAndYFromEvent(event?: EventType) { : { ...DEFAULT_POINT }; } + if (isMouseEvent(event)) { + return { + x: event.clientX, + y: event.clientY, + }; + } + + // for focus events try to extract the center position of the target element + const target = event?.target; + const boundingClientRect = + target && 'getBoundingClientRect' in target ? target.getBoundingClientRect() : null; + + if (!boundingClientRect) return { ...DEFAULT_POINT }; + return { - x: event.clientX, - y: event.clientY, + x: boundingClientRect.x + boundingClientRect.width / 2, + y: boundingClientRect.y + boundingClientRect.height / 2, }; } diff --git a/packages/visx-event/src/typeGuards.ts b/packages/visx-event/src/typeGuards.ts index f316e075e..83adece8d 100644 --- a/packages/visx-event/src/typeGuards.ts +++ b/packages/visx-event/src/typeGuards.ts @@ -23,6 +23,11 @@ export function isTouchEvent(event?: EventType): event is TouchEvent | React.Tou return !!event && 'changedTouches' in event; } +// functional definition of MouseEvent +export function isMouseEvent(event?: EventType): event is MouseEvent | React.MouseEvent { + return !!event && 'clientX' in event; +} + // functional definition of event export function isEvent(event?: EventType | Element): event is EventType { return ( diff --git a/packages/visx-event/src/types.ts b/packages/visx-event/src/types.ts index 8ec87341d..8c1a47f92 100644 --- a/packages/visx-event/src/types.ts +++ b/packages/visx-event/src/types.ts @@ -1 +1,7 @@ -export type EventType = MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent; +export type EventType = + | MouseEvent + | TouchEvent + | FocusEvent + | React.MouseEvent + | React.TouchEvent + | React.FocusEvent; diff --git a/packages/visx-event/test/getXandYFromEvent.test.ts b/packages/visx-event/test/getXandYFromEvent.test.ts index 9c04539e5..337a66b2a 100644 --- a/packages/visx-event/test/getXandYFromEvent.test.ts +++ b/packages/visx-event/test/getXandYFromEvent.test.ts @@ -1,7 +1,7 @@ import getXAndYFromEvent from '../src/getXAndYFromEvent'; describe('getXAndYFromEvent()', () => { - test('it should return { x: 0, y: 0 } if no event argument', () => { + it('should return { x: 0, y: 0 } if no event argument', () => { const result = getXAndYFromEvent(); // @ts-ignore const result2 = getXAndYFromEvent(null); @@ -9,13 +9,13 @@ describe('getXAndYFromEvent()', () => { expect(result2).toEqual({ x: 0, y: 0 }); }); - test('it should return { x, y } for mouse events', () => { + it('should return { x, y } for mouse events', () => { const e = { clientX: 0, clientY: 0 }; const result = getXAndYFromEvent(e as MouseEvent); expect(result).toEqual({ x: e.clientX, y: e.clientY }); }); - test('it should return { x, y } for touch events with changedTouches', () => { + it('should return { x, y } for touch events with changedTouches', () => { const touch0 = { clientX: 0, clientY: 0 }; const touch1 = { clientX: 1, clientY: 1 }; const e = { changedTouches: [touch0, touch1] }; @@ -23,9 +23,15 @@ describe('getXAndYFromEvent()', () => { expect(result).toEqual({ x: touch0.clientX, y: touch0.clientY }); }); - test('it should return { x: 0, y: 0 } for touch events with no changedTouches', () => { + it('should return { x: 0, y: 0 } for touch events with no changedTouches', () => { const e = { changedTouches: [] }; const result = getXAndYFromEvent((e as unknown) as TouchEvent); expect(result).toEqual({ x: 0, y: 0 }); }); + + it('should return the middle of an element for focus events', () => { + const e = { target: { getBoundingClientRect: () => ({ x: 5, y: 5, width: 10, height: 2 }) } }; + const result = getXAndYFromEvent((e as unknown) as FocusEvent); + expect(result).toEqual({ x: 10, y: 6 }); + }); });