Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/twbs/bootstrap.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorJohann-S <johann.servoire@gmail.com>2018-03-04 00:04:11 +0300
committerXhmikosR <xhmikosr@gmail.com>2018-10-20 15:32:09 +0300
commitcaefd7046372e954d21550bbdadcabf98b2a86f0 (patch)
tree9e1835a7d61a8302c4dcaaff5b5a023efcae96b1 /js
parentbf573896472c83e2b85b52f6cbf606765b73cb3e (diff)
Add touch support in our carousel with HammerJS.
Diffstat (limited to 'js')
-rw-r--r--js/src/carousel.js54
-rw-r--r--js/tests/index.html4
-rw-r--r--js/tests/karma.conf.js17
-rw-r--r--js/tests/unit/.eslintrc.json4
-rw-r--r--js/tests/unit/carousel.js127
-rw-r--r--js/tests/visual/carousel.html1
6 files changed, 183 insertions, 24 deletions
diff --git a/js/src/carousel.js b/js/src/carousel.js
index fcc78af6f1..b2765ac5a8 100644
--- a/js/src/carousel.js
+++ b/js/src/carousel.js
@@ -1,4 +1,5 @@
import $ from 'jquery'
+import Hammer from 'hammerjs'
import Util from './util'
/**
@@ -23,13 +24,15 @@ const JQUERY_NO_CONFLICT = $.fn[NAME]
const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
+const HAMMER_ENABLED = typeof Hammer !== 'undefined'
const Default = {
interval : 5000,
keyboard : true,
slide : false,
pause : 'hover',
- wrap : true
+ wrap : true,
+ touch : true
}
const DefaultType = {
@@ -37,7 +40,8 @@ const DefaultType = {
keyboard : 'boolean',
slide : '(boolean|string)',
pause : '(string|boolean)',
- wrap : 'boolean'
+ wrap : 'boolean',
+ touch : 'boolean'
}
const Direction = {
@@ -55,7 +59,9 @@ const Event = {
MOUSELEAVE : `mouseleave${EVENT_KEY}`,
TOUCHEND : `touchend${EVENT_KEY}`,
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,
- CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
+ CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,
+ SWIPELEFT : 'swipeleft',
+ SWIPERIGHT : 'swiperight'
}
const ClassName = {
@@ -84,21 +90,30 @@ const Selector = {
* Class Definition
* ------------------------------------------------------------------------
*/
-
class Carousel {
constructor(element, config) {
- this._items = null
- this._interval = null
- this._activeElement = null
-
- this._isPaused = false
- this._isSliding = false
-
- this.touchTimeout = null
-
- this._config = this._getConfig(config)
- this._element = $(element)[0]
- this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)
+ this._items = null
+ this._interval = null
+ this._activeElement = null
+ this._isPaused = false
+ this._isSliding = false
+ this.touchTimeout = null
+ this.hammer = null
+
+ this._config = this._getConfig(config)
+ this._element = element
+ this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)
+ this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
+
+ if (HAMMER_ENABLED && this._touchSupported && this._config.touch) {
+ this.hammer = new Hammer(this._element, {
+ recognizers: [[
+ Hammer.Swipe, {
+ direction: Hammer.DIRECTION_HORIZONTAL
+ }
+ ]]
+ })
+ }
this._addEventListeners()
}
@@ -226,11 +241,16 @@ class Carousel {
.on(Event.KEYDOWN, (event) => this._keydown(event))
}
+ if (this.hammer) {
+ this.hammer.on(Event.SWIPELEFT, () => this.next())
+ this.hammer.on(Event.SWIPERIGHT, () => this.prev())
+ }
+
if (this._config.pause === 'hover') {
$(this._element)
.on(Event.MOUSEENTER, (event) => this.pause(event))
.on(Event.MOUSELEAVE, (event) => this.cycle(event))
- if ('ontouchstart' in document.documentElement) {
+ if (this._touchSupported) {
// If it's a touch-enabled device, mouseenter/leave are fired as
// part of the mouse compatibility events on first tap - the carousel
// would stop cycling until user tapped out of it;
diff --git a/js/tests/index.html b/js/tests/index.html
index ce4a0e3081..201e15f2a8 100644
--- a/js/tests/index.html
+++ b/js/tests/index.html
@@ -20,6 +20,7 @@
}())
</script>
<script src="../../node_modules/popper.js/dist/umd/popper.min.js"></script>
+ <script src="../../node_modules/hammerjs/hammer.min.js"></script>
<!-- QUnit -->
<link rel="stylesheet" href="../../node_modules/qunit/qunit/qunit.css" media="screen">
@@ -28,6 +29,9 @@
<!-- Sinon -->
<script src="../../node_modules/sinon/pkg/sinon-no-sourcemaps.js"></script>
+ <!-- Hammer simulator -->
+ <script src="../../node_modules/hammer-simulator/index.js"></script>
+
<script>
// Disable jQuery event aliases to ensure we don't accidentally use any of them
[
diff --git a/js/tests/karma.conf.js b/js/tests/karma.conf.js
index 070606a521..c3c64e8850 100644
--- a/js/tests/karma.conf.js
+++ b/js/tests/karma.conf.js
@@ -12,16 +12,16 @@ const jqueryFile = process.env.USE_OLD_JQUERY ? 'https://code.jquery.com/jquery-
const bundle = process.env.BUNDLE === 'true'
const browserStack = process.env.BROWSER === 'true'
-const frameworks = [
- 'qunit',
- 'sinon'
-]
-
const plugins = [
'karma-qunit',
'karma-sinon'
]
+const frameworks = [
+ 'qunit',
+ 'sinon'
+]
+
const reporters = ['dots']
const detectBrowsers = {
@@ -46,7 +46,12 @@ const customLaunchers = {
}
}
-let files = ['node_modules/popper.js/dist/umd/popper.min.js']
+let files = [
+ 'node_modules/popper.js/dist/umd/popper.min.js',
+ 'node_modules/hammerjs/hammer.min.js',
+ 'node_modules/hammer-simulator/index.js'
+]
+
const conf = {
basePath: '../..',
port: 9876,
diff --git a/js/tests/unit/.eslintrc.json b/js/tests/unit/.eslintrc.json
index 0896f406d0..a7fa64af0e 100644
--- a/js/tests/unit/.eslintrc.json
+++ b/js/tests/unit/.eslintrc.json
@@ -9,7 +9,9 @@
"sinon": false,
"Util": false,
"Alert": false,
- "Button": false
+ "Button": false,
+ "Carousel": false,
+ "Simulator": false
},
"parserOptions": {
"ecmaVersion": 5,
diff --git a/js/tests/unit/carousel.js b/js/tests/unit/carousel.js
index 20b12f4b06..0bc52219e4 100644
--- a/js/tests/unit/carousel.js
+++ b/js/tests/unit/carousel.js
@@ -1,6 +1,10 @@
$(function () {
'use strict'
+ window.Carousel = typeof bootstrap !== 'undefined' ? bootstrap.Carousel : Carousel
+
+ var touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
+
QUnit.module('carousel plugin')
QUnit.test('should be defined on jQuery object', function (assert) {
@@ -25,6 +29,20 @@ $(function () {
assert.strictEqual(typeof $.fn.carousel, 'undefined', 'carousel was set back to undefined (orig value)')
})
+ QUnit.test('should return version', function (assert) {
+ assert.expect(1)
+
+ assert.strictEqual(typeof Carousel.VERSION, 'string')
+ })
+
+ QUnit.test('should return default parameters', function (assert) {
+ assert.expect(1)
+
+ var defaultConfig = Carousel.Default
+
+ assert.strictEqual(defaultConfig.touch, true)
+ })
+
QUnit.test('should throw explicit error on undefined method', function (assert) {
assert.expect(1)
var $el = $('<div/>')
@@ -989,4 +1007,113 @@ $(function () {
}, 80)
}, 80)
})
+
+ QUnit.test('should allow swiperight and call prev', function (assert) {
+ if (!touchSupported) {
+ assert.expect(0)
+
+ return
+ }
+
+ assert.expect(2)
+ var done = assert.async()
+ document.documentElement.ontouchstart = $.noop
+
+ var carouselHTML =
+ '<div class="carousel" data-interval="false">' +
+ ' <div class="carousel-inner">' +
+ ' <div id="item" class="carousel-item">' +
+ ' <img alt="">' +
+ ' </div>' +
+ ' <div class="carousel-item active">' +
+ ' <img alt="">' +
+ ' </div>' +
+ ' </div>' +
+ '</div>'
+
+ var $carousel = $(carouselHTML)
+ $carousel.appendTo('#qunit-fixture')
+ var $item = $('#item')
+ $carousel.bootstrapCarousel()
+
+ $carousel.one('slid.bs.carousel', function () {
+ assert.ok(true, 'slid event fired')
+ assert.ok($item.hasClass('active'))
+ delete document.documentElement.ontouchstart
+ done()
+ })
+
+ Simulator.gestures.swipe($carousel[0], {
+ deltaX: 300,
+ deltaY: 0
+ })
+ })
+
+ QUnit.test('should not use HammerJS when touch option is false', function (assert) {
+ assert.expect(1)
+
+ var $carousel = $('<div></div>').appendTo('#qunit-fixture')
+ $carousel.bootstrapCarousel({
+ touch: false
+ })
+
+ var carousel = $carousel.data('bs.carousel')
+
+ assert.strictEqual(carousel.hammer, null)
+ })
+
+ QUnit.test('should use HammerJS when touch option is true', function (assert) {
+ assert.expect(1)
+
+ document.documentElement.ontouchstart = $.noop
+
+ var $carousel = $('<div></div>').appendTo('#qunit-fixture')
+ $carousel.bootstrapCarousel()
+
+ var carousel = $carousel.data('bs.carousel')
+
+ assert.ok(carousel.hammer !== null)
+ })
+
+ QUnit.test('should allow swipeleft and call next', function (assert) {
+ if (!touchSupported) {
+ assert.expect(0)
+
+ return
+ }
+
+ assert.expect(2)
+ var done = assert.async()
+ document.documentElement.ontouchstart = $.noop
+
+ var carouselHTML =
+ '<div class="carousel" data-interval="false">' +
+ ' <div class="carousel-inner">' +
+ ' <div id="item" class="carousel-item active">' +
+ ' <img alt="">' +
+ ' </div>' +
+ ' <div class="carousel-item">' +
+ ' <img alt="">' +
+ ' </div>' +
+ ' </div>' +
+ '</div>'
+
+ var $carousel = $(carouselHTML)
+ $carousel.appendTo('#qunit-fixture')
+ var $item = $('#item')
+ $carousel.bootstrapCarousel()
+
+ $carousel.one('slid.bs.carousel', function () {
+ assert.ok(true, 'slid event fired')
+ assert.ok(!$item.hasClass('active'))
+ delete document.documentElement.ontouchstart
+ done()
+ })
+
+ Simulator.gestures.swipe($carousel[0], {
+ pos: [300, 10],
+ deltaX: -300,
+ deltaY: 0
+ })
+ })
})
diff --git a/js/tests/visual/carousel.html b/js/tests/visual/carousel.html
index 630f870cf4..cd917caa62 100644
--- a/js/tests/visual/carousel.html
+++ b/js/tests/visual/carousel.html
@@ -46,6 +46,7 @@
</div>
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
+ <script src="../../../node_modules/hammerjs/hammer.min.js"></script>
<script src="../../dist/util.js"></script>
<script src="../../dist/carousel.js"></script>
<script>