[Layout] Support sub-object selection in layout (#1811)

Updates to sub object selection, first cut of selection APIs.

* [API] Add inspector view registry to register inspector view providers and show a view in the inspector.

[API] Modify the selection API to register the click event and handle the event. The API will add a class to the selected object and the immediate parent of the selected object.

[Directive] Implemenet mct-selectable directive for making an element selectable.

[Layout] Update the layout controller to use the Selection API. Also, add double click gesture to allow drilling into a selected object.

Populate the Elements pool with contained elements of the selected object. Update toolbar and inspector to listen for the changes in selection.

* [Frontend] Mods to markup and CSS for sub-object selection

* MCTSelectable allows selection in initialization, use to select on navigation

[Frontend] Show grid in first nested layout, hide from deeper nesting. Only show grids when applicable to relative selection.

* Fix checkstyle and lint errors

* Bring back the change that made mct-init-select work

* [Inspector] Make sure the right content is displayed based on whether a view provider exists or not.

* Only show table options when editing

* Make reviewers' requested changes

* Fix broken tests

* [Frontend] Cleanups and tweaks

Fixes #1811
- Cleanups between frame, editor and selecting.scss;
- Hover and selected borders visually pumped up a bit;
- Solid borders on hover and selecting when browsing;
- Dashed borders for layouts when editing;
- Fixed cursor to only show move capability when
element is selected;

* [Frontend] Tweaks to frame.no-frame layout

Fixes #1811
- Margin set to 0;
- Overflow set to hidden;

* [Frontend] Fixed position items border width fixed

Fixes #1811
- Set to 1px;

* Add tests for inspector controller and fix broken tests. Clean up code.

* [Fixed Position] Stop event propagation on click handlers in fixed position to avoid the event reaching the selection click handlers which caused issues with toolbar and selection."

* Fix tests

* Add tests

* Add test

* Remove element from document
This commit is contained in:
Pegah Sarram
2017-12-07 13:04:46 -08:00
committed by Pete Richards
parent 50b4d5cb28
commit 425655bae0
34 changed files with 944 additions and 360 deletions

View File

@@ -38,7 +38,8 @@ define([
"implementation": InspectorController,
"depends": [
"$scope",
"policyService"
"openmct",
"$document"
]
}
],

View File

@@ -21,44 +21,69 @@
*****************************************************************************/
define(
['../../browse/src/InspectorRegion'],
function (InspectorRegion) {
[],
function () {
/**
* The InspectorController adds region data for a domain object's type
* to the scope.
* The InspectorController listens for the selection changes and adds the selection
* object to the scope.
*
* @constructor
*/
function InspectorController($scope, policyService) {
var domainObject = $scope.domainObject,
typeCapability = domainObject.getCapability('type'),
statusListener;
function InspectorController($scope, openmct, $document) {
var self = this;
self.$scope = $scope;
/**
* Filters region parts to only those allowed by region policies
* @param regions
* @returns {{}}
* Callback handler for the selection change event.
* Adds the selection object to the scope. If the selected item has an inspector view,
* it puts the key in the scope. If provider view exists, it shows the view.
*/
function filterRegions(inspector) {
//Dupe so we're not modifying the type definition.
return inspector.regions && inspector.regions.filter(function (region) {
return policyService.allow('region', region, domainObject);
});
function setSelection(selection) {
if (selection[0]) {
var view = openmct.inspectorViews.get(selection);
var container = $document[0].querySelectorAll('.inspector-provider-view')[0];
container.innerHTML = "";
if (view) {
self.providerView = true;
view.show(container);
} else {
self.providerView = false;
$scope.inspectorKey = selection[0].context.oldItem.getCapability("type").typeDef.inspector;
}
}
self.$scope.selection = selection;
}
function setRegions() {
$scope.regions = filterRegions(typeCapability.getDefinition().inspector || new InspectorRegion());
}
openmct.selection.on("change", setSelection);
setSelection(openmct.selection.get());
statusListener = domainObject.getCapability("status").listen(setRegions);
$scope.$on("$destroy", function () {
statusListener();
openmct.selection.off("change", setSelection);
});
setRegions();
}
/**
* Gets the selected item.
*
* @returns a domain object
*/
InspectorController.prototype.selectedItem = function () {
return this.$scope.selection[0].context.oldItem;
};
/**
* Checks if a provider view exists.
*
* @returns 'true' if provider view exists, 'false' otherwise
*/
InspectorController.prototype.hasProviderView = function () {
return this.providerView;
};
return InspectorController;
}
);

View File

@@ -27,82 +27,93 @@ define(
describe("The inspector controller ", function () {
var mockScope,
mockDomainObject,
mockTypeCapability,
mockTypeDefinition,
mockPolicyService,
mockStatusCapability,
capabilities = {},
controller;
mockOpenMCT,
mockSelection,
mockInspectorViews,
mockTypeDef,
controller,
container,
$document = [],
selectable = [];
beforeEach(function () {
mockTypeDefinition = {
inspector:
{
'regions': [
{'name': 'Part One'},
{'name': 'Part Two'}
]
}
mockTypeDef = {
typeDef: {
inspector: "some-key"
}
};
mockTypeCapability = jasmine.createSpyObj('typeCapability', [
'getDefinition'
]);
mockTypeCapability.getDefinition.andReturn(mockTypeDefinition);
capabilities.type = mockTypeCapability;
mockStatusCapability = jasmine.createSpyObj('statusCapability', [
'listen'
]);
capabilities.status = mockStatusCapability;
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getCapability'
]);
mockDomainObject.getCapability.andCallFake(function (name) {
return capabilities[name];
});
mockPolicyService = jasmine.createSpyObj('policyService', [
'allow'
]);
mockDomainObject.getCapability.andReturn(mockTypeDef);
mockScope = jasmine.createSpyObj('$scope',
['$on']
['$on', 'selection']
);
mockScope.domainObject = mockDomainObject;
selectable[0] = {
context: {
oldItem: mockDomainObject
}
};
mockSelection = jasmine.createSpyObj("selection", [
'on',
'off',
'get'
]);
mockSelection.get.andReturn(selectable);
mockInspectorViews = jasmine.createSpyObj('inspectorViews', ['get']);
mockOpenMCT = {
selection: mockSelection,
inspectorViews: mockInspectorViews
};
container = jasmine.createSpy('container', ['innerHTML']);
$document[0] = jasmine.createSpyObj("$document", ['querySelectorAll']);
$document[0].querySelectorAll.andReturn([container]);
controller = new InspectorController(mockScope, mockOpenMCT, $document);
});
it("filters out regions disallowed by region policy", function () {
mockPolicyService.allow.andReturn(false);
controller = new InspectorController(mockScope, mockPolicyService);
expect(mockScope.regions.length).toBe(0);
it("listens for selection change event", function () {
expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
expect(controller.selectedItem()).toEqual(mockDomainObject);
var mockItem = jasmine.createSpyObj('domainObject', [
'getCapability'
]);
mockItem.getCapability.andReturn(mockTypeDef);
selectable[0].context.oldItem = mockItem;
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
expect(controller.selectedItem()).toEqual(mockItem);
});
it("does not filter out regions allowed by region policy", function () {
mockPolicyService.allow.andReturn(true);
controller = new InspectorController(mockScope, mockPolicyService);
expect(mockScope.regions.length).toBe(2);
it("cleans up on scope destroy", function () {
expect(mockScope.$on).toHaveBeenCalledWith(
'$destroy',
jasmine.any(Function)
);
mockScope.$on.calls[0].args[1]();
expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
});
it("Responds to status changes", function () {
mockPolicyService.allow.andReturn(true);
controller = new InspectorController(mockScope, mockPolicyService);
expect(mockScope.regions.length).toBe(2);
expect(mockStatusCapability.listen).toHaveBeenCalled();
mockPolicyService.allow.andReturn(false);
mockStatusCapability.listen.mostRecentCall.args[0]();
expect(mockScope.regions.length).toBe(0);
});
it("Unregisters status listener", function () {
var mockListener = jasmine.createSpy('listener');
mockStatusCapability.listen.andReturn(mockListener);
controller = new InspectorController(mockScope, mockPolicyService);
expect(mockScope.$on).toHaveBeenCalledWith("$destroy", jasmine.any(Function));
mockScope.$on.mostRecentCall.args[1]();
expect(mockListener).toHaveBeenCalled();
it("adds selection object to scope", function () {
expect(mockScope.selection).toEqual(selectable);
expect(controller.selectedItem()).toEqual(mockDomainObject);
});
});
}