diff --git a/__tests__/hooks/useIsTouchDevice.test.ts b/__tests__/hooks/useIsTouchDevice.test.ts index 3dffe50545..5bc0e2f54e 100644 --- a/__tests__/hooks/useIsTouchDevice.test.ts +++ b/__tests__/hooks/useIsTouchDevice.test.ts @@ -5,8 +5,10 @@ describe("useIsTouchDevice", () => { let addEventListenerSpy: jest.SpyInstance; let removeEventListenerSpy: jest.SpyInstance; let touchStartHandler: EventListener | null = null; + let originalMaxTouchPoints: number | undefined; beforeEach(() => { + originalMaxTouchPoints = (globalThis.navigator as Navigator | undefined)?.maxTouchPoints; addEventListenerSpy = jest.spyOn(globalThis, "addEventListener").mockImplementation((event, handler) => { if (event === "touchstart") { touchStartHandler = handler as EventListener; @@ -19,6 +21,20 @@ describe("useIsTouchDevice", () => { addEventListenerSpy.mockRestore(); removeEventListenerSpy.mockRestore(); touchStartHandler = null; + if (typeof originalMaxTouchPoints === "number") { + Object.defineProperty(globalThis.navigator, "maxTouchPoints", { + value: originalMaxTouchPoints, + configurable: true, + }); + } else { + // Ensure tests don't leak touch points across cases. + try { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete (globalThis.navigator as any).maxTouchPoints; + } catch { + // ignore + } + } jest.restoreAllMocks(); }); @@ -46,6 +62,32 @@ describe("useIsTouchDevice", () => { expect(addEventListenerSpy).not.toHaveBeenCalledWith("touchstart", expect.any(Function), expect.any(Object)); }); + it("returns true initially when maxTouchPoints > 0 and no fine pointer", () => { + Object.defineProperty(globalThis.navigator, "maxTouchPoints", { value: 10, configurable: true }); + Object.defineProperty(globalThis, "matchMedia", { + writable: true, + value: jest.fn(() => ({ matches: false })), + }); + + const { result } = renderHook(() => useIsTouchDevice()); + expect(result.current).toBe(true); + expect(addEventListenerSpy).not.toHaveBeenCalledWith("touchstart", expect.any(Function), expect.any(Object)); + }); + + it("returns false when a fine pointer exists even if maxTouchPoints > 0", () => { + Object.defineProperty(globalThis.navigator, "maxTouchPoints", { value: 10, configurable: true }); + Object.defineProperty(globalThis, "matchMedia", { + writable: true, + value: jest.fn((query: string) => ({ + matches: query === "(any-pointer: fine)", + })), + }); + + const { result } = renderHook(() => useIsTouchDevice()); + expect(result.current).toBe(false); + expect(addEventListenerSpy).not.toHaveBeenCalledWith("touchstart", expect.any(Function), expect.any(Object)); + }); + it("returns false initially but switches to true after touchstart when no fine pointer", () => { Object.defineProperty(globalThis, "matchMedia", { writable: true, diff --git a/components/waves/drops/WaveDropActions.tsx b/components/waves/drops/WaveDropActions.tsx index c08df2a5bc..4f9fa27d2a 100644 --- a/components/waves/drops/WaveDropActions.tsx +++ b/components/waves/drops/WaveDropActions.tsx @@ -55,7 +55,7 @@ export default function WaveDropActions({