mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 02:29:24 +00:00
Compare commits
8 Commits
vue-table-
...
create-new
Author | SHA1 | Date | |
---|---|---|---|
a53a669154 | |||
0602613b6e | |||
7620432388 | |||
fc8663c049 | |||
adfe30a891 | |||
5218e350c3 | |||
88615e92d2 | |||
12477a220b |
@ -33,6 +33,7 @@ define([
|
||||
"./src/actions/SaveAndStopEditingAction",
|
||||
"./src/actions/SaveAsAction",
|
||||
"./src/actions/CancelAction",
|
||||
"./src/actions/CreateNewFolderAction",
|
||||
"./src/policies/EditActionPolicy",
|
||||
"./src/policies/EditPersistableObjectsPolicy",
|
||||
"./src/policies/EditableLinkPolicy",
|
||||
@ -71,6 +72,7 @@ define([
|
||||
SaveAndStopEditingAction,
|
||||
SaveAsAction,
|
||||
CancelAction,
|
||||
CreateNewFolderAction,
|
||||
EditActionPolicy,
|
||||
EditPersistableObjectsPolicy,
|
||||
EditableLinkPolicy,
|
||||
@ -145,7 +147,10 @@ define([
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$timeout",
|
||||
"objectService"
|
||||
"objectService",
|
||||
"typeService",
|
||||
"policyService",
|
||||
"instantiate"
|
||||
]
|
||||
}
|
||||
],
|
||||
@ -242,6 +247,14 @@ define([
|
||||
"cssClass": "icon-x no-label",
|
||||
"description": "Discard changes made to these objects.",
|
||||
"depends": []
|
||||
},
|
||||
{
|
||||
"key": "create-new-folder",
|
||||
"implementation": CreateNewFolderAction,
|
||||
"description": "Creates a new folder.",
|
||||
"depends": [
|
||||
"typeService"
|
||||
]
|
||||
}
|
||||
],
|
||||
"policies": [
|
||||
|
@ -0,0 +1,33 @@
|
||||
<!--span
|
||||
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.
|
||||
-->
|
||||
<div name="newFolder" ng-controller="LocatorController">
|
||||
<div ng-if="!createNewFolder">
|
||||
<a class="s-button icon-folder-new" ng-click="createNewFolderClickHandler()" >
|
||||
<span class="title-label">New Folder</span>
|
||||
</a>
|
||||
</div>
|
||||
<div ng-if="createNewFolder">
|
||||
<span><input type="text" ng-model="newFolderName" name="newFolderName"></span>
|
||||
<a class="s-button" ng-click="createClickHandler()">Create</a>
|
||||
<a class="s-button icon-x" ng-click="cancelClickHandler()"></a>
|
||||
</div>
|
||||
</div>
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
|
||||
@ -26,4 +26,22 @@
|
||||
ng-model="treeModel">
|
||||
</mct-representation>
|
||||
</div>
|
||||
|
||||
<!-- Create New Folder Action -->
|
||||
<div class="newFolderCreation" style="margin-top:10px; position:absolute; left:0px;">
|
||||
|
||||
<!-- New folder button, triggers create new folder action. -->
|
||||
<div ng-show="!newFolderCreationTriggered">
|
||||
<a class="s-button icon-folder-new" ng-class="{disabled: !validParent()}" ng-click="newFolderButtonClickHandler()">
|
||||
<span class="text-label">New Folder</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Get folder name -->
|
||||
<div ng-show="newFolderCreationTriggered">
|
||||
<input type="text" ng-model="newFolderNameInput">
|
||||
<a class="s-button" ng-class="{ disabled: !validParent() || !validFolderName() }" ng-click="newFolderCreateButtonClickHandler()">Create</a>
|
||||
<a class="s-button icon-x" ng-click="newFolderCancelButtonClickHandler()"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
89
platform/commonUI/edit/src/actions/CreateNewFolderAction.js
Normal file
89
platform/commonUI/edit/src/actions/CreateNewFolderAction.js
Normal file
@ -0,0 +1,89 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
define([
|
||||
|
||||
],
|
||||
function (
|
||||
|
||||
) {
|
||||
|
||||
/**
|
||||
* The CreateNewFolderAction; action is triggered by the new folder button in the locator.
|
||||
*
|
||||
* @constructor
|
||||
* @implements {Action}
|
||||
* @memberof platform/commonUI/edit
|
||||
*/
|
||||
function CreateNewFolderAction(
|
||||
typeService,
|
||||
context
|
||||
) {
|
||||
this.parent = (context || {}).domainObject;
|
||||
this.typeService = typeService;
|
||||
this.type = typeService.getType('folder');
|
||||
}
|
||||
|
||||
CreateNewFolderAction.prototype.perform = function (folderName) {
|
||||
var parent = this.parent,
|
||||
typeService = this.typeService,
|
||||
newModel = this.type.getInitialModel(),
|
||||
folderType = typeService.getType('folder');
|
||||
|
||||
newModel.type = folderType.getKey();
|
||||
newModel.name = folderName;
|
||||
|
||||
function instantiateObject() {
|
||||
var newObject = parent.useCapability('instantiation', newModel);
|
||||
newObject.useCapability('mutation', function () {
|
||||
newModel.location = parent.getId();
|
||||
});
|
||||
return addToParentAndReturn(newObject);
|
||||
}
|
||||
|
||||
function addToParentAndReturn(newObject) {
|
||||
return parent.getCapability('composition').add(newObject)
|
||||
.then(function () {
|
||||
return newObject;
|
||||
});
|
||||
}
|
||||
|
||||
return instantiateObject(newModel, parent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if this action is applicable in a given context.
|
||||
* This will ensure that a domain object is present in the context,
|
||||
* and that this domain object is in Edit mode.
|
||||
* @returns true if applicable
|
||||
*/
|
||||
CreateNewFolderAction.appliesTo = function (context) {
|
||||
var parent = (context || {}).domainObject;
|
||||
return parent && parent.hasCapability('editor');
|
||||
|
||||
};
|
||||
|
||||
return CreateNewFolderAction;
|
||||
}
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
@ -31,7 +31,7 @@ define(
|
||||
* @memberof platform/commonUI/browse
|
||||
* @constructor
|
||||
*/
|
||||
function LocatorController($scope, $timeout, objectService) {
|
||||
function LocatorController($scope, $timeout, objectService, typeService, policyService, instantiate) {
|
||||
// Populate values needed by the locator control. These are:
|
||||
// * rootObject: The top-level object, since we want to show
|
||||
// the full tree
|
||||
@ -66,8 +66,8 @@ define(
|
||||
|
||||
// Restrict which locations can be selected
|
||||
if (domainObject &&
|
||||
$scope.structure &&
|
||||
$scope.structure.validate) {
|
||||
$scope.structure &&
|
||||
$scope.structure.validate) {
|
||||
if (!$scope.structure.validate(domainObject)) {
|
||||
setLocatingObject(priorObject, undefined);
|
||||
return;
|
||||
@ -81,11 +81,64 @@ define(
|
||||
!!$scope.treeModel.selectedObject
|
||||
);
|
||||
}
|
||||
|
||||
// Check if create new folder is a valid action for selected object
|
||||
$scope.validParent = function () {
|
||||
if ($scope.treeModel.selectedObject) {
|
||||
return policyService.allow(
|
||||
"composition",
|
||||
$scope.treeModel.selectedObject,
|
||||
instantiate(typeService.getType('folder').getInitialModel())
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$scope.newFolderButtonClickHandler = function () {
|
||||
$scope.newFolderCreationTriggered = true;
|
||||
};
|
||||
|
||||
$scope.newFolderCancelButtonClickHandler = function () {
|
||||
$scope.newFolderCreationTriggered = false;
|
||||
resetNewFolderNameInput();
|
||||
};
|
||||
|
||||
// Get expected input pattern for folder name
|
||||
var folderNamePattern = new RegExp(
|
||||
typeService.getType('folder').getProperties()[0].propertyDefinition.pattern
|
||||
);
|
||||
|
||||
// Validate folder name externally to avoid affecting overall form validation
|
||||
$scope.validFolderName = function () {
|
||||
return $scope.newFolderNameInput && folderNamePattern.test($scope.newFolderNameInput);
|
||||
};
|
||||
|
||||
function selectAndScrollToNewFolder(newFolder) {
|
||||
$scope.treeModel.selectedObject = newFolder;
|
||||
}
|
||||
|
||||
function resetNewFolderNameInput() {
|
||||
$scope.newFolderNameInput = "Unnamed Folder";
|
||||
$scope.newFolderCreationTriggered = false;
|
||||
}
|
||||
|
||||
|
||||
// Create new folder, update selection to new folder and reset new folder button
|
||||
$scope.newFolderCreateButtonClickHandler = function () {
|
||||
var createNewFolderAction = $scope.treeModel.selectedObject.getCapability('action').getActions('create-new-folder')[0];
|
||||
createNewFolderAction.perform($scope.newFolderNameInput)
|
||||
.then(selectAndScrollToNewFolder)
|
||||
.then(resetNewFolderNameInput);
|
||||
};
|
||||
|
||||
// Initial state for the tree's model
|
||||
$scope.treeModel =
|
||||
{ selectedObject: $scope.ngModel[$scope.field] };
|
||||
$scope.treeModel = { selectedObject: $scope.ngModel[$scope.field] };
|
||||
|
||||
//Initial values for new folder action
|
||||
$scope.newFolderNameInput = "Unnamed Folder";
|
||||
$scope.newFolderCreationTriggered = false;
|
||||
|
||||
// Watch for changes from the tree
|
||||
$scope.$watch("treeModel.selectedObject", setLocatingObject);
|
||||
|
117
platform/commonUI/edit/test/actions/CreateNewFolderActionSpec.js
Normal file
117
platform/commonUI/edit/test/actions/CreateNewFolderActionSpec.js
Normal file
@ -0,0 +1,117 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['../../src/actions/CreateNewFolderAction'],
|
||||
function (CreateNewFolderAction) {
|
||||
|
||||
describe("The Create New Folder Action", function () {
|
||||
var mockDomainObject,
|
||||
mockNewObject,
|
||||
mockType,
|
||||
testModel,
|
||||
mockFolderName,
|
||||
mockTypeService,
|
||||
mockActionContext,
|
||||
mockCompositionCapability,
|
||||
action;
|
||||
function mockPromise(value) {
|
||||
return (value && value.then) ? value : {
|
||||
then: function (callback) {
|
||||
return mockPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
beforeEach(function () {
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[
|
||||
"getCapability",
|
||||
"useCapability",
|
||||
"hasCapability",
|
||||
"getId"
|
||||
]
|
||||
);
|
||||
mockNewObject = jasmine.createSpyObj(
|
||||
"newObject",
|
||||
[
|
||||
"getCapability",
|
||||
"useCapability",
|
||||
"hasCapability",
|
||||
"getId"
|
||||
]
|
||||
);
|
||||
mockType = jasmine.createSpyObj(
|
||||
"type",
|
||||
[
|
||||
"getKey",
|
||||
"getInitialModel"
|
||||
]
|
||||
);
|
||||
testModel = {
|
||||
type: mockType,
|
||||
name: "Name",
|
||||
location: "someLocation"
|
||||
};
|
||||
mockFolderName = "Name";
|
||||
mockTypeService = jasmine.createSpyObj(
|
||||
"typeService",
|
||||
["getType"]
|
||||
);
|
||||
mockActionContext = { domainObject: mockDomainObject };
|
||||
mockCompositionCapability = jasmine.createSpyObj(
|
||||
"composition",
|
||||
["add"]
|
||||
);
|
||||
mockType.getKey.and.returnValue("test");
|
||||
mockType.getInitialModel.and.returnValue(testModel);
|
||||
mockDomainObject.getCapability.and.callFake(function (capability) {
|
||||
return (capability === 'composition') && mockCompositionCapability;
|
||||
});
|
||||
mockDomainObject.hasCapability.and.returnValue(true);
|
||||
mockCompositionCapability.add.and.returnValue(mockPromise(true));
|
||||
mockDomainObject.useCapability.and.callFake(function (capability) {
|
||||
return (capability === 'instantiation') && mockNewObject;
|
||||
});
|
||||
mockTypeService.getType.and.returnValue(mockType);
|
||||
mockDomainObject.getId.and.returnValue("id");
|
||||
action = new CreateNewFolderAction(mockTypeService, mockActionContext);
|
||||
});
|
||||
it("uses the instantiation capability when performed", function () {
|
||||
action.perform(mockFolderName);
|
||||
expect(mockDomainObject.useCapability)
|
||||
.toHaveBeenCalledWith("instantiation", jasmine.any(Object));
|
||||
});
|
||||
it("adds new objects to the parent's composition", function () {
|
||||
action.perform(mockFolderName);
|
||||
expect(mockDomainObject.getCapability).toHaveBeenCalledWith("composition");
|
||||
expect(mockCompositionCapability.add).toHaveBeenCalled();
|
||||
});
|
||||
it("is only applicable when a domain object is in context", function () {
|
||||
expect(CreateNewFolderAction.appliesTo(mockActionContext)).toBeTruthy();
|
||||
expect(CreateNewFolderAction.appliesTo({})).toBeFalsy();
|
||||
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith('editor');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@ -31,26 +31,63 @@ define(
|
||||
var mockScope,
|
||||
mockTimeout,
|
||||
mockDomainObject,
|
||||
mockFolderObject,
|
||||
mockRootObject,
|
||||
mockContext,
|
||||
mockActions,
|
||||
mockObjectService,
|
||||
mockTypeService,
|
||||
mockType,
|
||||
mockInstantiate,
|
||||
mockPolicyService,
|
||||
getObjectsPromise,
|
||||
testModel,
|
||||
capabilities,
|
||||
mockCreateNewFolderAction,
|
||||
mockActionCapability,
|
||||
mockProperties,
|
||||
controller;
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
["$watch"]
|
||||
["$watch", "validParent"]
|
||||
);
|
||||
mockTimeout = jasmine.createSpy("$timeout");
|
||||
mockInstantiate = jasmine.createSpy("instantiate");
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getCapability"]
|
||||
[
|
||||
"useCapability",
|
||||
"getModel",
|
||||
"getCapability"
|
||||
]
|
||||
);
|
||||
mockFolderObject = jasmine.createSpyObj(
|
||||
"folderObject",
|
||||
[
|
||||
"useCapability",
|
||||
"getModel",
|
||||
"getCapability"
|
||||
]
|
||||
);
|
||||
mockCreateNewFolderAction = jasmine.createSpyObj(
|
||||
"createNewFolderAction",
|
||||
[
|
||||
"perform"
|
||||
]
|
||||
);
|
||||
mockRootObject = jasmine.createSpyObj(
|
||||
"rootObject",
|
||||
["getCapability"]
|
||||
);
|
||||
mockActionCapability = jasmine.createSpyObj(
|
||||
"actionCapability",
|
||||
[
|
||||
"getActions",
|
||||
"perform"
|
||||
]
|
||||
);
|
||||
mockContext = jasmine.createSpyObj(
|
||||
"context",
|
||||
["getRoot"]
|
||||
@ -59,25 +96,70 @@ define(
|
||||
"objectService",
|
||||
["getObjects"]
|
||||
);
|
||||
mockTypeService = jasmine.createSpyObj(
|
||||
"typeService",
|
||||
["getType"]
|
||||
);
|
||||
mockPolicyService = jasmine.createSpyObj(
|
||||
"policyService",
|
||||
["allow"]
|
||||
);
|
||||
getObjectsPromise = jasmine.createSpyObj(
|
||||
"promise",
|
||||
["then"]
|
||||
);
|
||||
|
||||
mockDomainObject.getCapability.and.returnValue(mockContext);
|
||||
mockType = jasmine.createSpyObj(
|
||||
"type",
|
||||
[
|
||||
"getKey",
|
||||
"getProperties",
|
||||
"getInitialModel"
|
||||
]
|
||||
);
|
||||
testModel = { someKey: "some value" };
|
||||
mockProperties = ['a', 'b', 'c'].map(function (k) {
|
||||
var mockProperty = jasmine.createSpyObj(
|
||||
'property-' + k,
|
||||
['propertyDefinition']
|
||||
);
|
||||
mockProperty.propertyDefinition = {
|
||||
key: "name",
|
||||
pattern: "test"
|
||||
};
|
||||
return mockProperty;
|
||||
});
|
||||
capabilities = {
|
||||
"action" : mockActionCapability,
|
||||
"context": mockContext
|
||||
};
|
||||
mockActions = [mockCreateNewFolderAction];
|
||||
mockContext.getRoot.and.returnValue(mockRootObject);
|
||||
mockObjectService.getObjects.and.returnValue(getObjectsPromise);
|
||||
|
||||
mockTypeService.getType.and.callFake(function (typename) {
|
||||
return mockType;
|
||||
});
|
||||
mockInstantiate.and.returnValue(mockFolderObject);
|
||||
mockType.getKey.and.returnValue("test");
|
||||
mockType.getInitialModel.and.returnValue(testModel);
|
||||
mockType.getProperties.and.returnValue(mockProperties);
|
||||
mockDomainObject.getCapability.and.callFake(function (capability) {
|
||||
return capabilities[capability];
|
||||
});
|
||||
mockDomainObject.useCapability.and.returnValue();
|
||||
mockDomainObject.getModel.and.returnValue(testModel);
|
||||
mockFolderObject.getCapability.and.returnValue(capabilities);
|
||||
mockFolderObject.useCapability.and.returnValue();
|
||||
mockFolderObject.getModel.and.returnValue(testModel);
|
||||
mockScope.ngModel = {};
|
||||
mockScope.field = "someField";
|
||||
|
||||
controller = new LocatorController(mockScope, mockTimeout, mockObjectService);
|
||||
controller = new LocatorController(mockScope, mockTimeout, mockObjectService, mockTypeService, mockPolicyService, mockInstantiate);
|
||||
});
|
||||
describe("when context is available", function () {
|
||||
|
||||
beforeEach(function () {
|
||||
mockContext.getRoot.and.returnValue(mockRootObject);
|
||||
controller = new LocatorController(mockScope, mockTimeout, mockObjectService);
|
||||
controller = new LocatorController(mockScope, mockTimeout, mockObjectService, mockTypeService, mockPolicyService, mockInstantiate);
|
||||
});
|
||||
|
||||
it("adds a treeModel to scope", function () {
|
||||
@ -145,7 +227,7 @@ define(
|
||||
getObjectsPromise.then.and.callFake(function (callback) {
|
||||
callback({'ROOT': defaultRoot});
|
||||
});
|
||||
controller = new LocatorController(mockScope, mockTimeout, mockObjectService);
|
||||
controller = new LocatorController(mockScope, mockTimeout, mockObjectService, mockTypeService, mockPolicyService, mockInstantiate);
|
||||
});
|
||||
|
||||
it("provides a default context where none is available", function () {
|
||||
@ -169,3 +251,4 @@ define(
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
Reference in New Issue
Block a user