diff options
author | Johann-S <johann.servoire@gmail.com> | 2018-03-04 00:04:11 +0300 |
---|---|---|
committer | XhmikosR <xhmikosr@gmail.com> | 2018-10-20 15:32:09 +0300 |
commit | caefd7046372e954d21550bbdadcabf98b2a86f0 (patch) | |
tree | 9e1835a7d61a8302c4dcaaff5b5a023efcae96b1 /js | |
parent | bf573896472c83e2b85b52f6cbf606765b73cb3e (diff) |
Add touch support in our carousel with HammerJS.
Diffstat (limited to 'js')
-rw-r--r-- | js/src/carousel.js | 54 | ||||
-rw-r--r-- | js/tests/index.html | 4 | ||||
-rw-r--r-- | js/tests/karma.conf.js | 17 | ||||
-rw-r--r-- | js/tests/unit/.eslintrc.json | 4 | ||||
-rw-r--r-- | js/tests/unit/carousel.js | 127 | ||||
-rw-r--r-- | js/tests/visual/carousel.html | 1 |
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> |