Address testathon issues relating to context menu (#2235)

* Support category arrays for legacy actions

* Fixed object path listener. Removed old context menus

* Removed old fullscreen action and Screenfull dependency

* Restore confirmation dialog on remove

* Restored tests

* Remove unused legacy policies
This commit is contained in:
Andrew Henry
2018-12-07 10:15:11 -08:00
committed by Pete Richards
parent a87fc51fbb
commit c748569433
30 changed files with 86 additions and 1318 deletions

View File

@@ -25,12 +25,10 @@ define([
"./src/MCTRepresentation",
"./src/gestures/DragGesture",
"./src/gestures/DropGesture",
"./src/gestures/ContextMenuGesture",
"./src/gestures/GestureProvider",
"./src/gestures/GestureRepresenter",
"./src/services/DndService",
"./src/TemplateLinker",
"./src/actions/ContextMenuAction",
"./src/TemplatePrefetcher",
'legacyRegistry'
], function (
@@ -38,12 +36,10 @@ define([
MCTRepresentation,
DragGesture,
DropGesture,
ContextMenuGesture,
GestureProvider,
GestureRepresenter,
DndService,
TemplateLinker,
ContextMenuAction,
TemplatePrefetcher,
legacyRegistry
) {
@@ -88,14 +84,6 @@ define([
"dndService",
"$q"
]
},
{
"key": "menu",
"implementation": ContextMenuGesture,
"depends": [
"$timeout",
"agentService"
]
}
],
"components": [
@@ -136,19 +124,6 @@ define([
"comment": "For internal use by mct-include and mct-representation."
}
],
"actions": [
{
"key": "menu",
"implementation": ContextMenuAction,
"depends": [
"$compile",
"$document",
"$rootScope",
"popupService",
"agentService"
]
}
],
"runs": [
{
"priority": "mandatory",

View File

@@ -1,138 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* Module defining ContextMenuAction. Created by shale on 06/30/2015.
*/
define(
["../gestures/GestureConstants"],
function (GestureConstants) {
var MENU_TEMPLATE = "<mct-representation key=\"'context-menu'\" " +
"mct-object=\"domainObject\" " +
"ng-class=\"menuClass\" " +
"ng-style=\"menuStyle\">" +
"</mct-representation>",
dismissExistingMenu;
/**
* Launches a custom context menu for the domain object it contains.
*
* @memberof platform/representation
* @constructor
* @param $compile Angular's $compile service
* @param $document the current document
* @param $rootScope Angular's root scope
* @param {platform/commonUI/general.PopupService} popupService
* @param actionContext the context in which the action
* should be performed
* @implements {Action}
*/
function ContextMenuAction(
$compile,
$document,
$rootScope,
popupService,
agentService,
actionContext
) {
this.$compile = $compile;
this.agentService = agentService;
this.actionContext = actionContext;
this.popupService = popupService;
this.getDocument = function () {
return $document;
};
this.getRootScope = function () {
return $rootScope;
};
}
ContextMenuAction.prototype.perform = function () {
var $compile = this.$compile,
$document = this.getDocument(),
$rootScope = this.getRootScope(),
actionContext = this.actionContext,
eventCoords = [
actionContext.event.pageX,
actionContext.event.pageY
],
menuDim = GestureConstants.MCT_MENU_DIMENSIONS,
body = $document.find('body'),
scope = $rootScope.$new(),
initiatingEvent = this.agentService.isMobile() ?
'touchstart' : 'mousedown',
menu,
popup;
// Remove the context menu
function dismiss() {
if (popup) {
popup.dismiss();
popup = undefined;
}
scope.$destroy();
body.off("mousedown", dismiss);
dismissExistingMenu = undefined;
}
// Dismiss any menu which was already showing
if (dismissExistingMenu) {
dismissExistingMenu();
}
// ...and record the presence of this menu.
dismissExistingMenu = dismiss;
// Set up the scope, including menu positioning
scope.domainObject = actionContext.domainObject;
scope.menuClass = { "context-menu-holder": true };
// Create the context menu
menu = $compile(MENU_TEMPLATE)(scope);
popup = this.popupService.display(menu, eventCoords, {
marginX: -menuDim[0],
marginY: -menuDim[1]
});
scope.menuClass['go-left'] = popup.goesLeft();
scope.menuClass['go-up'] = popup.goesUp();
// Stop propagation so that clicks or touches on the menu do not close the menu
menu.on(initiatingEvent, function (event) {
event.stopPropagation();
});
// Dismiss the menu when body is clicked/touched elsewhere
// ('mousedown' because 'click' breaks left-click context menus)
// ('touchstart' because 'touch' breaks context menus up)
body.on(initiatingEvent, dismiss);
// NOTE: Apply to mobile?
menu.on('click', dismiss);
// Don't launch browser's context menu
actionContext.event.preventDefault();
};
return ContextMenuAction;
}
);

View File

@@ -1,100 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* Module defining ContextMenuGesture.
* Created by vwoeltje on 11/17/14. Modified by shale on 06/30/2015.
*/
define(
function () {
/**
* Add listeners to a representation such that it calls the
* context menu action for the domain object it contains.
*
* @memberof platform/representation
* @constructor
* @param element the jqLite-wrapped element which should exhibit
* the context menu
* @param {DomainObject} domainObject the object on which actions
* in the context menu will be performed
* @implements {Gesture}
*/
function ContextMenuGesture($timeout, agentService, element, domainObject) {
var isPressing,
isDragging,
longTouchTime = 500;
function showMenu(event) {
domainObject.getCapability('action').perform({
key: 'menu',
domainObject: domainObject,
event: event
});
}
// When context menu event occurs, show object actions instead
if (!agentService.isMobile()) {
// When context menu event occurs, show object actions instead
element.on('contextmenu', showMenu);
} else if (agentService.isMobile()) {
// If on mobile device, then start timeout for the single touch event
// during the timeout 'isPressing' is true.
element.on('touchstart', function (event) {
if (event.touches.length < 2) {
isPressing = true;
// After the timeout, if 'isPressing' is
// true, display context menu for object
$timeout(function () {
if (isPressing && !isDragging) {
showMenu(event);
}
}, longTouchTime);
}
});
// If on Mobile Device, and user scrolls/drags set flag to true
element.on('touchmove', function () {
isDragging = true;
});
// Whenever the touch event ends, 'isPressing' & 'isDragging' is false.
element.on('touchend', function () {
isPressing = false;
isDragging = false;
});
}
this.showMenuCallback = showMenu;
this.element = element;
}
ContextMenuGesture.prototype.destroy = function () {
this.element.off('contextmenu', this.showMenu);
};
return ContextMenuGesture;
}
);

View File

@@ -1,202 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* Module defining ContextMenuActionSpec. Created by shale on 07/02/2015.
*/
define(
["../../src/actions/ContextMenuAction"],
function (ContextMenuAction) {
var JQLITE_FUNCTIONS = ["on", "off", "find", "append", "remove"],
DOMAIN_OBJECT_METHODS = ["getId", "getModel", "getCapability", "hasCapability", "useCapability"];
describe("The 'context menu' action", function () {
var mockCompile,
mockCompiledTemplate,
mockMenu,
mockDocument,
mockBody,
mockPopupService,
mockRootScope,
mockAgentService,
mockScope,
mockDomainObject,
mockEvent,
mockPopup,
mockActionContext,
action;
beforeEach(function () {
mockCompile = jasmine.createSpy("$compile");
mockCompiledTemplate = jasmine.createSpy("template");
mockMenu = jasmine.createSpyObj("menu", JQLITE_FUNCTIONS);
mockDocument = jasmine.createSpyObj("$document", JQLITE_FUNCTIONS);
mockBody = jasmine.createSpyObj("body", JQLITE_FUNCTIONS);
mockPopupService =
jasmine.createSpyObj("popupService", ["display"]);
mockPopup = jasmine.createSpyObj("popup", [
"dismiss",
"goesLeft",
"goesUp"
]);
mockRootScope = jasmine.createSpyObj("$rootScope", ["$new"]);
mockAgentService = jasmine.createSpyObj("agentService", ["isMobile"]);
mockScope = jasmine.createSpyObj("scope", ["$destroy"]);
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
mockEvent = jasmine.createSpyObj("event", ["preventDefault", "stopPropagation"]);
mockEvent.pageX = 123;
mockEvent.pageY = 321;
mockCompile.and.returnValue(mockCompiledTemplate);
mockCompiledTemplate.and.returnValue(mockMenu);
mockDocument.find.and.returnValue(mockBody);
mockRootScope.$new.and.returnValue(mockScope);
mockPopupService.display.and.returnValue(mockPopup);
mockActionContext = {key: 'menu', domainObject: mockDomainObject, event: mockEvent};
action = new ContextMenuAction(
mockCompile,
mockDocument,
mockRootScope,
mockPopupService,
mockAgentService,
mockActionContext
);
});
it("displays a popup when performed", function () {
action.perform();
expect(mockPopupService.display).toHaveBeenCalledWith(
mockMenu,
[mockEvent.pageX, mockEvent.pageY],
jasmine.any(Object)
);
});
it("prevents the default context menu behavior", function () {
action.perform();
expect(mockEvent.preventDefault).toHaveBeenCalled();
});
it("adds classes to menus based on position", function () {
var booleans = [false, true];
booleans.forEach(function (goLeft) {
booleans.forEach(function (goUp) {
mockPopup.goesLeft.and.returnValue(goLeft);
mockPopup.goesUp.and.returnValue(goUp);
action.perform();
expect(!!mockScope.menuClass['go-up'])
.toEqual(goUp);
expect(!!mockScope.menuClass['go-left'])
.toEqual(goLeft);
});
});
});
it("removes a menu when body is clicked", function () {
// Show the menu
action.perform();
// Verify precondition
expect(mockBody.remove).not.toHaveBeenCalled();
// Find and fire body's mousedown listener
mockBody.on.calls.all().forEach(function (call) {
if (call.args[0] === 'mousedown') {
call.args[1]();
}
});
// Menu should have been removed
expect(mockPopup.dismiss).toHaveBeenCalled();
// Listener should have been detached from body
expect(mockBody.off).toHaveBeenCalledWith(
'mousedown',
jasmine.any(Function)
);
});
it("removes a menu when it is clicked", function () {
// Show the menu
action.perform();
// Verify precondition
expect(mockMenu.remove).not.toHaveBeenCalled();
// Find and fire menu's click listener
mockMenu.on.calls.all().forEach(function (call) {
if (call.args[0] === 'click') {
call.args[1]();
}
});
// Menu should have been removed
expect(mockPopup.dismiss).toHaveBeenCalled();
});
it("keeps a menu when menu is clicked", function () {
// Show the menu
action.perform();
// Find and fire body's mousedown listener
mockMenu.on.calls.all().forEach(function (call) {
if (call.args[0] === 'mousedown') {
call.args[1](mockEvent);
}
});
// Menu should have been removed
expect(mockPopup.dismiss).not.toHaveBeenCalled();
// Listener should have been detached from body
expect(mockBody.off).not.toHaveBeenCalled();
});
it("keeps a menu when menu is clicked on mobile", function () {
mockAgentService.isMobile.and.returnValue(true);
action = new ContextMenuAction(
mockCompile,
mockDocument,
mockRootScope,
mockPopupService,
mockAgentService,
mockActionContext
);
action.perform();
mockMenu.on.calls.all().forEach(function (call) {
if (call.args[0] === 'touchstart') {
call.args[1](mockEvent);
}
});
expect(mockPopup.dismiss).not.toHaveBeenCalled();
});
});
}
);

View File

@@ -1,119 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* Module defining ContextMenuGestureSpec. Created by vwoeltje on 11/22/14.
*/
define(
["../../src/gestures/ContextMenuGesture"],
function (ContextMenuGesture) {
var JQLITE_FUNCTIONS = ["on", "off", "find", "append", "remove"],
DOMAIN_OBJECT_METHODS = ["getId", "getModel", "getCapability", "hasCapability", "useCapability"];
describe("The 'context menu' gesture", function () {
var mockTimeout,
mockElement,
mockAgentService,
mockDomainObject,
mockTouchEvent,
mockContextMenuAction,
mockTouch,
gesture,
fireGesture,
fireTouchStartGesture,
fireTouchEndGesture;
beforeEach(function () {
mockTimeout = jasmine.createSpy("$timeout");
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
mockAgentService = jasmine.createSpyObj("agentService", ["isMobile"]);
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
mockContextMenuAction = jasmine.createSpyObj(
"action",
["perform", "getActions"]
);
mockDomainObject.getCapability.and.returnValue(mockContextMenuAction);
mockContextMenuAction.perform.and.returnValue(jasmine.any(Function));
mockAgentService.isMobile.and.returnValue(false);
gesture = new ContextMenuGesture(mockTimeout, mockAgentService, mockElement, mockDomainObject);
// Capture the contextmenu callback
fireGesture = mockElement.on.calls.mostRecent().args[1];
});
it("attaches a callback for context menu events", function () {
// Fire a click and expect it to happen
fireGesture();
expect(mockElement.on).toHaveBeenCalledWith(
"contextmenu",
jasmine.any(Function)
);
});
it("detaches a callback for context menu events when destroyed", function () {
expect(mockElement.off).not.toHaveBeenCalled();
gesture.destroy();
expect(mockElement.off).toHaveBeenCalledWith(
"contextmenu",
//mockElement.on.calls.mostRecent().args[1]
mockDomainObject.calls
);
});
it("attaches a callback for context menu events on mobile", function () {
// Mock touch event and set to mobile device
mockTouchEvent = jasmine.createSpyObj("event", ["preventDefault", "touches"]);
mockTouch = jasmine.createSpyObj("touch", ["length"]);
mockTouch.length = 1;
mockTouchEvent.touches.and.returnValue(mockTouch);
mockAgentService.isMobile.and.returnValue(true);
// Then create new (mobile) gesture
gesture = new ContextMenuGesture(mockTimeout, mockAgentService, mockElement, mockDomainObject);
// Set calls for the touchstart and touchend gestures
fireTouchStartGesture = mockElement.on.calls.all()[1].args[1];
fireTouchEndGesture = mockElement.on.calls.mostRecent().args[1];
// Fire touchstart and expect touch start to begin
fireTouchStartGesture(mockTouchEvent);
expect(mockElement.on).toHaveBeenCalledWith(
"touchstart",
jasmine.any(Function)
);
// Expect timeout to begin and then fireTouchEnd
expect(mockTimeout).toHaveBeenCalled();
mockTimeout.calls.mostRecent().args[0]();
fireTouchEndGesture(mockTouchEvent);
});
});
}
);