diff --git a/src/js/bootstrap-slider.js b/src/js/bootstrap-slider.js index 3d17247c..69f3c068 100644 --- a/src/js/bootstrap-slider.js +++ b/src/js/bootstrap-slider.js @@ -1659,6 +1659,9 @@ const windowIsDefined = (typeof window === "object"); this._setDataVal(val); this._trigger('slideStop', val); + // No longer need 'dragged' after mouse up + this._state.dragged = null; + return false; }, _calculateValue: function(snapToClosestTick) { diff --git a/test/specs/DraggingHandlesSpec.js b/test/specs/DraggingHandlesSpec.js index 0a92ce3a..18c74075 100644 --- a/test/specs/DraggingHandlesSpec.js +++ b/test/specs/DraggingHandlesSpec.js @@ -1,41 +1,60 @@ describe("Dragging handles tests", function() { var testSlider; + var mouseEventArguments; + var tickOffsets; + + beforeEach(function() { + // Create slider + testSlider = new Slider(document.getElementById("testSlider1"), { + ticks: [0, 1, 2, 3, 4, 5, 6], + value: [4, 5], + step: 1, + range: true, + }); + // Set up default set of mouse event arguments + mouseEventArguments = [ + 'mousemove', // type + true, // canBubble + true, // cancelable + document, // view, + 0, // detail + 0, // screenX + 0, // screenY + undefined, // clientX + testSlider.sliderElem.offsetTop, // clientY, + false, // ctrlKey + false, // altKey + false, // shiftKey + false, // metaKey, + 0, // button + null // relatedTarget + ]; + // Calculate and store the 'clientX' for each tick in the slider + tickOffsets = testSlider.ticks.map(function (tick) { + return tick.offsetLeft + testSlider.sliderElem.offsetLeft; + }); + }); + + afterEach(function() { + if(testSlider) { + if(testSlider instanceof Slider) { testSlider.destroy(); } + testSlider = null; + } + }); + describe("Dragging handles over each other", function() { it("should swap reliably given imprecision", function() { - testSlider = new Slider(document.getElementById("testSlider1"), { - ticks: [0, 1, 2, 3, 4, 5, 6], - value: [4, 5], - step: 1, - range: true, - }); - var mouseEventArguments = [ - 'mousemove', // type - true, // canBubble - true, // cancelable - document, // view, - 0, // detail - 0, // screenX - 0, // screenY - undefined, // clientX - testSlider.sliderElem.offsetTop, // clientY, - false, // ctrlKey - false, // altKey - false, // shiftKey - false, // metaKey, - 0, // button - null // relatedTarget - ]; // Create mouse event with position to the left of problem tick var mouseLeft = document.createEvent('MouseEvents'); - mouseEventArguments[7] = testSlider.ticks[4].offsetLeft + testSlider.sliderElem.offsetLeft; // clientX + mouseEventArguments[7] = tickOffsets[4]; // clientX mouseLeft.initMouseEvent.apply(mouseLeft, mouseEventArguments); // Create mouse event with position on problem tick var mouseOverlap = document.createEvent('MouseEvents'); - mouseEventArguments[7] = testSlider.ticks[5].offsetLeft + testSlider.sliderElem.offsetLeft; // clientX + mouseEventArguments[7] = tickOffsets[5]; // clientX mouseOverlap.initMouseEvent.apply(mouseOverlap, mouseEventArguments); // Create mouse event with position to the right of problem tick var mouseRight = document.createEvent('MouseEvents'); - mouseEventArguments[7] = testSlider.ticks[6].offsetLeft + testSlider.sliderElem.offsetLeft; // clientX + mouseEventArguments[7] = tickOffsets[6]; // clientX mouseRight.initMouseEvent.apply(mouseRight, mouseEventArguments); // Simulate drag without swapping testSlider.mousedown(mouseLeft); @@ -64,14 +83,100 @@ describe("Dragging handles tests", function() { expect(testSlider.getValue()).toEqual([4, 5]); // End with mouse up testSlider.mouseup(); - expect(testSlider._state.dragged).toBe(0); + expect(testSlider._state.dragged).toBeNull(); expect(testSlider.getValue()).toEqual([4, 5]); }); }); - afterEach(function() { - if(testSlider) { - if(testSlider instanceof Slider) { testSlider.destroy(); } - testSlider = null; + + describe("Drag handles over each other and use keyboard to move handles over each other", function() { + var keyboardEvent; + + function createMouseEvent(type, tickIdx) { + var mouseEvent = document.createEvent('MouseEvent'); + mouseEventArguments[0] = type; + mouseEventArguments[7] = tickOffsets[tickIdx]; + mouseEvent.initMouseEvent.apply(mouseEvent, mouseEventArguments); + return mouseEvent; } + + beforeEach(function() { + // Create keyboard event + keyboardEvent = document.createEvent('Event'); + keyboardEvent.initEvent('keydown', true, true); + }); + + afterEach(function() { + keyboardEvent = null; + }); + + it("should drag and keydown handles properly to the right then back to the left", function() { + // Simulate drag without swapping + testSlider.mousedown(createMouseEvent('mousedown', 4)); + expect(testSlider._state.dragged).toBe(0); + expect(testSlider.getValue()).toEqual([4, 5]); + + // Simulate handle overlap + testSlider.mousemove(createMouseEvent('mousemove', 5)); + expect(testSlider._state.dragged).toBe(0); + expect(testSlider.getValue()).toEqual([5, 5]); + + // Simulate left over right drag + testSlider.mousemove(createMouseEvent('mousemove', 6)); + expect(testSlider._state.dragged).toBe(1); + expect(testSlider.getValue()).toEqual([5, 6]); + + // End with mouse up + testSlider.mouseup(); + expect(testSlider._state.dragged).toBeNull(); + expect(testSlider.getValue()).toEqual([5, 6]); + + // Now move the handles past each other with the Left arrow key + keyboardEvent.keyCode = keyboardEvent.which = 37; + + // Move handle2 to the left with keyboard + testSlider.handle2Keydown(keyboardEvent); + expect(testSlider._state.keyCtrl).toBeUndefined(); + expect(testSlider.getValue()).toEqual([5, 5]); + + // Move handle2 to the left again + testSlider.handle2Keydown(keyboardEvent); + expect(testSlider._state.keyCtrl).toBeUndefined(); + expect(testSlider.getValue()).toEqual([4, 5]); + }); + + it("should drag and keydown handles properly to the left then back to the right", function() { + // Simulate drag without swapping + testSlider.mousedown(createMouseEvent('mousedown', 5)); + expect(testSlider._state.dragged).toBe(1); + expect(testSlider.getValue()).toEqual([4, 5]); + + // Simulate handle overlap + testSlider.mousemove(createMouseEvent('mousemove', 4)); + expect(testSlider._state.dragged).toBe(1); + expect(testSlider.getValue()).toEqual([4, 4]); + + // Simulate left over right drag + testSlider.mousemove(createMouseEvent('mousemove', 3)); + expect(testSlider._state.dragged).toBe(0); + expect(testSlider.getValue()).toEqual([3, 4]); + + // End with mouse up + testSlider.mouseup(); + expect(testSlider._state.dragged).toBeNull(); + expect(testSlider.getValue()).toEqual([3, 4]); + + // Now move the handles past each other with the Right arrow key + keyboardEvent.keyCode = keyboardEvent.which = 39; + + // Move handle1 to the right with keyboard + testSlider.handle1Keydown(keyboardEvent); + expect(testSlider._state.keyCtrl).toBeUndefined(); + expect(testSlider.getValue()).toEqual([4, 4]); + + // Move handle1 to the right again + testSlider.handle1Keydown(keyboardEvent); + expect(testSlider._state.keyCtrl).toBeUndefined(); + expect(testSlider.getValue()).toEqual([4, 5]); + }); }); }); \ No newline at end of file