From b4039ec40aa6612d1e1cc9fdb8001bad92c0fdcd Mon Sep 17 00:00:00 2001 From: JonySamarelli <74078324+JonySamarelli@users.noreply.github.com> Date: Sun, 10 Mar 2024 20:22:27 -0300 Subject: [PATCH] Add Swiper utility function for touch swipe events (#85) * feat: Add Swiper utility function for touch swipe events * test: Add swipe tests * refactor: eslint configuration and refactor based on a review * fix: update the method to assign the mock to event target * feat: allow configure deadZone * feat: add destroy method --------- Co-authored-by: Alecell Co-authored-by: Diogo Ferreira Reis <88992612+diogocaronte@users.noreply.github.com> --- .eslintrc.json | 3 +- src/utils/swiper.js | 73 +++++++++++++++++++++ src/utils/swiper.spec.js | 133 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 src/utils/swiper.js create mode 100644 src/utils/swiper.spec.js diff --git a/.eslintrc.json b/.eslintrc.json index a5079f6b..16649cf6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -8,6 +8,7 @@ }, "rules": { "no-console": "warn", - "object-curly-newline": "off" + "object-curly-newline": "off", + "import/prefer-default-export": "off" } } diff --git a/src/utils/swiper.js b/src/utils/swiper.js new file mode 100644 index 00000000..3236d1e1 --- /dev/null +++ b/src/utils/swiper.js @@ -0,0 +1,73 @@ +export function initializeSwiper({ deadZone = 50 } = {}) { + const coordinates = { + $element: null, + xDown: null, + yDown: null, + xUp: null, + yUp: null, + }; + + const dispatchSwipeLeft = () => { + const event = new Event('swipe-left'); + coordinates.$element.dispatchEvent(event); + }; + + const dispatchSwipeRight = () => { + const event = new Event('swipe-right'); + coordinates.$element.dispatchEvent(event); + }; + + const dispatchSwipeUp = () => { + const event = new Event('swipe-up'); + coordinates.$element.dispatchEvent(event); + }; + + const dispatchSwipeDown = () => { + const event = new Event('swipe-down'); + coordinates.$element.dispatchEvent(event); + }; + + function swipeDirection() { + const xDiff = coordinates.xUp - coordinates.xDown; + const yDiff = coordinates.yUp - coordinates.yDown; + + const isHorizontal = Math.abs(xDiff) > Math.abs(yDiff); + const isLeft = xDiff < 0; + const isRight = xDiff > 0; + const isUp = yDiff < 0; + const isDown = yDiff > 0; + + if (isHorizontal && isLeft) dispatchSwipeLeft(); + if (isHorizontal && isRight) dispatchSwipeRight(); + if (!isHorizontal && isUp) dispatchSwipeUp(); + if (!isHorizontal && isDown) dispatchSwipeDown(); + } + + function handleTouchStart(event) { + coordinates.$element = event.target; + + coordinates.xDown = event.touches[0].clientX; + coordinates.yDown = event.touches[0].clientY; + } + + function handleTouchEnd(event) { + coordinates.xUp = event.changedTouches[0].clientX; + coordinates.yUp = event.changedTouches[0].clientY; + + const xDiff = coordinates.xUp - coordinates.xDown; + const yDiff = coordinates.yUp - coordinates.yDown; + + + const didSwipe = Math.abs(xDiff) > deadZone || Math.abs(yDiff) > deadZone; + + if (didSwipe) swipeDirection(); + } + + window.addEventListener('touchstart', handleTouchStart, false); + window.addEventListener('touchend', handleTouchEnd, false); + + return function destroy() { + window.removeEventListener('touchstart', handleTouchStart); + window.removeEventListener('touchend', handleTouchEnd); + } +} diff --git a/src/utils/swiper.spec.js b/src/utils/swiper.spec.js new file mode 100644 index 00000000..35b5709e --- /dev/null +++ b/src/utils/swiper.spec.js @@ -0,0 +1,133 @@ +import { describe, expect, it, beforeEach } from 'vitest'; +import { initializeSwiper } from './swiper'; + +describe('Swiper', () => { + initializeSwiper(); + const mockElement = document.createElement('div'); + const touchStartEvent = new Event('touchstart'); + const touchEndEvent = new Event('touchend'); + Object.defineProperty(touchStartEvent, 'target', { + writable: false, + value: mockElement, + }); + + let eventFired = { + swipeLeft: false, + swipeRight: false, + swipeUp: false, + swipeDown: false, + }; + + mockElement.addEventListener('swipe-left', () => { + eventFired.swipeLeft = true; + }); + mockElement.addEventListener('swipe-right', () => { + eventFired.swipeRight = true; + }); + mockElement.addEventListener('swipe-up', () => { + eventFired.swipeUp = true; + }); + mockElement.addEventListener('swipe-down', () => { + eventFired.swipeDown = true; + }); + + beforeEach(() => { + eventFired = { + swipeLeft: false, + swipeRight: false, + swipeUp: false, + swipeDown: false, + }; + }); + + it('dispatches swipe-left event', () => { + touchStartEvent.touches = [{ clientX: 200, clientY: 0 }]; + touchEndEvent.changedTouches = [{ clientX: 0, clientY: 100 }]; + + window.dispatchEvent(touchStartEvent); + window.dispatchEvent(touchEndEvent); + + expect(eventFired.swipeLeft).toBe(true); + expect(eventFired.swipeRight).toBe(false); + expect(eventFired.swipeUp).toBe(false); + expect(eventFired.swipeDown).toBe(false); + }); + + it('dispatches swipe-right event', () => { + touchStartEvent.touches = [{ clientX: 0, clientY: 0 }]; + touchEndEvent.changedTouches = [{ clientX: 200, clientY: 100 }]; + + window.dispatchEvent(touchStartEvent); + window.dispatchEvent(touchEndEvent); + + expect(eventFired.swipeLeft).toBe(false); + expect(eventFired.swipeRight).toBe(true); + expect(eventFired.swipeUp).toBe(false); + expect(eventFired.swipeDown).toBe(false); + }); + + it('dispatches swipe-up event', () => { + touchStartEvent.touches = [{ clientX: 0, clientY: 200 }]; + touchEndEvent.changedTouches = [{ clientX: 100, clientY: 0 }]; + + window.dispatchEvent(touchStartEvent); + window.dispatchEvent(touchEndEvent); + + expect(eventFired.swipeLeft).toBe(false); + expect(eventFired.swipeRight).toBe(false); + expect(eventFired.swipeUp).toBe(true); + expect(eventFired.swipeDown).toBe(false); + }); + + it('dispatches swipe-down event', () => { + touchStartEvent.touches = [{ clientX: 0, clientY: 0 }]; + touchEndEvent.changedTouches = [{ clientX: 100, clientY: 200 }]; + + window.dispatchEvent(touchStartEvent); + window.dispatchEvent(touchEndEvent); + + expect(eventFired.swipeLeft).toBe(false); + expect(eventFired.swipeRight).toBe(false); + expect(eventFired.swipeUp).toBe(false); + expect(eventFired.swipeDown).toBe(true); + }); + + it('does not dispatch any event when swipe is not detected', () => { + touchStartEvent.touches = [{ clientX: 0, clientY: 0 }]; + touchEndEvent.changedTouches = [{ clientX: 0, clientY: 0 }]; + + window.dispatchEvent(touchStartEvent); + window.dispatchEvent(touchEndEvent); + + expect(eventFired.swipeLeft).toBe(false); + expect(eventFired.swipeRight).toBe(false); + expect(eventFired.swipeUp).toBe(false); + expect(eventFired.swipeDown).toBe(false); + }); + + it('does not dispatch any event when swipe is not long enough', () => { + touchStartEvent.touches = [{ clientX: 0, clientY: 0 }]; + touchEndEvent.changedTouches = [{ clientX: 50, clientY: 50 }]; + + window.dispatchEvent(touchStartEvent); + window.dispatchEvent(touchEndEvent); + + expect(eventFired.swipeLeft).toBe(false); + expect(eventFired.swipeRight).toBe(false); + expect(eventFired.swipeUp).toBe(false); + expect(eventFired.swipeDown).toBe(false); + }); + + it('dispatches vertical event when direction is esqual', () => { + touchStartEvent.touches = [{ clientX: 0, clientY: 0 }]; + touchEndEvent.changedTouches = [{ clientX: 51, clientY: 51 }]; + + window.dispatchEvent(touchStartEvent); + window.dispatchEvent(touchEndEvent); + + expect(eventFired.swipeLeft).toBe(false); + expect(eventFired.swipeRight).toBe(false); + expect(eventFired.swipeUp).toBe(false); + expect(eventFired.swipeDown).toBe(true); + }); +});