Merge branch 'master' into inspector

This commit is contained in:
slhale 2015-08-27 10:40:43 -07:00
commit a957f87f61
5 changed files with 147 additions and 25 deletions

View File

@ -59,7 +59,7 @@
"glyph": "Z", "glyph": "Z",
"name": "Remove", "name": "Remove",
"description": "Remove this object from its containing object.", "description": "Remove this object from its containing object.",
"depends": [ "$q" ] "depends": [ "$q", "navigationService" ]
}, },
{ {
"key": "save", "key": "save",

View File

@ -41,9 +41,10 @@ define(
* @constructor * @constructor
* @implements {Action} * @implements {Action}
*/ */
function RemoveAction($q, context) { function RemoveAction($q, navigationService, context) {
this.domainObject = (context || {}).domainObject; this.domainObject = (context || {}).domainObject;
this.$q = $q; this.$q = $q;
this.navigationService = navigationService;
} }
/** /**
@ -53,8 +54,8 @@ define(
*/ */
RemoveAction.prototype.perform = function () { RemoveAction.prototype.perform = function () {
var $q = this.$q, var $q = this.$q,
navigationService = this.navigationService,
domainObject = this.domainObject; domainObject = this.domainObject;
/* /*
* Check whether an object ID matches the ID of the object being * Check whether an object ID matches the ID of the object being
* removed (used to filter a parent's composition to handle the * removed (used to filter a parent's composition to handle the
@ -80,21 +81,53 @@ define(
var persistence = domainObject.getCapability('persistence'); var persistence = domainObject.getCapability('persistence');
return persistence && persistence.persist(); 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 * 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) { function removeFromContext(object) {
var parent = contextCapability.getParent(); 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( return $q.when(
parent.useCapability('mutation', doMutate) parent.useCapability('mutation', doMutate)
).then(function () { ).then(function () {
return doPersist(parent); return doPersist(parent);
}); });
} }
return $q.when(this.domainObject.getCapability('context')) return $q.when(domainObject)
.then(removeFromContext); .then(removeFromContext);
}; };

View File

@ -28,9 +28,16 @@ define(
describe("The Remove action", function () { describe("The Remove action", function () {
var mockQ, var mockQ,
mockNavigationService,
mockDomainObject, mockDomainObject,
mockParent, mockParent,
mockChildObject,
mockGrandchildObject,
mockRootObject,
mockContext, mockContext,
mockChildContext,
mockGrandchildContext,
mockRootContext,
mockMutation, mockMutation,
mockPersistence, mockPersistence,
mockType, mockType,
@ -54,6 +61,18 @@ define(
"domainObject", "domainObject",
[ "getId", "getCapability" ] [ "getId", "getCapability" ]
); );
mockChildObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getCapability" ]
);
mockGrandchildObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getCapability" ]
);
mockRootObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getCapability" ]
);
mockQ = { when: mockPromise }; mockQ = { when: mockPromise };
mockParent = { mockParent = {
getModel: function () { getModel: function () {
@ -67,28 +86,41 @@ define(
} }
}; };
mockContext = jasmine.createSpyObj("context", [ "getParent" ]); 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" ]); mockMutation = jasmine.createSpyObj("mutation", [ "invoke" ]);
mockPersistence = jasmine.createSpyObj("persistence", [ "persist" ]); mockPersistence = jasmine.createSpyObj("persistence", [ "persist" ]);
mockType = jasmine.createSpyObj("type", [ "hasFeature" ]); mockType = jasmine.createSpyObj("type", [ "hasFeature" ]);
mockNavigationService = jasmine.createSpyObj(
"navigationService",
[
"getNavigation",
"setNavigation",
"addListener",
"removeListener"
]
);
mockNavigationService.getNavigation.andReturn(mockDomainObject);
mockDomainObject.getId.andReturn("test"); mockDomainObject.getId.andReturn("test");
mockDomainObject.getCapability.andReturn(mockContext); mockDomainObject.getCapability.andReturn(mockContext);
mockContext.getParent.andReturn(mockParent); mockContext.getParent.andReturn(mockParent);
mockType.hasFeature.andReturn(true); mockType.hasFeature.andReturn(true);
capabilities = { capabilities = {
mutation: mockMutation, mutation: mockMutation,
persistence: mockPersistence, persistence: mockPersistence,
type: mockType type: mockType
}; };
model = { model = {
composition: [ "a", "test", "b", "c" ] composition: [ "a", "test", "b" ]
}; };
actionContext = { domainObject: mockDomainObject }; actionContext = { domainObject: mockDomainObject };
action = new RemoveAction(mockQ, actionContext); action = new RemoveAction(mockQ, mockNavigationService, actionContext);
}); });
it("only applies to objects with parents", function () { it("only applies to objects with parents", function () {
@ -123,11 +155,64 @@ define(
// Should have removed "test" - that was our // Should have removed "test" - that was our
// mock domain object's id. // mock domain object's id.
expect(result.composition).toEqual(["a", "b", "c"]); expect(result.composition).toEqual(["a", "b"]);
// Finally, should have persisted // Finally, should have persisted
expect(mockPersistence.persist).toHaveBeenCalled(); 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();
});
}); });
} }

View File

@ -447,6 +447,7 @@ ul.tree {
color: #fff; } color: #fff; }
/* line 287, ../sass/search/_search.scss */ /* line 287, ../sass/search/_search.scss */
.search .search-scroll .results .search-result-item .label .type-icon .l-icon-link { .search .search-scroll .results .search-result-item .label .type-icon .l-icon-link {
display: none;
text-shadow: black 0 1px 2px; text-shadow: black 0 1px 2px;
z-index: 2; z-index: 2;
color: #49dedb; color: #49dedb;
@ -455,37 +456,37 @@ ul.tree {
height: 8px; height: 8px;
width: 8px; width: 8px;
margin-left: -25px; } 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 { .search .search-scroll .results .search-result-item:not(.selected):hover {
background: #404040; background: #404040;
color: #cccccc; } 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 { .search .search-scroll .results .search-result-item:not(.selected):hover .context-trigger {
display: block; } 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 { .search .search-scroll .results .search-result-item:not(.selected):hover .icon {
color: #33ccff; } color: #33ccff; }
/* line 310, ../sass/search/_search.scss */ /* line 313, ../sass/search/_search.scss */
.search .search-scroll .load-icon { .search .search-scroll .load-icon {
position: relative; } position: relative; }
/* line 312, ../sass/search/_search.scss */ /* line 315, ../sass/search/_search.scss */
.search .search-scroll .load-icon.loading { .search .search-scroll .load-icon.loading {
pointer-events: none; pointer-events: none;
margin-left: 6px; } margin-left: 6px; }
/* line 316, ../sass/search/_search.scss */ /* line 319, ../sass/search/_search.scss */
.search .search-scroll .load-icon.loading .title-label { .search .search-scroll .load-icon.loading .title-label {
font-style: italic; font-style: italic;
font-size: .9em; font-size: .9em;
opacity: 0.5; opacity: 0.5;
margin-left: 26px; margin-left: 26px;
line-height: 24px; } line-height: 24px; }
/* line 326, ../sass/search/_search.scss */ /* line 329, ../sass/search/_search.scss */
.search .search-scroll .load-icon.loading .wait-spinner { .search .search-scroll .load-icon.loading .wait-spinner {
margin-left: 6px; } margin-left: 6px; }
/* line 331, ../sass/search/_search.scss */ /* line 334, ../sass/search/_search.scss */
.search .search-scroll .load-icon:not(.loading) { .search .search-scroll .load-icon:not(.loading) {
cursor: pointer; } cursor: pointer; }
/* line 336, ../sass/search/_search.scss */ /* line 339, ../sass/search/_search.scss */
.search .search-scroll .load-more-button { .search .search-scroll .load-more-button {
margin-top: 5px 0; margin-top: 5px 0;
font-size: 0.8em; font-size: 0.8em;

View File

@ -285,6 +285,9 @@
} }
.label .type-icon .l-icon-link { .label .type-icon .l-icon-link {
// Hide links for now. See GitHub issue #84.
display: none;
@include txtShdwSubtle(1); @include txtShdwSubtle(1);
z-index: 2; z-index: 2;
@include ancillaryIcon(8px, $colorIconLink); @include ancillaryIcon(8px, $colorIconLink);