Move overview drag and drop code to a utility class.

This commit is contained in:
cahrens
2014-04-03 13:54:16 -04:00
parent 2f16bbe184
commit aade444f00
6 changed files with 697 additions and 695 deletions

View File

@@ -0,0 +1,313 @@
define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec/create_sinon", "jquery"],
function (ContentDragger, Notification, create_sinon, $) {
describe("Overview drag and drop functionality", function () {
beforeEach(function () {
setFixtures(readFixtures('mock/mock-outline.underscore'));
ContentDragger.makeDraggable('.unit', '.unit-drag-handle', 'ol.sortable-unit-list', 'li.courseware-subsection, article.subsection-body');
ContentDragger.makeDraggable('.courseware-subsection', '.subsection-drag-handle', '.sortable-subsection-list', 'section');
});
describe("findDestination", function () {
it("correctly finds the drop target of a drag", function () {
var $ele, destination;
$ele = $('#unit-1');
$ele.offset({
top: $ele.offset().top + 10,
left: $ele.offset().left
});
destination = ContentDragger.findDestination($ele, 1);
expect(destination.ele).toBe($('#unit-2'));
expect(destination.attachMethod).toBe('before');
});
it("can drag and drop across section boundaries, with special handling for single sibling", function () {
var $ele, $unit0, $unit4, destination;
$ele = $('#unit-1');
$unit4 = $('#unit-4');
$ele.offset({
top: $unit4.offset().top + 8,
left: $ele.offset().left
});
destination = ContentDragger.findDestination($ele, 1);
expect(destination.ele).toBe($unit4);
expect(destination.attachMethod).toBe('after');
destination = ContentDragger.findDestination($ele, -1);
expect(destination.ele).toBe($unit4);
expect(destination.attachMethod).toBe('before');
$ele.offset({
top: $unit4.offset().top + $unit4.height() + 1,
left: $ele.offset().left
});
destination = ContentDragger.findDestination($ele, 0);
expect(destination.ele).toBe($unit4);
expect(destination.attachMethod).toBe('after');
$unit0 = $('#unit-0');
$ele.offset({
top: $unit0.offset().top - 16,
left: $ele.offset().left
});
destination = ContentDragger.findDestination($ele, 0);
expect(destination.ele).toBe($unit0);
expect(destination.attachMethod).toBe('before');
});
it("can drop before the first element, even if element being dragged is\nslightly before the first element", function () {
var $ele, destination;
$ele = $('#subsection-2');
$ele.offset({
top: $('#subsection-0').offset().top - 5,
left: $ele.offset().left
});
destination = ContentDragger.findDestination($ele, -1);
expect(destination.ele).toBe($('#subsection-0'));
expect(destination.attachMethod).toBe('before');
});
it("can drag and drop across section boundaries, with special handling for last element", function () {
var $ele, destination;
$ele = $('#unit-4');
$ele.offset({
top: $('#unit-3').offset().bottom + 4,
left: $ele.offset().left
});
destination = ContentDragger.findDestination($ele, -1);
expect(destination.ele).toBe($('#unit-3'));
expect(destination.attachMethod).toBe('after');
$ele.offset({
top: $('#unit-3').offset().top + 4,
left: $ele.offset().left
});
destination = ContentDragger.findDestination($ele, -1);
expect(destination.ele).toBe($('#unit-3'));
expect(destination.attachMethod).toBe('before');
});
it("can drop past the last element, even if element being dragged is\nslightly before/taller then the last element", function () {
var $ele, destination;
$ele = $('#subsection-2');
$ele.offset({
top: $('#subsection-4').offset().top - 1,
left: $ele.offset().left
});
destination = ContentDragger.findDestination($ele, 1);
expect(destination.ele).toBe($('#subsection-4'));
expect(destination.attachMethod).toBe('after');
});
it("can drag into an empty list", function () {
var $ele, destination;
$ele = $('#unit-1');
$ele.offset({
top: $('#subsection-3').offset().top + 10,
left: $ele.offset().left
});
destination = ContentDragger.findDestination($ele, 1);
expect(destination.ele).toBe($('#subsection-list-3'));
expect(destination.attachMethod).toBe('prepend');
});
it("reports a null destination on a failed drag", function () {
var $ele, destination;
$ele = $('#unit-1');
$ele.offset({
top: $ele.offset().top + 200,
left: $ele.offset().left
});
destination = ContentDragger.findDestination($ele, 1);
expect(destination).toEqual({
ele: null,
attachMethod: ""
});
});
it("can drag into a collapsed list", function () {
var $ele, destination;
$('#subsection-2').addClass('collapsed');
$ele = $('#unit-2');
$ele.offset({
top: $('#subsection-2').offset().top + 3,
left: $ele.offset().left
});
destination = ContentDragger.findDestination($ele, 1);
expect(destination.ele).toBe($('#subsection-list-2'));
expect(destination.parentList).toBe($('#subsection-2'));
expect(destination.attachMethod).toBe('prepend');
});
});
describe("onDragStart", function () {
it("sets the dragState to its default values", function () {
expect(ContentDragger.dragState).toEqual({});
ContentDragger.onDragStart({
element: $('#unit-1')
}, null, null);
expect(ContentDragger.dragState).toEqual({
dropDestination: null,
attachMethod: '',
parentList: null,
lastY: 0,
dragDirection: 0
});
});
it("collapses expanded elements", function () {
expect($('#subsection-1')).not.toHaveClass('collapsed');
ContentDragger.onDragStart({
element: $('#subsection-1')
}, null, null);
expect($('#subsection-1')).toHaveClass('collapsed');
expect($('#subsection-1')).toHaveClass('expand-on-drop');
});
});
describe("onDragMove", function () {
beforeEach(function () {
this.scrollSpy = spyOn(window, 'scrollBy').andCallThrough();
});
it("adds the correct CSS class to the drop destination", function () {
var $ele, dragX, dragY;
$ele = $('#unit-1');
dragY = $ele.offset().top + 10;
dragX = $ele.offset().left;
$ele.offset({
top: dragY,
left: dragX
});
ContentDragger.onDragMove({
element: $ele,
dragPoint: {
y: dragY
}
}, '', {
clientX: dragX
});
expect($('#unit-2')).toHaveClass('drop-target drop-target-before');
expect($ele).toHaveClass('valid-drop');
});
it("does not add CSS class to the drop destination if out of bounds", function () {
var $ele, dragY;
$ele = $('#unit-1');
dragY = $ele.offset().top + 10;
$ele.offset({
top: dragY,
left: $ele.offset().left
});
ContentDragger.onDragMove({
element: $ele,
dragPoint: {
y: dragY
}
}, '', {
clientX: $ele.offset().left - 3
});
expect($('#unit-2')).not.toHaveClass('drop-target drop-target-before');
expect($ele).not.toHaveClass('valid-drop');
});
it("scrolls up if necessary", function () {
ContentDragger.onDragMove({
element: $('#unit-1')
}, '', {
clientY: 2
});
expect(this.scrollSpy).toHaveBeenCalledWith(0, -10);
});
it("scrolls down if necessary", function () {
ContentDragger.onDragMove({
element: $('#unit-1')
}, '', {
clientY: window.innerHeight - 5
});
expect(this.scrollSpy).toHaveBeenCalledWith(0, 10);
});
});
describe("onDragEnd", function () {
beforeEach(function () {
this.reorderSpy = spyOn(ContentDragger, 'handleReorder');
});
afterEach(function () {
this.reorderSpy.reset();
});
it("calls handleReorder on a successful drag", function () {
ContentDragger.dragState.dropDestination = $('#unit-2');
ContentDragger.dragState.attachMethod = "before";
ContentDragger.dragState.parentList = $('#subsection-1');
$('#unit-1').offset({
top: $('#unit-1').offset().top + 10,
left: $('#unit-1').offset().left
});
ContentDragger.onDragEnd({
element: $('#unit-1')
}, null, {
clientX: $('#unit-1').offset().left
});
expect(this.reorderSpy).toHaveBeenCalled();
});
it("clears out the drag state", function () {
ContentDragger.onDragEnd({
element: $('#unit-1')
}, null, null);
expect(ContentDragger.dragState).toEqual({});
});
it("sets the element to the correct position", function () {
ContentDragger.onDragEnd({
element: $('#unit-1')
}, null, null);
expect(['0px', 'auto']).toContain($('#unit-1').css('top'));
expect(['0px', 'auto']).toContain($('#unit-1').css('left'));
});
it("expands an element if it was collapsed on drag start", function () {
$('#subsection-1').addClass('collapsed');
$('#subsection-1').addClass('expand-on-drop');
ContentDragger.onDragEnd({
element: $('#subsection-1')
}, null, null);
expect($('#subsection-1')).not.toHaveClass('collapsed');
expect($('#subsection-1')).not.toHaveClass('expand-on-drop');
});
it("expands a collapsed element when something is dropped in it", function () {
$('#subsection-2').addClass('collapsed');
ContentDragger.dragState.dropDestination = $('#list-2');
ContentDragger.dragState.attachMethod = "prepend";
ContentDragger.dragState.parentList = $('#subsection-2');
ContentDragger.onDragEnd({
element: $('#unit-1')
}, null, {
clientX: $('#unit-1').offset().left
});
expect($('#subsection-2')).not.toHaveClass('collapsed');
});
});
describe("AJAX", function () {
beforeEach(function () {
this.savingSpies = spyOnConstructor(Notification, "Mini", ["show", "hide"]);
this.savingSpies.show.andReturn(this.savingSpies);
this.clock = sinon.useFakeTimers();
});
afterEach(function () {
this.clock.restore();
});
it("should send an update on reorder", function () {
var requests, savingOptions;
requests = create_sinon["requests"](this);
ContentDragger.dragState.dropDestination = $('#unit-4');
ContentDragger.dragState.attachMethod = "after";
ContentDragger.dragState.parentList = $('#subsection-2');
$('#unit-1').offset({
top: $('#unit-4').offset().top + 10,
left: $('#unit-4').offset().left
});
ContentDragger.onDragEnd({
element: $('#unit-1')
}, null, {
clientX: $('#unit-1').offset().left
});
expect(requests.length).toEqual(2);
expect(this.savingSpies.constructor).toHaveBeenCalled();
expect(this.savingSpies.show).toHaveBeenCalled();
expect(this.savingSpies.hide).not.toHaveBeenCalled();
savingOptions = this.savingSpies.constructor.mostRecentCall.args[0];
expect(savingOptions.title).toMatch(/Saving/);
expect($('#unit-1')).toHaveClass('was-dropped');
expect(requests[0].requestBody).toEqual('{"children":["second-unit-id","third-unit-id"]}');
requests[0].respond(200);
expect(this.savingSpies.hide).not.toHaveBeenCalled();
expect(requests[1].requestBody).toEqual('{"children":["fourth-unit-id","first-unit-id"]}');
requests[1].respond(200);
expect(this.savingSpies.hide).toHaveBeenCalled();
this.clock.tick(1001);
expect($('#unit-1')).not.toHaveClass('was-dropped');
});
});
});
});