Compare commits
10 Commits
export-mar
...
stacked-pl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2243fbb40b | ||
|
|
4a3dce9253 | ||
|
|
d80c0eef8e | ||
|
|
55829dcf05 | ||
|
|
d78956327c | ||
|
|
4a0728a55b | ||
|
|
2e1d57aa8c | ||
|
|
1c2b0678be | ||
|
|
6f810add43 | ||
|
|
12727adb16 |
@@ -131,10 +131,10 @@
|
||||
}
|
||||
],
|
||||
// maximum recent bounds to retain in conductor history
|
||||
records: 10,
|
||||
records: 10
|
||||
// maximum duration between start and end bounds
|
||||
// for utc-based time systems this is in milliseconds
|
||||
limit: ONE_DAY
|
||||
// limit: ONE_DAY
|
||||
},
|
||||
{
|
||||
name: "Realtime",
|
||||
|
||||
225
src/api/actions/ActionCollectionSpec.js
Normal file
225
src/api/actions/ActionCollectionSpec.js
Normal file
@@ -0,0 +1,225 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2020, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import ActionCollection from './ActionCollection';
|
||||
import { createOpenMct, resetApplicationState } from '../../utils/testing';
|
||||
|
||||
describe('The ActionCollection', () => {
|
||||
let openmct;
|
||||
let actionCollection;
|
||||
let mockApplicableActions;
|
||||
let mockObjectPath;
|
||||
let mockView;
|
||||
|
||||
beforeEach(() => {
|
||||
openmct = createOpenMct();
|
||||
mockObjectPath = [
|
||||
{
|
||||
name: 'mock folder',
|
||||
type: 'fake-folder',
|
||||
identifier: {
|
||||
key: 'mock-folder',
|
||||
namespace: ''
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'mock parent folder',
|
||||
type: 'fake-folder',
|
||||
identifier: {
|
||||
key: 'mock-parent-folder',
|
||||
namespace: ''
|
||||
}
|
||||
}
|
||||
];
|
||||
mockView = {
|
||||
getViewContext: () => {
|
||||
return {
|
||||
onlyAppliesToTestCase: true
|
||||
};
|
||||
}
|
||||
};
|
||||
mockApplicableActions = {
|
||||
'test-action-object-path': {
|
||||
name: 'Test Action Object Path',
|
||||
key: 'test-action-object-path',
|
||||
cssClass: 'test-action-object-path',
|
||||
description: 'This is a test action for object path',
|
||||
group: 'action',
|
||||
priority: 9,
|
||||
appliesTo: (objectPath) => {
|
||||
if (objectPath.length) {
|
||||
return objectPath[0].type === 'fake-folder';
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
invoke: () => {
|
||||
}
|
||||
},
|
||||
'test-action-view': {
|
||||
name: 'Test Action View',
|
||||
key: 'test-action-view',
|
||||
cssClass: 'test-action-view',
|
||||
description: 'This is a test action for view',
|
||||
group: 'action',
|
||||
priority: 9,
|
||||
showInStatusBar: true,
|
||||
appliesTo: (objectPath, view = {}) => {
|
||||
if (view.getViewContext) {
|
||||
let viewContext = view.getViewContext();
|
||||
|
||||
return viewContext.onlyAppliesToTestCase;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
invoke: () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
actionCollection = new ActionCollection(mockApplicableActions, mockObjectPath, mockView, openmct);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
actionCollection.destroy();
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("disable method invoked with action keys", () => {
|
||||
it("marks those actions as isDisabled", () => {
|
||||
let actionKey = 'test-action-object-path';
|
||||
let actionsObject = actionCollection.getActionsObject();
|
||||
let action = actionsObject[actionKey];
|
||||
|
||||
expect(action.isDisabled).toBeFalsy();
|
||||
|
||||
actionCollection.disable([actionKey]);
|
||||
actionsObject = actionCollection.getActionsObject();
|
||||
action = actionsObject[actionKey];
|
||||
|
||||
expect(action.isDisabled).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe("enable method invoked with action keys", () => {
|
||||
it("marks the isDisabled property as false", () => {
|
||||
let actionKey = 'test-action-object-path';
|
||||
|
||||
actionCollection.disable([actionKey]);
|
||||
|
||||
let actionsObject = actionCollection.getActionsObject();
|
||||
let action = actionsObject[actionKey];
|
||||
|
||||
expect(action.isDisabled).toBeTrue();
|
||||
|
||||
actionCollection.enable([actionKey]);
|
||||
actionsObject = actionCollection.getActionsObject();
|
||||
action = actionsObject[actionKey];
|
||||
|
||||
expect(action.isDisabled).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe("hide method invoked with action keys", () => {
|
||||
it("marks those actions as isHidden", () => {
|
||||
let actionKey = 'test-action-object-path';
|
||||
let actionsObject = actionCollection.getActionsObject();
|
||||
let action = actionsObject[actionKey];
|
||||
|
||||
expect(action.isHidden).toBeFalsy();
|
||||
|
||||
actionCollection.hide([actionKey]);
|
||||
actionsObject = actionCollection.getActionsObject();
|
||||
action = actionsObject[actionKey];
|
||||
|
||||
expect(action.isHidden).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe("show method invoked with action keys", () => {
|
||||
it("marks the isHidden property as false", () => {
|
||||
let actionKey = 'test-action-object-path';
|
||||
|
||||
actionCollection.hide([actionKey]);
|
||||
|
||||
let actionsObject = actionCollection.getActionsObject();
|
||||
let action = actionsObject[actionKey];
|
||||
|
||||
expect(action.isHidden).toBeTrue();
|
||||
|
||||
actionCollection.show([actionKey]);
|
||||
actionsObject = actionCollection.getActionsObject();
|
||||
action = actionsObject[actionKey];
|
||||
|
||||
expect(action.isHidden).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getVisibleActions method", () => {
|
||||
it("returns an array of non hidden actions", () => {
|
||||
let action1Key = 'test-action-object-path';
|
||||
let action2Key = 'test-action-view';
|
||||
|
||||
actionCollection.hide([action1Key]);
|
||||
|
||||
let visibleActions = actionCollection.getVisibleActions();
|
||||
|
||||
expect(Array.isArray(visibleActions)).toBeTrue();
|
||||
expect(visibleActions.length).toEqual(1);
|
||||
expect(visibleActions[0].key).toEqual(action2Key);
|
||||
|
||||
actionCollection.show([action1Key]);
|
||||
visibleActions = actionCollection.getVisibleActions();
|
||||
|
||||
expect(visibleActions.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getStatusBarActions method", () => {
|
||||
it("returns an array of non disabled, non hidden statusBar actions", () => {
|
||||
let action2Key = 'test-action-view';
|
||||
|
||||
let statusBarActions = actionCollection.getStatusBarActions();
|
||||
|
||||
expect(Array.isArray(statusBarActions)).toBeTrue();
|
||||
expect(statusBarActions.length).toEqual(1);
|
||||
expect(statusBarActions[0].key).toEqual(action2Key);
|
||||
|
||||
actionCollection.disable([action2Key]);
|
||||
statusBarActions = actionCollection.getStatusBarActions();
|
||||
|
||||
expect(statusBarActions.length).toEqual(0);
|
||||
|
||||
actionCollection.enable([action2Key]);
|
||||
statusBarActions = actionCollection.getStatusBarActions();
|
||||
|
||||
expect(statusBarActions.length).toEqual(1);
|
||||
expect(statusBarActions[0].key).toEqual(action2Key);
|
||||
|
||||
actionCollection.hide([action2Key]);
|
||||
statusBarActions = actionCollection.getStatusBarActions();
|
||||
|
||||
expect(statusBarActions.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -22,22 +22,41 @@
|
||||
|
||||
import ActionsAPI from './ActionsAPI';
|
||||
import { createOpenMct, resetApplicationState } from '../../utils/testing';
|
||||
import ActionCollection from './ActionCollection';
|
||||
|
||||
describe('The Actions API', () => {
|
||||
let openmct;
|
||||
let actionsAPI;
|
||||
let mockAction;
|
||||
let mockObjectPath;
|
||||
let mockObjectPathAction;
|
||||
let mockViewContext1;
|
||||
|
||||
beforeEach(() => {
|
||||
openmct = createOpenMct();
|
||||
actionsAPI = new ActionsAPI(openmct);
|
||||
mockObjectPathAction = {
|
||||
name: 'Test Action Object Path',
|
||||
key: 'test-action-object-path',
|
||||
cssClass: 'test-action-object-path',
|
||||
description: 'This is a test action for object path',
|
||||
group: 'action',
|
||||
priority: 9,
|
||||
appliesTo: (objectPath) => {
|
||||
if (objectPath.length) {
|
||||
return objectPath[0].type === 'fake-folder';
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
invoke: () => {
|
||||
}
|
||||
};
|
||||
mockAction = {
|
||||
name: 'Test Action',
|
||||
key: 'test-action',
|
||||
cssClass: 'test-action',
|
||||
description: 'This is a test action',
|
||||
name: 'Test Action View',
|
||||
key: 'test-action-view',
|
||||
cssClass: 'test-action-view',
|
||||
description: 'This is a test action for view',
|
||||
group: 'action',
|
||||
priority: 9,
|
||||
appliesTo: (objectPath, view = {}) => {
|
||||
@@ -45,8 +64,6 @@ describe('The Actions API', () => {
|
||||
let viewContext = view.getViewContext();
|
||||
|
||||
return viewContext.onlyAppliesToTestCase;
|
||||
} else if (objectPath.length) {
|
||||
return objectPath[0].type === 'fake-folder';
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -100,9 +117,32 @@ describe('The Actions API', () => {
|
||||
describe("get method", () => {
|
||||
beforeEach(() => {
|
||||
actionsAPI.register(mockAction);
|
||||
actionsAPI.register(mockObjectPathAction);
|
||||
});
|
||||
|
||||
it("returns an object with relevant actions when invoked with objectPath only", () => {
|
||||
it("returns an ActionCollection when invoked with an objectPath only", () => {
|
||||
let actionCollection = actionsAPI.get(mockObjectPath);
|
||||
let instanceOfActionCollection = actionCollection instanceof ActionCollection;
|
||||
|
||||
expect(instanceOfActionCollection).toBeTrue();
|
||||
});
|
||||
|
||||
it("returns an ActionCollection when invoked with an objectPath and view", () => {
|
||||
let actionCollection = actionsAPI.get(mockObjectPath, mockViewContext1);
|
||||
let instanceOfActionCollection = actionCollection instanceof ActionCollection;
|
||||
|
||||
expect(instanceOfActionCollection).toBeTrue();
|
||||
});
|
||||
|
||||
it("returns relevant actions when invoked with objectPath only", () => {
|
||||
let actionCollection = actionsAPI.get(mockObjectPath);
|
||||
let action = actionCollection.getActionsObject()[mockObjectPathAction.key];
|
||||
|
||||
expect(action.key).toEqual(mockObjectPathAction.key);
|
||||
expect(action.name).toEqual(mockObjectPathAction.name);
|
||||
});
|
||||
|
||||
it("returns relevant actions when invoked with objectPath and view", () => {
|
||||
let actionCollection = actionsAPI.get(mockObjectPath, mockViewContext1);
|
||||
let action = actionCollection.getActionsObject()[mockAction.key];
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ export default class DuplicateAction {
|
||||
{
|
||||
key: "name",
|
||||
control: "textfield",
|
||||
name: "Folder Name",
|
||||
name: "Name",
|
||||
pattern: "\\S+",
|
||||
required: true,
|
||||
cssClass: "l-input-lg"
|
||||
|
||||
@@ -48,13 +48,14 @@ export default class DuplicateTask {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the duplicate/copy task with the objects provided in the constructor.
|
||||
* Execute the duplicate/copy task with the objects provided.
|
||||
* @returns {promise} Which will resolve with a clone of the object
|
||||
* once complete.
|
||||
*/
|
||||
async duplicate(domainObject, parent, filter) {
|
||||
this.domainObject = domainObject;
|
||||
this.parent = parent;
|
||||
this.namespace = parent.identifier.namespace;
|
||||
this.filter = filter || this.isCreatable;
|
||||
|
||||
await this.buildDuplicationPlan();
|
||||
@@ -96,13 +97,14 @@ export default class DuplicateTask {
|
||||
let initialCount = this.clones.length;
|
||||
let dialog = this.openmct.overlays.progressDialog({
|
||||
progressPerc: 0,
|
||||
message: `Duplicating ${initialCount} files.`,
|
||||
message: `Duplicating ${initialCount} objects.`,
|
||||
iconClass: 'info',
|
||||
title: 'Duplicating'
|
||||
});
|
||||
let clonesDone = Promise.all(this.clones.map(clone => {
|
||||
|
||||
let clonesDone = Promise.all(this.clones.map((clone) => {
|
||||
let percentPersisted = Math.ceil(100 * (++this.persisted / initialCount));
|
||||
let message = `Duplicating ${initialCount - this.persisted} files.`;
|
||||
let message = `Duplicating ${initialCount - this.persisted} objects.`;
|
||||
|
||||
dialog.updateProgress(percentPersisted, message);
|
||||
|
||||
@@ -110,6 +112,7 @@ export default class DuplicateTask {
|
||||
}));
|
||||
|
||||
await clonesDone;
|
||||
|
||||
dialog.dismiss();
|
||||
this.openmct.notifications.info(`Duplicated ${this.persisted} objects.`);
|
||||
|
||||
@@ -141,10 +144,7 @@ export default class DuplicateTask {
|
||||
async duplicateObject(originalObject) {
|
||||
// Check if the creatable (or other passed in filter).
|
||||
if (this.filter(originalObject)) {
|
||||
// Clone original object
|
||||
let clone = this.cloneObjectModel(originalObject);
|
||||
|
||||
// Get children, if any
|
||||
let composeesCollection = this.openmct.composition.get(originalObject);
|
||||
let composees;
|
||||
|
||||
@@ -152,7 +152,6 @@ export default class DuplicateTask {
|
||||
composees = await composeesCollection.load();
|
||||
}
|
||||
|
||||
// Recursively duplicate children
|
||||
return this.duplicateComposees(clone, composees);
|
||||
}
|
||||
|
||||
@@ -198,12 +197,11 @@ export default class DuplicateTask {
|
||||
*/
|
||||
async duplicateComposees(clonedParent, composees = []) {
|
||||
let idMap = {};
|
||||
|
||||
let allComposeesDuplicated = composees.reduce(async (previousPromise, nextComposee) => {
|
||||
await previousPromise;
|
||||
let clonedComposee = await this.duplicateObject(nextComposee);
|
||||
idMap[this.getId(nextComposee)] = this.getId(clonedComposee);
|
||||
await this.composeChild(clonedComposee, clonedParent, clonedComposee !== nextComposee);
|
||||
this.composeChild(clonedComposee, clonedParent, clonedComposee !== nextComposee);
|
||||
|
||||
return;
|
||||
}, Promise.resolve());
|
||||
@@ -216,11 +214,9 @@ export default class DuplicateTask {
|
||||
return clonedParent;
|
||||
}
|
||||
|
||||
async composeChild(child, parent, setLocation) {
|
||||
const PERSIST_BOOL = false;
|
||||
let parentComposition = this.openmct.composition.get(parent);
|
||||
await parentComposition.load();
|
||||
parentComposition.add(child, PERSIST_BOOL);
|
||||
composeChild(child, parent, setLocation) {
|
||||
let childKeyString = this.openmct.objects.makeKeyString(child.identifier);
|
||||
parent.composition.push(childKeyString);
|
||||
|
||||
//If a location is not specified, set it.
|
||||
if (setLocation && child.location === undefined) {
|
||||
@@ -239,7 +235,7 @@ export default class DuplicateTask {
|
||||
let clone = JSON.parse(JSON.stringify(domainObject));
|
||||
let identifier = {
|
||||
key: uuid(),
|
||||
namespace: domainObject.identifier.namespace
|
||||
namespace: this.namespace // set to NEW parent's namespace
|
||||
};
|
||||
|
||||
if (clone.modified || clone.persisted || clone.location) {
|
||||
|
||||
@@ -143,9 +143,7 @@ export default {
|
||||
this.openmct.notifications.alert(message);
|
||||
}
|
||||
|
||||
const link = `${location.host}${location.pathname}${hash}`;
|
||||
const url = new URL(link);
|
||||
window.location.href = url.hash;
|
||||
window.location.hash = hash;
|
||||
},
|
||||
formatTime(unixTime, timeFormat) {
|
||||
return Moment.utc(unixTime).format(timeFormat);
|
||||
|
||||
@@ -117,8 +117,10 @@ define(
|
||||
* @returns {promise}
|
||||
*/
|
||||
ExportImageService.prototype.exportJPG = function (element, filename, className) {
|
||||
const processedFilename = replaceDotsWithUnderscores(filename);
|
||||
|
||||
return this.renderElement(element, "jpg", className).then(function (img) {
|
||||
saveAs(img, filename);
|
||||
saveAs(img, processedFilename);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -130,8 +132,10 @@ define(
|
||||
* @returns {promise}
|
||||
*/
|
||||
ExportImageService.prototype.exportPNG = function (element, filename, className) {
|
||||
const processedFilename = replaceDotsWithUnderscores(filename);
|
||||
|
||||
return this.renderElement(element, "png", className).then(function (img) {
|
||||
saveAs(img, filename);
|
||||
saveAs(img, processedFilename);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -146,6 +150,12 @@ define(
|
||||
return this.renderElement(element, "png", className);
|
||||
};
|
||||
|
||||
function replaceDotsWithUnderscores(filename) {
|
||||
const regex = /\./gi;
|
||||
|
||||
return filename.replace(regex, '_');
|
||||
}
|
||||
|
||||
/**
|
||||
* canvas.toBlob() not supported in IE < 10, Opera, and Safari. This polyfill
|
||||
* implements the method in browsers that would not otherwise support it.
|
||||
|
||||
@@ -39,10 +39,11 @@ define([], function () {
|
||||
const thisRequest = {
|
||||
pending: 0
|
||||
};
|
||||
currentRequest = thisRequest;
|
||||
$scope.currentRequest = thisRequest;
|
||||
const telemetryObjects = $scope.telemetryObjects = [];
|
||||
const thisTickWidthMap = {};
|
||||
|
||||
currentRequest = thisRequest;
|
||||
$scope.currentRequest = thisRequest;
|
||||
tickWidthMap = thisTickWidthMap;
|
||||
|
||||
if (unlisten) {
|
||||
@@ -52,14 +53,10 @@ define([], function () {
|
||||
|
||||
function addChild(child) {
|
||||
const id = openmct.objects.makeKeyString(child.identifier);
|
||||
const legacyObject = openmct.legacyObject(child);
|
||||
|
||||
thisTickWidthMap[id] = 0;
|
||||
thisRequest.pending += 1;
|
||||
objectService.getObjects([id])
|
||||
.then(function (objects) {
|
||||
thisRequest.pending -= 1;
|
||||
const childObj = objects[id];
|
||||
telemetryObjects.push(childObj);
|
||||
});
|
||||
telemetryObjects.push(legacyObject);
|
||||
}
|
||||
|
||||
function removeChild(childIdentifier) {
|
||||
@@ -84,6 +81,7 @@ define([], function () {
|
||||
}
|
||||
|
||||
thisRequest.pending += 1;
|
||||
|
||||
openmct.objects.get(domainObject.getId())
|
||||
.then(function (obj) {
|
||||
thisRequest.pending -= 1;
|
||||
|
||||
@@ -70,11 +70,8 @@ define(
|
||||
if (multiSelect) {
|
||||
this.handleMultiSelect(selectable);
|
||||
} else {
|
||||
this.setSelectionStyles(selectable);
|
||||
this.selected = [selectable];
|
||||
this.handleSingleSelect(selectable);
|
||||
}
|
||||
|
||||
this.emit('change', this.selected);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -87,6 +84,20 @@ define(
|
||||
this.addSelectionAttributes(selectable);
|
||||
this.selected.push(selectable);
|
||||
}
|
||||
|
||||
this.emit('change', this.selected);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.handleSingleSelect = function (selectable) {
|
||||
if (!_.isEqual([selectable], this.selected)) {
|
||||
this.setSelectionStyles(selectable);
|
||||
this.selected = [selectable];
|
||||
|
||||
this.emit('change', this.selected);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,6 +47,7 @@ mct-plot {
|
||||
.c-plot,
|
||||
.gl-plot {
|
||||
overflow: hidden;
|
||||
min-height: 100px;
|
||||
|
||||
.s-status-taking-snapshot & {
|
||||
.c-control-bar {
|
||||
@@ -79,7 +80,8 @@ mct-plot {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
&--stacked {
|
||||
|
||||
@@ -162,5 +162,5 @@
|
||||
// This element is the recipient for object styling; cannot be display: contents
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
display: contents;
|
||||
display: block;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user