UNPKG

@qazuor/react-hooks

Version:

A comprehensive collection of production-ready React hooks for modern web applications. Features type-safe implementations, extensive testing, and zero dependencies. Includes hooks for state management, browser APIs, user interactions, and development uti

129 lines (106 loc) 4.12 kB
import { act, renderHook } from '@testing-library/react'; import { useRef } from 'react'; import { useClickOutside } from '../src/hooks/useClickOutside'; describe('useClickOutside', () => { let handler: jest.Mock<void, [MouseEvent]>; let targetElement: HTMLDivElement; let outsideElement: HTMLDivElement; let body: HTMLElement; beforeEach(() => { handler = jest.fn(); targetElement = document.createElement('div'); outsideElement = document.createElement('div'); body = document.body; body.appendChild(targetElement); body.appendChild(outsideElement); }); afterEach(() => { body.removeChild(targetElement); body.removeChild(outsideElement); jest.clearAllMocks(); }); it('should handle clicks outside the target element', () => { const { result } = renderHook(() => { const ref = useRef(targetElement); useClickOutside(ref, handler); return { ref }; }); act(() => { outsideElement.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); }); expect(handler).toHaveBeenCalledTimes(1); }); it('should not trigger when clicking inside the target element', () => { const { result } = renderHook(() => { const ref = useRef(targetElement); useClickOutside(ref, handler); return { ref }; }); act(() => { targetElement.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); }); expect(handler).not.toHaveBeenCalled(); }); it('should support different event types', () => { const mouseupHandler = jest.fn(); const clickHandler = jest.fn(); const touchHandler = jest.fn(); renderHook(() => { const ref = useRef(targetElement); useClickOutside(ref, mouseupHandler, { eventType: 'mouseup' }); return { ref }; }); renderHook(() => { const ref = useRef(targetElement); useClickOutside(ref, clickHandler, { eventType: 'click' }); return { ref }; }); renderHook(() => { const ref = useRef(targetElement); useClickOutside(ref, touchHandler, { eventType: 'mousedown' }); return { ref }; }); act(() => { outsideElement.dispatchEvent(new MouseEvent('mouseup', { bubbles: true })); }); expect(mouseupHandler).toHaveBeenCalledTimes(1); expect(clickHandler).not.toHaveBeenCalled(); act(() => { outsideElement.dispatchEvent(new MouseEvent('click', { bubbles: true })); }); expect(clickHandler).toHaveBeenCalledTimes(1); act(() => { outsideElement.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); }); expect(touchHandler).toHaveBeenCalledTimes(1); }); it('should respect enabled option', () => { const { result, rerender } = renderHook( ({ enabled }) => { const ref = useRef(targetElement); useClickOutside(ref, handler, { enabled }); return { ref }; }, { initialProps: { enabled: false } } ); act(() => { outsideElement.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); }); expect(handler).not.toHaveBeenCalled(); rerender({ enabled: true }); act(() => { outsideElement.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); }); expect(handler).toHaveBeenCalledTimes(1); }); it('should cleanup event listeners on unmount', () => { const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener'); const { unmount } = renderHook(() => { const ref = useRef(targetElement); useClickOutside(ref, handler); return { ref }; }); unmount(); expect(removeEventListenerSpy).toHaveBeenCalled(); }); });