diff --git a/packages/visx-xychart/src/hooks/useEventEmitter.ts b/packages/visx-xychart/src/hooks/useEventEmitter.ts
index b3f991db9..7af1c1883 100644
--- a/packages/visx-xychart/src/hooks/useEventEmitter.ts
+++ b/packages/visx-xychart/src/hooks/useEventEmitter.ts
@@ -44,7 +44,10 @@ export default function useEventEmitter(
if (emitter && eventType && handler) {
// register handler, with source filtering as needed
const handlerWithSourceFilter: Handler = (params?: HandlerParams) => {
- if (!params?.source || !sourcesRef.current || sourcesRef.current?.includes(params.source)) {
+ if (
+ !sourcesRef.current ||
+ (params?.source && sourcesRef.current?.includes(params.source))
+ ) {
handler(params);
}
};
diff --git a/packages/visx-xychart/test/hooks/useEventEmitter.test.tsx b/packages/visx-xychart/test/hooks/useEventEmitter.test.tsx
index 5eb563ca7..9efd11410 100644
--- a/packages/visx-xychart/test/hooks/useEventEmitter.test.tsx
+++ b/packages/visx-xychart/test/hooks/useEventEmitter.test.tsx
@@ -3,6 +3,10 @@ import { mount } from 'enzyme';
import useEventEmitter from '../../src/hooks/useEventEmitter';
import { EventEmitterProvider } from '../../src';
+// avoids a lot of coercing of types
+const getEvent = (eventType: string) =>
+ (new MouseEvent(eventType) as unknown) as React.PointerEvent;
+
describe('useEventEmitter', () => {
it('should be defined', () => {
expect(useEventEmitter).toBeDefined();
@@ -12,8 +16,8 @@ describe('useEventEmitter', () => {
expect.assertions(1);
const Component = () => {
- const registry = useEventEmitter();
- expect(registry).toEqual(expect.any(Function));
+ const emitter = useEventEmitter();
+ expect(emitter).toEqual(expect.any(Function));
return null;
};
@@ -33,9 +37,41 @@ describe('useEventEmitter', () => {
useEffect(() => {
if (emit) {
- // @ts-ignore not a React.MouseEvent
- emit('pointermove', new MouseEvent('pointermove'));
+ emit('pointermove', getEvent('pointermove'));
+ expect(listener).toHaveBeenCalledTimes(1);
+ }
+ });
+
+ return null;
+ };
+
+ mount(
+
+
+ ,
+ );
+ });
+
+ it('should filter invalid sources if specified', () => {
+ expect.assertions(4);
+
+ const Component = () => {
+ const eventType = 'pointermove';
+ const sourceId = 'sourceId';
+ const listener = jest.fn();
+ const filteredListener = jest.fn();
+ const emit = useEventEmitter();
+ useEventEmitter('pointermove', listener);
+ useEventEmitter('pointermove', filteredListener, [sourceId]);
+
+ useEffect(() => {
+ if (emit) {
+ emit(eventType, getEvent(eventType));
expect(listener).toHaveBeenCalledTimes(1);
+ expect(filteredListener).toHaveBeenCalledTimes(0);
+ emit(eventType, getEvent(eventType), sourceId);
+ expect(listener).toHaveBeenCalledTimes(2);
+ expect(filteredListener).toHaveBeenCalledTimes(1);
}
});
diff --git a/packages/visx-xychart/test/hooks/usePointerEventEmitters.test.tsx b/packages/visx-xychart/test/hooks/usePointerEventEmitters.test.tsx
new file mode 100644
index 000000000..c5c162489
--- /dev/null
+++ b/packages/visx-xychart/test/hooks/usePointerEventEmitters.test.tsx
@@ -0,0 +1,55 @@
+import React, { useEffect } from 'react';
+import { mount } from 'enzyme';
+import { EventEmitterProvider, useEventEmitter } from '../../src';
+import usePointerEventEmitters from '../../src/hooks/usePointerEventEmitters';
+
+describe('usePointerEventEmitters', () => {
+ it('should be defined', () => {
+ expect(usePointerEventEmitters).toBeDefined();
+ });
+
+ it('should provide an emitter for each callback specified', () => {
+ expect.assertions(1);
+
+ const Component = () => {
+ const emitters = usePointerEventEmitters({ source: 'visx', onPointerOut: false });
+ expect(emitters).toEqual({
+ onPointerMove: expect.any(Function),
+ onPointerOut: undefined,
+ onPointerUp: expect.any(Function),
+ });
+ return null;
+ };
+
+ mount(
+
+
+ ,
+ );
+ });
+ it('emitters should emit events', () => {
+ expect.assertions(1);
+
+ const Component = () => {
+ const source = 'sourceId';
+ const listener = jest.fn();
+ useEventEmitter('pointerup', listener, [source]);
+ const emitters = usePointerEventEmitters({ source });
+
+ useEffect(() => {
+ if (emitters.onPointerUp) {
+ emitters.onPointerUp((new MouseEvent('pointerup') as unknown) as React.PointerEvent);
+ expect(listener).toHaveBeenCalledTimes(1);
+ }
+ });
+
+ return null;
+ };
+
+ mount(
+
+
+ ,
+ );
+ });
+});
diff --git a/packages/visx-xychart/test/hooks/usePointerEventHandlers.test.tsx b/packages/visx-xychart/test/hooks/usePointerEventHandlers.test.tsx
new file mode 100644
index 000000000..b33b4a529
--- /dev/null
+++ b/packages/visx-xychart/test/hooks/usePointerEventHandlers.test.tsx
@@ -0,0 +1,103 @@
+import React, { useEffect } from 'react';
+import { mount } from 'enzyme';
+import { EventEmitterProvider, useEventEmitter, DataContext } from '../../src';
+import usePointerEventHandlers, {
+ POINTER_EVENTS_ALL,
+} from '../../src/hooks/usePointerEventHandlers';
+import getDataContext from '../mocks/getDataContext';
+
+const series1 = { key: 'series1', data: [{}], xAccessor: () => 4, yAccessor: () => 7 };
+const series2 = { key: 'series2', data: [{}], xAccessor: () => 4, yAccessor: () => 7 };
+// avoids a lot of coercing of types
+const getEvent = (eventType: string) =>
+ (new MouseEvent(eventType) as unknown) as React.PointerEvent;
+
+describe('usePointerEventHandlers', () => {
+ function setup(children: React.ReactNode) {
+ return mount(
+
+ {children}
+ ,
+ );
+ }
+
+ it('should be defined', () => {
+ expect(usePointerEventHandlers).toBeDefined();
+ });
+ it('should invoke handlers for each pointer event handler specified', () => {
+ expect.assertions(3);
+
+ const Component = () => {
+ const sourceId = 'sourceId';
+ const pointerMoveListener = jest.fn();
+ const pointerOutListener = jest.fn();
+ const pointerUpListener = jest.fn();
+ const emit = useEventEmitter();
+
+ usePointerEventHandlers({
+ sources: [sourceId],
+ dataKey: series1.key,
+ onPointerMove: pointerMoveListener,
+ onPointerOut: pointerOutListener,
+ onPointerUp: pointerUpListener,
+ });
+
+ useEffect(() => {
+ if (emit) {
+ emit('pointermove', getEvent('pointermove'), sourceId);
+ emit('pointermove', getEvent('pointermove'), 'invalidSource');
+ expect(pointerMoveListener).toHaveBeenCalledTimes(1);
+
+ emit('pointerout', getEvent('pointerout'), sourceId);
+ emit('pointerout', getEvent('pointerout'), 'invalidSource');
+ expect(pointerOutListener).toHaveBeenCalledTimes(1);
+
+ emit('pointerup', getEvent('pointerup'), sourceId);
+ emit('pointerup', getEvent('pointerup'), 'invalidSource');
+ expect(pointerUpListener).toHaveBeenCalledTimes(1);
+ }
+ });
+
+ return null;
+ };
+
+ setup();
+ });
+
+ it('should invoke handlers once for each dataKey specified', () => {
+ expect.assertions(4);
+
+ const Component = () => {
+ const sourceId = 'sourceId';
+ const pointerMoveListenerAll = jest.fn();
+ const pointerMoveListenerMultipleKeys = jest.fn();
+ const emit = useEventEmitter();
+
+ usePointerEventHandlers({
+ sources: [sourceId],
+ dataKey: POINTER_EVENTS_ALL,
+ onPointerMove: pointerMoveListenerAll,
+ });
+ usePointerEventHandlers({
+ sources: [sourceId],
+ dataKey: [series1.key, series2.key],
+ onPointerMove: pointerMoveListenerMultipleKeys,
+ });
+
+ useEffect(() => {
+ if (emit) {
+ emit('pointermove', getEvent('pointermove'), sourceId);
+ expect(pointerMoveListenerAll).toHaveBeenCalledTimes(2);
+ expect(pointerMoveListenerMultipleKeys).toHaveBeenCalledTimes(2);
+ emit('pointermove', getEvent('pointermove'), 'invalidSource');
+ expect(pointerMoveListenerAll).toHaveBeenCalledTimes(2);
+ expect(pointerMoveListenerMultipleKeys).toHaveBeenCalledTimes(2);
+ }
+ });
+
+ return null;
+ };
+
+ setup();
+ });
+});