mirror of
https://github.com/twbs/bootstrap.git
synced 2025-01-18 10:52:19 +01:00
Extract Carousel's swipe functionality to a separate Class (#32999)
This commit is contained in:
parent
b21c7ccbb7
commit
8ec6c94522
@ -54,7 +54,7 @@
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.min.js",
|
||||
"maxSize": "16 kB"
|
||||
"maxSize": "16.25 kB"
|
||||
}
|
||||
],
|
||||
"ci": {
|
||||
|
@ -8,9 +8,9 @@
|
||||
import {
|
||||
defineJQueryPlugin,
|
||||
getElementFromSelector,
|
||||
getNextActiveElement,
|
||||
isRTL,
|
||||
isVisible,
|
||||
getNextActiveElement,
|
||||
reflow,
|
||||
triggerTransitionEnd,
|
||||
typeCheckConfig
|
||||
@ -18,6 +18,7 @@ import {
|
||||
import EventHandler from './dom/event-handler'
|
||||
import Manipulator from './dom/manipulator'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
import Swipe from './util/swipe'
|
||||
import BaseComponent from './base-component'
|
||||
|
||||
/**
|
||||
@ -34,7 +35,6 @@ const DATA_API_KEY = '.data-api'
|
||||
const ARROW_LEFT_KEY = 'ArrowLeft'
|
||||
const ARROW_RIGHT_KEY = 'ArrowRight'
|
||||
const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
|
||||
const SWIPE_THRESHOLD = 40
|
||||
|
||||
const Default = {
|
||||
interval: 5000,
|
||||
@ -69,11 +69,6 @@ const EVENT_SLID = `slid${EVENT_KEY}`
|
||||
const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
|
||||
const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`
|
||||
const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`
|
||||
const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`
|
||||
const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`
|
||||
const EVENT_TOUCHEND = `touchend${EVENT_KEY}`
|
||||
const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`
|
||||
const EVENT_POINTERUP = `pointerup${EVENT_KEY}`
|
||||
const EVENT_DRAG_START = `dragstart${EVENT_KEY}`
|
||||
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
|
||||
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
|
||||
@ -85,7 +80,6 @@ const CLASS_NAME_END = 'carousel-item-end'
|
||||
const CLASS_NAME_START = 'carousel-item-start'
|
||||
const CLASS_NAME_NEXT = 'carousel-item-next'
|
||||
const CLASS_NAME_PREV = 'carousel-item-prev'
|
||||
const CLASS_NAME_POINTER_EVENT = 'pointer-event'
|
||||
|
||||
const SELECTOR_ACTIVE = '.active'
|
||||
const SELECTOR_ACTIVE_ITEM = '.active.carousel-item'
|
||||
@ -97,9 +91,6 @@ const SELECTOR_INDICATOR = '[data-bs-target]'
|
||||
const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'
|
||||
const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]'
|
||||
|
||||
const POINTER_TYPE_TOUCH = 'touch'
|
||||
const POINTER_TYPE_PEN = 'pen'
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
@ -115,14 +106,10 @@ class Carousel extends BaseComponent {
|
||||
this._isPaused = false
|
||||
this._isSliding = false
|
||||
this.touchTimeout = null
|
||||
this.touchStartX = 0
|
||||
this.touchDeltaX = 0
|
||||
this._swipeHelper = null
|
||||
|
||||
this._config = this._getConfig(config)
|
||||
this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)
|
||||
this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
|
||||
this._pointerEvent = Boolean(window.PointerEvent)
|
||||
|
||||
this._addEventListeners()
|
||||
}
|
||||
|
||||
@ -214,6 +201,14 @@ class Carousel extends BaseComponent {
|
||||
this._slide(order, this._items[index])
|
||||
}
|
||||
|
||||
dispose() {
|
||||
if (this._swipeHelper) {
|
||||
this._swipeHelper.dispose()
|
||||
}
|
||||
|
||||
super.dispose()
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_getConfig(config) {
|
||||
@ -226,24 +221,6 @@ class Carousel extends BaseComponent {
|
||||
return config
|
||||
}
|
||||
|
||||
_handleSwipe() {
|
||||
const absDeltax = Math.abs(this.touchDeltaX)
|
||||
|
||||
if (absDeltax <= SWIPE_THRESHOLD) {
|
||||
return
|
||||
}
|
||||
|
||||
const direction = absDeltax / this.touchDeltaX
|
||||
|
||||
this.touchDeltaX = 0
|
||||
|
||||
if (!direction) {
|
||||
return
|
||||
}
|
||||
|
||||
this._slide(direction > 0 ? DIRECTION_RIGHT : DIRECTION_LEFT)
|
||||
}
|
||||
|
||||
_addEventListeners() {
|
||||
if (this._config.keyboard) {
|
||||
EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))
|
||||
@ -254,38 +231,17 @@ class Carousel extends BaseComponent {
|
||||
EventHandler.on(this._element, EVENT_MOUSELEAVE, event => this.cycle(event))
|
||||
}
|
||||
|
||||
if (this._config.touch && this._touchSupported) {
|
||||
if (this._config.touch && Swipe.isSupported()) {
|
||||
this._addTouchEventListeners()
|
||||
}
|
||||
}
|
||||
|
||||
_addTouchEventListeners() {
|
||||
const hasPointerPenTouch = event => {
|
||||
return this._pointerEvent &&
|
||||
(event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)
|
||||
for (const itemImg of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {
|
||||
EventHandler.on(itemImg, EVENT_DRAG_START, event => event.preventDefault())
|
||||
}
|
||||
|
||||
const start = event => {
|
||||
if (hasPointerPenTouch(event)) {
|
||||
this.touchStartX = event.clientX
|
||||
} else if (!this._pointerEvent) {
|
||||
this.touchStartX = event.touches[0].clientX
|
||||
}
|
||||
}
|
||||
|
||||
const move = event => {
|
||||
// ensure swiping with one touch and not pinching
|
||||
this.touchDeltaX = event.touches && event.touches.length > 1 ?
|
||||
0 :
|
||||
event.touches[0].clientX - this.touchStartX
|
||||
}
|
||||
|
||||
const end = event => {
|
||||
if (hasPointerPenTouch(event)) {
|
||||
this.touchDeltaX = event.clientX - this.touchStartX
|
||||
}
|
||||
|
||||
this._handleSwipe()
|
||||
const endCallBack = () => {
|
||||
if (this._config.pause === 'hover') {
|
||||
// If it's a touch-enabled device, mouseenter/leave are fired as
|
||||
// part of the mouse compatibility events on first tap - the carousel
|
||||
@ -304,20 +260,13 @@ class Carousel extends BaseComponent {
|
||||
}
|
||||
}
|
||||
|
||||
for (const itemImg of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {
|
||||
EventHandler.on(itemImg, EVENT_DRAG_START, event => event.preventDefault())
|
||||
const swipeConfig = {
|
||||
leftCallback: () => this._slide(DIRECTION_LEFT),
|
||||
rightCallback: () => this._slide(DIRECTION_RIGHT),
|
||||
endCallback: endCallBack
|
||||
}
|
||||
|
||||
if (this._pointerEvent) {
|
||||
EventHandler.on(this._element, EVENT_POINTERDOWN, event => start(event))
|
||||
EventHandler.on(this._element, EVENT_POINTERUP, event => end(event))
|
||||
|
||||
this._element.classList.add(CLASS_NAME_POINTER_EVENT)
|
||||
} else {
|
||||
EventHandler.on(this._element, EVENT_TOUCHSTART, event => start(event))
|
||||
EventHandler.on(this._element, EVENT_TOUCHMOVE, event => move(event))
|
||||
EventHandler.on(this._element, EVENT_TOUCHEND, event => end(event))
|
||||
}
|
||||
this._swipeHelper = new Swipe(this._element, swipeConfig)
|
||||
}
|
||||
|
||||
_keydown(event) {
|
||||
|
122
js/src/util/swipe.js
Normal file
122
js/src/util/swipe.js
Normal file
@ -0,0 +1,122 @@
|
||||
import EventHandler from '../dom/event-handler'
|
||||
import { execute, typeCheckConfig } from './index'
|
||||
|
||||
const EVENT_KEY = '.bs.swipe'
|
||||
const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`
|
||||
const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`
|
||||
const EVENT_TOUCHEND = `touchend${EVENT_KEY}`
|
||||
const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`
|
||||
const EVENT_POINTERUP = `pointerup${EVENT_KEY}`
|
||||
const POINTER_TYPE_TOUCH = 'touch'
|
||||
const POINTER_TYPE_PEN = 'pen'
|
||||
const CLASS_NAME_POINTER_EVENT = 'pointer-event'
|
||||
const SWIPE_THRESHOLD = 40
|
||||
const NAME = 'swipe'
|
||||
|
||||
const Default = {
|
||||
leftCallback: null,
|
||||
rightCallback: null,
|
||||
endCallback: null
|
||||
}
|
||||
|
||||
const DefaultType = {
|
||||
leftCallback: '(function|null)',
|
||||
rightCallback: '(function|null)',
|
||||
endCallback: '(function|null)'
|
||||
}
|
||||
|
||||
class Swipe {
|
||||
constructor(element, config) {
|
||||
this._element = element
|
||||
|
||||
if (!element || !Swipe.isSupported()) {
|
||||
return
|
||||
}
|
||||
|
||||
this._config = this._getConfig(config)
|
||||
this._deltaX = 0
|
||||
this._supportPointerEvents = Boolean(window.PointerEvent)
|
||||
this._initEvents()
|
||||
}
|
||||
|
||||
dispose() {
|
||||
EventHandler.off(this._element, EVENT_KEY)
|
||||
}
|
||||
|
||||
_start(event) {
|
||||
if (!this._supportPointerEvents) {
|
||||
this._deltaX = event.touches[0].clientX
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (this._eventIsPointerPenTouch(event)) {
|
||||
this._deltaX = event.clientX
|
||||
}
|
||||
}
|
||||
|
||||
_end(event) {
|
||||
if (this._eventIsPointerPenTouch(event)) {
|
||||
this._deltaX = event.clientX - this._deltaX
|
||||
}
|
||||
|
||||
this._handleSwipe()
|
||||
execute(this._config.endCallback)
|
||||
}
|
||||
|
||||
_move(event) {
|
||||
this._deltaX = event.touches && event.touches.length > 1 ?
|
||||
0 :
|
||||
event.touches[0].clientX - this._deltaX
|
||||
}
|
||||
|
||||
_handleSwipe() {
|
||||
const absDeltaX = Math.abs(this._deltaX)
|
||||
|
||||
if (absDeltaX <= SWIPE_THRESHOLD) {
|
||||
return
|
||||
}
|
||||
|
||||
const direction = absDeltaX / this._deltaX
|
||||
|
||||
this._deltaX = 0
|
||||
|
||||
if (!direction) {
|
||||
return
|
||||
}
|
||||
|
||||
execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback)
|
||||
}
|
||||
|
||||
_initEvents() {
|
||||
if (this._supportPointerEvents) {
|
||||
EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event))
|
||||
EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event))
|
||||
|
||||
this._element.classList.add(CLASS_NAME_POINTER_EVENT)
|
||||
} else {
|
||||
EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event))
|
||||
EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event))
|
||||
EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event))
|
||||
}
|
||||
}
|
||||
|
||||
_getConfig(config) {
|
||||
config = {
|
||||
...Default,
|
||||
...(typeof config === 'object' ? config : {})
|
||||
}
|
||||
typeCheckConfig(NAME, config, DefaultType)
|
||||
return config
|
||||
}
|
||||
|
||||
_eventIsPointerPenTouch(event) {
|
||||
return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)
|
||||
}
|
||||
|
||||
static isSupported() {
|
||||
return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
|
||||
}
|
||||
}
|
||||
|
||||
export default Swipe
|
@ -2,6 +2,7 @@ import Carousel from '../../src/carousel'
|
||||
import EventHandler from '../../src/dom/event-handler'
|
||||
import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
|
||||
import { isRTL, noop } from '../../src/util/index'
|
||||
import Swipe from '../../src/util/swipe'
|
||||
|
||||
describe('Carousel', () => {
|
||||
const { Simulator, PointerEvent } = window
|
||||
@ -301,23 +302,24 @@ describe('Carousel', () => {
|
||||
})
|
||||
|
||||
expect(carousel._addTouchEventListeners).not.toHaveBeenCalled()
|
||||
expect(carousel._swipeHelper).toBeNull()
|
||||
})
|
||||
|
||||
it('should not add touch event listeners if touch supported = false', () => {
|
||||
fixtureEl.innerHTML = '<div></div>'
|
||||
|
||||
const carouselEl = fixtureEl.querySelector('div')
|
||||
spyOn(Swipe, 'isSupported').and.returnValue(false)
|
||||
|
||||
const carousel = new Carousel(carouselEl)
|
||||
|
||||
EventHandler.off(carouselEl, '.bs-carousel')
|
||||
carousel._touchSupported = false
|
||||
EventHandler.off(carouselEl, Carousel.EVENT_KEY)
|
||||
|
||||
spyOn(carousel, '_addTouchEventListeners')
|
||||
|
||||
carousel._addEventListeners()
|
||||
|
||||
expect(carousel._addTouchEventListeners).not.toHaveBeenCalled()
|
||||
expect(carousel._swipeHelper).toBeNull()
|
||||
})
|
||||
|
||||
it('should add touch event listeners by default', () => {
|
||||
@ -566,7 +568,7 @@ describe('Carousel', () => {
|
||||
}, () => {
|
||||
restorePointerEvents()
|
||||
delete document.documentElement.ontouchstart
|
||||
expect(carousel.touchDeltaX).toEqual(0)
|
||||
expect(carousel._swipeHelper._deltaX).toEqual(0)
|
||||
done()
|
||||
})
|
||||
})
|
||||
@ -1237,19 +1239,20 @@ describe('Carousel', () => {
|
||||
|
||||
const carouselEl = fixtureEl.querySelector('#myCarousel')
|
||||
const addEventSpy = spyOn(carouselEl, 'addEventListener').and.callThrough()
|
||||
const removeEventSpy = spyOn(carouselEl, 'removeEventListener').and.callThrough()
|
||||
const removeEventSpy = spyOn(EventHandler, 'off').and.callThrough()
|
||||
|
||||
// Headless browser does not support touch events, so need to fake it
|
||||
// to test that touch events are add/removed properly.
|
||||
document.documentElement.ontouchstart = noop
|
||||
|
||||
const carousel = new Carousel(carouselEl)
|
||||
const swipeHelperSpy = spyOn(carousel._swipeHelper, 'dispose').and.callThrough()
|
||||
|
||||
const expectedArgs = [
|
||||
['keydown', jasmine.any(Function), jasmine.any(Boolean)],
|
||||
['mouseover', jasmine.any(Function), jasmine.any(Boolean)],
|
||||
['mouseout', jasmine.any(Function), jasmine.any(Boolean)],
|
||||
...(carousel._pointerEvent ?
|
||||
...(carousel._swipeHelper._supportPointerEvents ?
|
||||
[
|
||||
['pointerdown', jasmine.any(Function), jasmine.any(Boolean)],
|
||||
['pointerup', jasmine.any(Function), jasmine.any(Boolean)]
|
||||
@ -1265,7 +1268,8 @@ describe('Carousel', () => {
|
||||
|
||||
carousel.dispose()
|
||||
|
||||
expect(removeEventSpy.calls.allArgs()).toEqual(expectedArgs)
|
||||
expect(removeEventSpy).toHaveBeenCalledWith(carouselEl, Carousel.EVENT_KEY)
|
||||
expect(swipeHelperSpy).toHaveBeenCalled()
|
||||
|
||||
delete document.documentElement.ontouchstart
|
||||
})
|
||||
|
263
js/tests/unit/util/swipe.spec.js
Normal file
263
js/tests/unit/util/swipe.spec.js
Normal file
@ -0,0 +1,263 @@
|
||||
import { clearFixture, getFixture } from '../../helpers/fixture'
|
||||
import EventHandler from '../../../src/dom/event-handler'
|
||||
import Swipe from '../../../src/util/swipe'
|
||||
import { noop } from '../../../src/util'
|
||||
|
||||
describe('Swipe', () => {
|
||||
const { Simulator, PointerEvent } = window
|
||||
const originWinPointerEvent = PointerEvent
|
||||
const supportPointerEvent = Boolean(PointerEvent)
|
||||
|
||||
let fixtureEl
|
||||
let swipeEl
|
||||
const clearPointerEvents = () => {
|
||||
window.PointerEvent = null
|
||||
}
|
||||
|
||||
const restorePointerEvents = () => {
|
||||
window.PointerEvent = originWinPointerEvent
|
||||
}
|
||||
|
||||
// The headless browser does not support touch events, so we need to fake it
|
||||
// in order to test that touch events are added properly
|
||||
const defineDocumentElementOntouchstart = () => {
|
||||
document.documentElement.ontouchstart = noop
|
||||
}
|
||||
|
||||
const deleteDocumentElementOntouchstart = () => {
|
||||
delete document.documentElement.ontouchstart
|
||||
}
|
||||
|
||||
const mockSwipeGesture = (element, options = {}, type = 'touch') => {
|
||||
Simulator.setType(type)
|
||||
const _options = { deltaX: 0, deltaY: 0, ...options }
|
||||
|
||||
Simulator.gestures.swipe(element, _options)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
fixtureEl = getFixture()
|
||||
const cssStyle = [
|
||||
'<style>',
|
||||
' #fixture .pointer-event {',
|
||||
' touch-action: pan-y;',
|
||||
' }',
|
||||
' #fixture div {',
|
||||
' width: 300px;',
|
||||
' height: 300px;',
|
||||
' }',
|
||||
'</style>'
|
||||
].join('')
|
||||
|
||||
fixtureEl.innerHTML = '<div id="swipeEl"></div>' + cssStyle
|
||||
swipeEl = fixtureEl.querySelector('div')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
clearFixture()
|
||||
deleteDocumentElementOntouchstart()
|
||||
})
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should add touch event listeners by default', () => {
|
||||
defineDocumentElementOntouchstart()
|
||||
|
||||
spyOn(Swipe.prototype, '_initEvents').and.callThrough()
|
||||
const swipe = new Swipe(swipeEl)
|
||||
expect(swipe._initEvents).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not add touch event listeners if touch is not supported', () => {
|
||||
spyOn(Swipe, 'isSupported').and.returnValue(false)
|
||||
|
||||
spyOn(Swipe.prototype, '_initEvents').and.callThrough()
|
||||
const swipe = new Swipe(swipeEl)
|
||||
|
||||
expect(swipe._initEvents).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Config', () => {
|
||||
it('Test leftCallback', done => {
|
||||
const spyRight = jasmine.createSpy('spy')
|
||||
clearPointerEvents()
|
||||
defineDocumentElementOntouchstart()
|
||||
// eslint-disable-next-line no-new
|
||||
new Swipe(swipeEl, {
|
||||
leftCallback: () => {
|
||||
expect(spyRight).not.toHaveBeenCalled()
|
||||
restorePointerEvents()
|
||||
done()
|
||||
},
|
||||
rightCallback: spyRight
|
||||
})
|
||||
|
||||
mockSwipeGesture(swipeEl, {
|
||||
pos: [300, 10],
|
||||
deltaX: -300
|
||||
})
|
||||
})
|
||||
|
||||
it('Test rightCallback', done => {
|
||||
const spyLeft = jasmine.createSpy('spy')
|
||||
clearPointerEvents()
|
||||
defineDocumentElementOntouchstart()
|
||||
// eslint-disable-next-line no-new
|
||||
new Swipe(swipeEl, {
|
||||
rightCallback: () => {
|
||||
expect(spyLeft).not.toHaveBeenCalled()
|
||||
restorePointerEvents()
|
||||
done()
|
||||
},
|
||||
leftCallback: spyLeft
|
||||
})
|
||||
|
||||
mockSwipeGesture(swipeEl, {
|
||||
pos: [10, 10],
|
||||
deltaX: 300
|
||||
})
|
||||
})
|
||||
|
||||
it('Test endCallback', done => {
|
||||
clearPointerEvents()
|
||||
defineDocumentElementOntouchstart()
|
||||
let isFirstTime = true
|
||||
|
||||
const callback = () => {
|
||||
if (isFirstTime) {
|
||||
isFirstTime = false
|
||||
return
|
||||
}
|
||||
|
||||
expect().nothing()
|
||||
restorePointerEvents()
|
||||
done()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Swipe(swipeEl, {
|
||||
endCallback: callback
|
||||
})
|
||||
mockSwipeGesture(swipeEl, {
|
||||
pos: [10, 10],
|
||||
deltaX: 300
|
||||
})
|
||||
|
||||
mockSwipeGesture(swipeEl, {
|
||||
pos: [300, 10],
|
||||
deltaX: -300
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Functionality on PointerEvents', () => {
|
||||
it('should allow swipeRight and call "rightCallback" with pointer events', done => {
|
||||
if (!supportPointerEvent) {
|
||||
expect().nothing()
|
||||
done()
|
||||
return
|
||||
}
|
||||
|
||||
const style = '#fixture .pointer-event { touch-action: none !important; }'
|
||||
fixtureEl.innerHTML += style
|
||||
|
||||
defineDocumentElementOntouchstart()
|
||||
// eslint-disable-next-line no-new
|
||||
new Swipe(swipeEl, {
|
||||
rightCallback: () => {
|
||||
deleteDocumentElementOntouchstart()
|
||||
expect().nothing()
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
mockSwipeGesture(swipeEl, { deltaX: 300 }, 'pointer')
|
||||
})
|
||||
|
||||
it('should allow swipeLeft and call "leftCallback" with pointer events', done => {
|
||||
if (!supportPointerEvent) {
|
||||
expect().nothing()
|
||||
done()
|
||||
return
|
||||
}
|
||||
|
||||
const style = '#fixture .pointer-event { touch-action: none !important; }'
|
||||
fixtureEl.innerHTML += style
|
||||
|
||||
defineDocumentElementOntouchstart()
|
||||
// eslint-disable-next-line no-new
|
||||
new Swipe(swipeEl, {
|
||||
leftCallback: () => {
|
||||
expect().nothing()
|
||||
deleteDocumentElementOntouchstart()
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
mockSwipeGesture(swipeEl, {
|
||||
pos: [300, 10],
|
||||
deltaX: -300
|
||||
}, 'pointer')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Dispose', () => {
|
||||
it('should call EventHandler.off', () => {
|
||||
defineDocumentElementOntouchstart()
|
||||
spyOn(EventHandler, 'off').and.callThrough()
|
||||
const swipe = new Swipe(swipeEl)
|
||||
|
||||
swipe.dispose()
|
||||
expect(EventHandler.off).toHaveBeenCalledWith(swipeEl, '.bs.swipe')
|
||||
})
|
||||
|
||||
it('should destroy', () => {
|
||||
const addEventSpy = spyOn(fixtureEl, 'addEventListener').and.callThrough()
|
||||
const removeEventSpy = spyOn(fixtureEl, 'removeEventListener').and.callThrough()
|
||||
defineDocumentElementOntouchstart()
|
||||
|
||||
const swipe = new Swipe(fixtureEl)
|
||||
|
||||
const expectedArgs =
|
||||
swipe._supportPointerEvents ?
|
||||
[
|
||||
['pointerdown', jasmine.any(Function), jasmine.any(Boolean)],
|
||||
['pointerup', jasmine.any(Function), jasmine.any(Boolean)]
|
||||
] :
|
||||
[
|
||||
['touchstart', jasmine.any(Function), jasmine.any(Boolean)],
|
||||
['touchmove', jasmine.any(Function), jasmine.any(Boolean)],
|
||||
['touchend', jasmine.any(Function), jasmine.any(Boolean)]
|
||||
]
|
||||
|
||||
expect(addEventSpy.calls.allArgs()).toEqual(expectedArgs)
|
||||
|
||||
swipe.dispose()
|
||||
|
||||
expect(removeEventSpy.calls.allArgs()).toEqual(expectedArgs)
|
||||
|
||||
delete document.documentElement.ontouchstart
|
||||
})
|
||||
})
|
||||
|
||||
describe('"isSupported" static', () => {
|
||||
it('should return "true" if "touchstart" exists in document element)', () => {
|
||||
Object.defineProperty(window.navigator, 'maxTouchPoints', () => 0)
|
||||
defineDocumentElementOntouchstart()
|
||||
|
||||
expect(Swipe.isSupported()).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return "false" if "touchstart" not exists in document element and "navigator.maxTouchPoints" are zero (0)', () => {
|
||||
Object.defineProperty(window.navigator, 'maxTouchPoints', () => 0)
|
||||
deleteDocumentElementOntouchstart()
|
||||
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
expect().nothing()
|
||||
return
|
||||
}
|
||||
|
||||
expect(Swipe.isSupported()).toBeFalse()
|
||||
})
|
||||
})
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user