mirror of
https://github.com/nasa/openmct.git
synced 2025-06-18 07:08:12 +00:00
[Representation] Add DnD service
Add service to communicate data after/during a drag-and-drop, to allow drag contents to be checked during dragover, and to allow full JavaScript objects to be passed during drags (within the same window.) WTD-988.
This commit is contained in:
@ -16,7 +16,7 @@
|
|||||||
{
|
{
|
||||||
"key": "drag",
|
"key": "drag",
|
||||||
"implementation": "gestures/DragGesture.js",
|
"implementation": "gestures/DragGesture.js",
|
||||||
"depends": [ "$log" ]
|
"depends": [ "$log", "dndService" ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "drop",
|
"key": "drop",
|
||||||
@ -42,6 +42,13 @@
|
|||||||
"implementation": "gestures/GestureRepresenter.js",
|
"implementation": "gestures/GestureRepresenter.js",
|
||||||
"depends": [ "gestureService" ]
|
"depends": [ "gestureService" ]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"key": "dndService",
|
||||||
|
"implementation": "services/DndService.js",
|
||||||
|
"depends": [ "$log" ]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,7 +19,7 @@ define(
|
|||||||
* @param {DomainObject} domainObject the domain object which
|
* @param {DomainObject} domainObject the domain object which
|
||||||
* is represented; this will be passed on drop.
|
* is represented; this will be passed on drop.
|
||||||
*/
|
*/
|
||||||
function DragGesture($log, element, domainObject) {
|
function DragGesture($log, dndService, element, domainObject) {
|
||||||
function startDrag(e) {
|
function startDrag(e) {
|
||||||
var event = (e || {}).originalEvent || e;
|
var event = (e || {}).originalEvent || e;
|
||||||
|
|
||||||
@ -45,6 +45,12 @@ define(
|
|||||||
domainObject.getId()
|
domainObject.getId()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Finally, also pass the object instance via the dndService,
|
||||||
|
// so more than the ID can be retrieved (if desired)
|
||||||
|
dndService.setData(
|
||||||
|
GestureConstants.MCT_DRAG_TYPE,
|
||||||
|
domainObject
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Exceptions at this point indicate that the browser
|
// Exceptions at this point indicate that the browser
|
||||||
// do not fully support drag-and-drop (e.g. if
|
// do not fully support drag-and-drop (e.g. if
|
||||||
@ -57,10 +63,16 @@ define(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function endDrag() {
|
||||||
|
// Clear the drag data after the drag is complete
|
||||||
|
dndService.removeData(GestureConstants.MCT_DRAG_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
// Mark the element as draggable, and handle the dragstart event
|
// Mark the element as draggable, and handle the dragstart event
|
||||||
$log.debug("Attaching drag gesture");
|
$log.debug("Attaching drag gesture");
|
||||||
element.attr('draggable', 'true');
|
element.attr('draggable', 'true');
|
||||||
element.on('dragstart', startDrag);
|
element.on('dragstart', startDrag);
|
||||||
|
element.on('dragend', endDrag);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
|
50
platform/representation/src/services/DndService.js
Normal file
50
platform/representation/src/services/DndService.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*global define*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drag-and-drop service.
|
||||||
|
* Supplements HTML5 drag-and-drop support by:
|
||||||
|
* * Storing arbitrary JavaScript objects (not just strings.)
|
||||||
|
* * Allowing inspection of dragged objects during `dragover` events,
|
||||||
|
* etc. (which cannot be done in Chrome for security reasons)
|
||||||
|
* @constructor
|
||||||
|
* @param $log Angular's $log service
|
||||||
|
*/
|
||||||
|
function DndService($log) {
|
||||||
|
var data = {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Set drag data associated with a given type.
|
||||||
|
* @param {string} key the type's identiifer
|
||||||
|
* @param {*} value the data being dragged
|
||||||
|
*/
|
||||||
|
setData: function (key, value) {
|
||||||
|
$log.debug("Setting drag data for " + key);
|
||||||
|
data[key] = value;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get drag data associated with a given type.
|
||||||
|
* @returns {*} the data being dragged
|
||||||
|
*/
|
||||||
|
getData: function (key) {
|
||||||
|
return data[key];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Remove data associated with active drags.
|
||||||
|
* @param {string} key the type to remove
|
||||||
|
*/
|
||||||
|
removeData: function (key) {
|
||||||
|
$log.debug("Clearing drag data for " + key);
|
||||||
|
delete data[key];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return DndService;
|
||||||
|
}
|
||||||
|
);
|
@ -10,6 +10,7 @@ define(
|
|||||||
|
|
||||||
var JQLITE_FUNCTIONS = [ "on", "off", "attr", "removeAttr" ],
|
var JQLITE_FUNCTIONS = [ "on", "off", "attr", "removeAttr" ],
|
||||||
LOG_FUNCTIONS = [ "error", "warn", "info", "debug"],
|
LOG_FUNCTIONS = [ "error", "warn", "info", "debug"],
|
||||||
|
DND_FUNCTIONS = [ "setData", "getData", "removeData" ],
|
||||||
DOMAIN_OBJECT_METHODS = [ "getId", "getModel", "getCapability", "hasCapability", "useCapability"],
|
DOMAIN_OBJECT_METHODS = [ "getId", "getModel", "getCapability", "hasCapability", "useCapability"],
|
||||||
TEST_ID = "test-id";
|
TEST_ID = "test-id";
|
||||||
|
|
||||||
@ -17,14 +18,16 @@ define(
|
|||||||
|
|
||||||
describe("The drag gesture", function () {
|
describe("The drag gesture", function () {
|
||||||
var mockLog,
|
var mockLog,
|
||||||
|
mockDndService,
|
||||||
mockElement,
|
mockElement,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockDataTransfer,
|
mockDataTransfer,
|
||||||
gesture,
|
handlers,
|
||||||
fireGesture;
|
gesture;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
|
mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
|
||||||
|
mockDndService = jasmine.createSpyObj("dndService", DND_FUNCTIONS);
|
||||||
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
|
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
|
||||||
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
|
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
|
||||||
mockDataTransfer = jasmine.createSpyObj("dataTransfer", ["setData"]);
|
mockDataTransfer = jasmine.createSpyObj("dataTransfer", ["setData"]);
|
||||||
@ -32,12 +35,18 @@ define(
|
|||||||
mockDomainObject.getId.andReturn(TEST_ID);
|
mockDomainObject.getId.andReturn(TEST_ID);
|
||||||
mockDomainObject.getModel.andReturn({});
|
mockDomainObject.getModel.andReturn({});
|
||||||
|
|
||||||
gesture = new DragGesture(mockLog, mockElement, mockDomainObject);
|
handlers = {};
|
||||||
fireGesture = mockElement.on.mostRecentCall.args[1];
|
|
||||||
|
gesture = new DragGesture(mockLog, mockDndService, mockElement, mockDomainObject);
|
||||||
|
|
||||||
|
// Look up all handlers registered by the gesture
|
||||||
|
mockElement.on.calls.forEach(function (call) {
|
||||||
|
handlers[call.args[0]] = call.args[1];
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("listens for dragstart on the element", function () {
|
it("listens for dragstart on the element", function () {
|
||||||
expect(mockElement.on.mostRecentCall.args[0]).toEqual("dragstart");
|
expect(handlers.dragstart).toEqual(jasmine.any(Function));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("marks an element as draggable", function () {
|
it("marks an element as draggable", function () {
|
||||||
@ -45,19 +54,40 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("places data in a dataTransfer object", function () {
|
it("places data in a dataTransfer object", function () {
|
||||||
fireGesture({ dataTransfer: mockDataTransfer });
|
handlers.dragstart({ dataTransfer: mockDataTransfer });
|
||||||
expect(mockDataTransfer.setData).toHaveBeenCalledWith(
|
expect(mockDataTransfer.setData).toHaveBeenCalledWith(
|
||||||
GestureConstants.MCT_DRAG_TYPE,
|
GestureConstants.MCT_DRAG_TYPE,
|
||||||
TEST_ID
|
TEST_ID
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("places domain object in the dnd service", function () {
|
||||||
|
handlers.dragstart({ dataTransfer: mockDataTransfer });
|
||||||
|
expect(mockDndService.setData).toHaveBeenCalledWith(
|
||||||
|
GestureConstants.MCT_DRAG_TYPE,
|
||||||
|
mockDomainObject
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clears domain object from the dnd service on drag end", function () {
|
||||||
|
// Start dragging
|
||||||
|
handlers.dragstart({ dataTransfer: mockDataTransfer });
|
||||||
|
|
||||||
|
// Verify precondition
|
||||||
|
expect(mockDndService.removeData).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// End the drag
|
||||||
|
handlers.dragend({ dataTransfer: mockDataTransfer });
|
||||||
|
expect(mockDndService.removeData)
|
||||||
|
.toHaveBeenCalledWith(GestureConstants.MCT_DRAG_TYPE);
|
||||||
|
});
|
||||||
|
|
||||||
it("logs a warning if dataTransfer cannot be set", function () {
|
it("logs a warning if dataTransfer cannot be set", function () {
|
||||||
// Verify precondition
|
// Verify precondition
|
||||||
expect(mockLog.warn).not.toHaveBeenCalled();
|
expect(mockLog.warn).not.toHaveBeenCalled();
|
||||||
|
|
||||||
// Fire the gesture without a dataTransfer field
|
// Fire the gesture without a dataTransfer field
|
||||||
fireGesture({});
|
handlers.dragstart({});
|
||||||
|
|
||||||
// Should have logged a warning
|
// Should have logged a warning
|
||||||
expect(mockLog.warn).toHaveBeenCalled();
|
expect(mockLog.warn).toHaveBeenCalled();
|
||||||
@ -73,7 +103,7 @@ define(
|
|||||||
|
|
||||||
// Verify that attribute/listener were removed
|
// Verify that attribute/listener were removed
|
||||||
expect(mockElement.removeAttr).toHaveBeenCalledWith("draggable");
|
expect(mockElement.removeAttr).toHaveBeenCalledWith("draggable");
|
||||||
expect(mockElement.off).toHaveBeenCalledWith("dragstart", fireGesture);
|
expect(mockElement.off).toHaveBeenCalledWith("dragstart", handlers.dragstart);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
45
platform/representation/test/services/DndServiceSpec.js
Normal file
45
platform/representation/test/services/DndServiceSpec.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||||
|
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../../src/services/DndService"],
|
||||||
|
function (DndService) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("The drag-and-drop service", function () {
|
||||||
|
var service;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
var mockLog = jasmine.createSpyObj("$log", ['debug']);
|
||||||
|
service = new DndService(mockLog);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows setting of arbitrary objects", function () {
|
||||||
|
var foo = {
|
||||||
|
bar: function () { return 42; }
|
||||||
|
};
|
||||||
|
|
||||||
|
service.setData('xyz', foo);
|
||||||
|
|
||||||
|
// Test that we can get back callable data, since this is
|
||||||
|
// a key reason for having a service separate from HTML5 DnD.
|
||||||
|
expect(service.getData('xyz').bar()).toEqual(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("stores data under specified keys", function () {
|
||||||
|
service.setData('abc', 42);
|
||||||
|
service.setData('def', "some data");
|
||||||
|
|
||||||
|
expect(service.getData('abc')).toEqual(42);
|
||||||
|
expect(service.getData('def')).toEqual("some data");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes data", function () {
|
||||||
|
service.setData('abc', 42);
|
||||||
|
service.removeData('abc');
|
||||||
|
expect(service.getData('abc')).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -4,6 +4,7 @@
|
|||||||
"gestures/DropGesture",
|
"gestures/DropGesture",
|
||||||
"gestures/GestureProvider",
|
"gestures/GestureProvider",
|
||||||
"gestures/GestureRepresenter",
|
"gestures/GestureRepresenter",
|
||||||
|
"services/DndService",
|
||||||
"MCTInclude",
|
"MCTInclude",
|
||||||
"MCTRepresentation"
|
"MCTRepresentation"
|
||||||
]
|
]
|
Reference in New Issue
Block a user