diff --git a/platform/commonUI/edit/bundle.json b/platform/commonUI/edit/bundle.json index 2aed41c2ee..c216a9d877 100644 --- a/platform/commonUI/edit/bundle.json +++ b/platform/commonUI/edit/bundle.json @@ -59,7 +59,7 @@ "glyph": "Z", "name": "Remove", "description": "Remove this object from its containing object.", - "depends": [ "$q" ] + "depends": [ "$q", "navigationService" ] }, { "key": "save", diff --git a/platform/commonUI/edit/src/actions/RemoveAction.js b/platform/commonUI/edit/src/actions/RemoveAction.js index da1c81b486..2d8e224491 100644 --- a/platform/commonUI/edit/src/actions/RemoveAction.js +++ b/platform/commonUI/edit/src/actions/RemoveAction.js @@ -41,9 +41,10 @@ define( * @constructor * @implements {Action} */ - function RemoveAction($q, context) { + function RemoveAction($q, navigationService, context) { this.domainObject = (context || {}).domainObject; this.$q = $q; + this.navigationService = navigationService; } /** @@ -53,8 +54,8 @@ define( */ RemoveAction.prototype.perform = function () { var $q = this.$q, + navigationService = this.navigationService, domainObject = this.domainObject; - /* * Check whether an object ID matches the ID of the object being * removed (used to filter a parent's composition to handle the @@ -80,21 +81,53 @@ define( var persistence = domainObject.getCapability('persistence'); return persistence && persistence.persist(); } + + /* + * Checks current object and ascendants of current + * object with object being removed, if the current + * object or any in the current object's path is being removed, + * navigate back to parent of removed object. + */ + function checkObjectNavigation(object, parentObject) { + // Traverse object starts at current location + var traverseObject = (navigationService).getNavigation(); + + // Stop when object is not defined (above ROOT) + while (traverseObject) { + + // If object currently traversed to is object being removed + // navigate to parent of current object and then exit loop + if (traverseObject.getId() === object.getId()) { + navigationService.setNavigation(parentObject); + return; + } + // Traverses to parent of current object, moving + // up the ascendant path + traverseObject = traverseObject.getCapability('context').getParent(); + } + } /* * Remove the object from its parent, as identified by its context - * capability. + * capability. Based on object's location and selected object's location + * user may be navigated to existing parent object */ - function removeFromContext(contextCapability) { - var parent = contextCapability.getParent(); + function removeFromContext(object) { + var contextCapability = object.getCapability('context'), + parent = contextCapability.getParent(); + + // If currently within path of removed object(s), + // navigates to existing object up tree + checkObjectNavigation(object, parent); + return $q.when( - parent.useCapability('mutation', doMutate) - ).then(function () { - return doPersist(parent); - }); + parent.useCapability('mutation', doMutate) + ).then(function () { + return doPersist(parent); + }); } - return $q.when(this.domainObject.getCapability('context')) + return $q.when(domainObject) .then(removeFromContext); }; diff --git a/platform/commonUI/edit/test/actions/RemoveActionSpec.js b/platform/commonUI/edit/test/actions/RemoveActionSpec.js index d23542f0ee..116627c87d 100644 --- a/platform/commonUI/edit/test/actions/RemoveActionSpec.js +++ b/platform/commonUI/edit/test/actions/RemoveActionSpec.js @@ -28,9 +28,16 @@ define( describe("The Remove action", function () { var mockQ, + mockNavigationService, mockDomainObject, mockParent, + mockChildObject, + mockGrandchildObject, + mockRootObject, mockContext, + mockChildContext, + mockGrandchildContext, + mockRootContext, mockMutation, mockPersistence, mockType, @@ -54,6 +61,18 @@ define( "domainObject", [ "getId", "getCapability" ] ); + mockChildObject = jasmine.createSpyObj( + "domainObject", + [ "getId", "getCapability" ] + ); + mockGrandchildObject = jasmine.createSpyObj( + "domainObject", + [ "getId", "getCapability" ] + ); + mockRootObject = jasmine.createSpyObj( + "domainObject", + [ "getId", "getCapability" ] + ); mockQ = { when: mockPromise }; mockParent = { getModel: function () { @@ -67,28 +86,41 @@ define( } }; mockContext = jasmine.createSpyObj("context", [ "getParent" ]); + mockChildContext = jasmine.createSpyObj("context", [ "getParent" ]); + mockGrandchildContext = jasmine.createSpyObj("context", [ "getParent" ]); + mockRootContext = jasmine.createSpyObj("context", [ "getParent" ]); mockMutation = jasmine.createSpyObj("mutation", [ "invoke" ]); mockPersistence = jasmine.createSpyObj("persistence", [ "persist" ]); mockType = jasmine.createSpyObj("type", [ "hasFeature" ]); - + mockNavigationService = jasmine.createSpyObj( + "navigationService", + [ + "getNavigation", + "setNavigation", + "addListener", + "removeListener" + ] + ); + mockNavigationService.getNavigation.andReturn(mockDomainObject); + + mockDomainObject.getId.andReturn("test"); mockDomainObject.getCapability.andReturn(mockContext); mockContext.getParent.andReturn(mockParent); mockType.hasFeature.andReturn(true); - capabilities = { mutation: mockMutation, persistence: mockPersistence, type: mockType }; model = { - composition: [ "a", "test", "b", "c" ] + composition: [ "a", "test", "b" ] }; actionContext = { domainObject: mockDomainObject }; - action = new RemoveAction(mockQ, actionContext); + action = new RemoveAction(mockQ, mockNavigationService, actionContext); }); it("only applies to objects with parents", function () { @@ -123,11 +155,64 @@ define( // Should have removed "test" - that was our // mock domain object's id. - expect(result.composition).toEqual(["a", "b", "c"]); + expect(result.composition).toEqual(["a", "b"]); // Finally, should have persisted expect(mockPersistence.persist).toHaveBeenCalled(); }); + + it("removes parent of object currently navigated to", function () { + // Navigates to child object + mockNavigationService.getNavigation.andReturn(mockChildObject); + + // Test is id of object being removed + // Child object has different id + mockDomainObject.getId.andReturn("test"); + mockChildObject.getId.andReturn("not test"); + + // Sets context for the child and domainObject + mockDomainObject.getCapability.andReturn(mockContext); + mockChildObject.getCapability.andReturn(mockChildContext); + + // Parents of child and domainObject are set + mockContext.getParent.andReturn(mockParent); + mockChildContext.getParent.andReturn(mockDomainObject); + + mockType.hasFeature.andReturn(true); + + action.perform(); + + // Expects navigation to parent of domainObject (removed object) + expect(mockNavigationService.setNavigation).toHaveBeenCalledWith(mockParent); + }); + + it("checks if removing object not in ascendent path (reaches ROOT)", function () { + // Navigates to grandchild of ROOT + mockNavigationService.getNavigation.andReturn(mockGrandchildObject); + + // domainObject (grandparent) is set as ROOT, child and grandchild + // are set objects not being removed + mockDomainObject.getId.andReturn("test 1"); + mockRootObject.getId.andReturn("ROOT"); + mockChildObject.getId.andReturn("not test 2"); + mockGrandchildObject.getId.andReturn("not test 3"); + + // Sets context for the grandchild, child, and domainObject + mockRootObject.getCapability.andReturn(mockRootContext); + mockChildObject.getCapability.andReturn(mockChildContext); + mockGrandchildObject.getCapability.andReturn(mockGrandchildContext); + + // Parents of grandchild and child are set + mockChildContext.getParent.andReturn(mockRootObject); + mockGrandchildContext.getParent.andReturn(mockChildObject); + + mockType.hasFeature.andReturn(true); + + action.perform(); + + // Expects no navigation to occur + expect(mockNavigationService.setNavigation).not.toHaveBeenCalled(); + }); }); } diff --git a/platform/commonUI/general/res/css/tree.css b/platform/commonUI/general/res/css/tree.css index 93414d5dc1..bf2792ad0c 100644 --- a/platform/commonUI/general/res/css/tree.css +++ b/platform/commonUI/general/res/css/tree.css @@ -447,6 +447,7 @@ ul.tree { color: #fff; } /* line 287, ../sass/search/_search.scss */ .search .search-scroll .results .search-result-item .label .type-icon .l-icon-link { + display: none; text-shadow: black 0 1px 2px; z-index: 2; color: #49dedb; @@ -455,37 +456,37 @@ ul.tree { height: 8px; width: 8px; margin-left: -25px; } - /* line 296, ../sass/search/_search.scss */ + /* line 299, ../sass/search/_search.scss */ .search .search-scroll .results .search-result-item:not(.selected):hover { background: #404040; color: #cccccc; } - /* line 299, ../sass/search/_search.scss */ + /* line 302, ../sass/search/_search.scss */ .search .search-scroll .results .search-result-item:not(.selected):hover .context-trigger { display: block; } - /* line 302, ../sass/search/_search.scss */ + /* line 305, ../sass/search/_search.scss */ .search .search-scroll .results .search-result-item:not(.selected):hover .icon { color: #33ccff; } - /* line 310, ../sass/search/_search.scss */ + /* line 313, ../sass/search/_search.scss */ .search .search-scroll .load-icon { position: relative; } - /* line 312, ../sass/search/_search.scss */ + /* line 315, ../sass/search/_search.scss */ .search .search-scroll .load-icon.loading { pointer-events: none; margin-left: 6px; } - /* line 316, ../sass/search/_search.scss */ + /* line 319, ../sass/search/_search.scss */ .search .search-scroll .load-icon.loading .title-label { font-style: italic; font-size: .9em; opacity: 0.5; margin-left: 26px; line-height: 24px; } - /* line 326, ../sass/search/_search.scss */ + /* line 329, ../sass/search/_search.scss */ .search .search-scroll .load-icon.loading .wait-spinner { margin-left: 6px; } - /* line 331, ../sass/search/_search.scss */ + /* line 334, ../sass/search/_search.scss */ .search .search-scroll .load-icon:not(.loading) { cursor: pointer; } - /* line 336, ../sass/search/_search.scss */ + /* line 339, ../sass/search/_search.scss */ .search .search-scroll .load-more-button { margin-top: 5px 0; font-size: 0.8em; diff --git a/platform/commonUI/general/res/sass/search/_search.scss b/platform/commonUI/general/res/sass/search/_search.scss index ce9286e947..079777cc66 100644 --- a/platform/commonUI/general/res/sass/search/_search.scss +++ b/platform/commonUI/general/res/sass/search/_search.scss @@ -285,6 +285,9 @@ } .label .type-icon .l-icon-link { + // Hide links for now. See GitHub issue #84. + display: none; + @include txtShdwSubtle(1); z-index: 2; @include ancillaryIcon(8px, $colorIconLink);