Compare commits

...

34 Commits

Author SHA1 Message Date
Deep Tailor
3c03995096 Merge branch 'master' of https://github.com/nasa/openmct into regex-search-tables 2020-12-17 18:44:29 -08:00
Charles Hacskaylo
f5cbb37e5a Fix Inspector-based font size and style controls and menus (#3497)
- Moved CSS rule that was pushing the font style control to the right
side of the Inspector to `l-shell__toolbar` rule definition;
- Fixed `menus-to-left` CSS rule and applied to font size and style
menu controls;
- Added a new `menus-no-icon` style for menus that don't have icons,
applied to font size and style menu controls;
2020-12-17 18:43:58 -08:00
Deep Tailor
c8fe0fb756 fix dupe filter request 2020-12-17 11:19:03 -08:00
Deep Tailor
4c364d701b fix styles breaking on cell selection 2020-12-15 14:09:41 -08:00
Deep Tailor
ed91f6411e fix error when selecting cells in channel table 2020-12-15 12:57:30 -08:00
Jamie V
8d9079984a [Verve Imagery] Missing JS imagery class (#3603)
* replacing class that was accidentally deleted

* moved to correct div... aye aye ayeee
2020-12-14 15:42:45 -08:00
Deep Tailor
a397fb2cb5 Merge branch 'master' of https://github.com/nasa/openmct into regex-search-tables 2020-12-14 10:55:51 -08:00
Andrew Henry
41783d8939 Fixed minor issues in Code Guidelines (#3596)
There was a missing semi-colon in a code example (oops!) and incorrect capitalization of the `const` keyword (overzealous word processor).
2020-12-11 15:13:51 -08:00
Shefali Joshi
441ad58fe7 Prepare for sprint 1.5.0 (#3594) 2020-12-11 14:12:52 -08:00
Nikhil
06a6a3f773 [Notebook] Link to snapshot should not be a fully qualified url #3445 (#3576)
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2020-12-11 09:41:35 -08:00
Nikhil
52fab78625 [Testing] Fixes console errors while running npm test (#3593)
* ERROR: Error: [$injector:unpr] Unknown provider: exportImageServiceProvider <- exportImageService
* [Vue warn]: Injection "stylesManager" not found
* [Vue warn]: Error in mounted hook: "TypeError: identifier is undefined"
2020-12-10 19:44:51 -08:00
Jamie V
5eb6c15959 [Duplicate Action] Fix Display Layout unwanted duplication issue (#3591)
* WIP: refactoring legacy dulicate action

* WIP: debugging duplicate duplicates...

* WIP: fixed duplicate duplicates issue

* added unit tests

* removing old legacy copyaction and renaming duplicate action

* removing fdescribe

* trying to see if a done callback fixes testing issues

* fixed tests

* testing autoflow tests on server

* tweaked autoflow tests to stop failing

* minor updates for new 3 dot menu

* WIP bug fixing

* WIP debugging

* WIP more debuggin

* WIP using parent namespace for all duped objs

* WIP

* WIP
;
;

* cleaning up debugging code

* fixed linting issues

* fixed layout configuration items issue

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-12-10 09:09:25 -08:00
Deep Tailor
ce8c31cfa4 [Stacked plots] fixes scroll issue and removed redundant calls to backend (#3585)
* fixes scroll issue and removed redundant calls to backend

* change scroll to auto
2020-12-08 10:10:50 -08:00
Jamie V
d80c0eef8e [Actions] Duplicate Action bug fixes (#3578)
* WIP: refactoring legacy dulicate action

* WIP: debugging duplicate duplicates...

* WIP: fixed duplicate duplicates issue

* added unit tests

* removing old legacy copyaction and renaming duplicate action

* removing fdescribe

* trying to see if a done callback fixes testing issues

* fixed tests

* testing autoflow tests on server

* tweaked autoflow tests to stop failing

* minor updates for new 3 dot menu

* WIP bug fixing

* WIP debugging

* WIP more debuggin

* WIP using parent namespace for all duped objs

* WIP

* WIP
;
;

* cleaning up debugging code

* fixed linting issues

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-12-07 10:15:41 -08:00
Nikhil
55829dcf05 [Conductor] Remove 24 hour default limit in time conductor (#3512) 2020-12-04 10:34:19 -08:00
Deep Tailor
d78956327c Action API unit tests (#3527)
* add tests

* add tests for ActionCollection

* add tests for getVisibleActions and getStatusBarActions

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2020-12-02 12:53:34 -08:00
Deep Tailor
cb8e906f93 Merge branch 'master' of https://github.com/nasa/openmct into regex-search-tables 2020-12-01 14:42:35 -08:00
Deep Tailor
be746db774 fix lint errors 2020-11-25 09:15:31 -08:00
Deep Tailor
de9d972cf6 merge with master 2020-11-25 09:10:32 -08:00
Deep Tailor
5ac43ccf6a Merge branch 'master' into regex-search-tables 2020-07-20 11:16:10 -07:00
Deep Tailor
d29b4d3b83 Merge branch 'master' into regex-search-tables 2020-07-13 12:44:36 -07:00
Deep Tailor
541e96a20a Merge branch 'master' into regex-search-tables 2020-05-26 11:13:27 -07:00
Andrew Henry
1554823416 Merge branch 'master' into regex-search-tables 2020-05-11 14:37:15 -07:00
Deep Tailor
674a888d80 Merge branch 'master' into regex-search-tables 2020-05-08 10:52:00 -07:00
Deep Tailor
ec9dbabc46 fixes regression when search string is modified 2020-05-08 10:50:36 -07:00
Deep Tailor
c148160fa8 Merge branch 'master' into regex-search-tables 2020-05-05 12:14:00 -07:00
Deep Tailor
8b45dae1f1 use to trigger dom update when clearing input 2020-04-21 21:08:32 -07:00
charlesh88
1ec0274984 Styling for table search regex button
- Polish size of button;
2020-04-21 17:29:56 -07:00
Deep Tailor
9de1927834 remove unused variable 2020-04-21 16:18:56 -07:00
Deep Tailor
df3991cbd5 Merge branch 'master' into regex-search-tables 2020-04-21 16:16:11 -07:00
Deep Tailor
528a2aad8c change title and clear out input on toggle regex 2020-04-21 16:01:43 -07:00
charlesh88
c9a29d0854 Styling for table search regex button
- Allow user input to be retained when toggling regex on/off;
2020-04-20 17:24:14 -07:00
charlesh88
d28c4e8711 Styling for table search regex button
- Hover display/hide and styling for regex button;
2020-04-20 17:23:29 -07:00
Deep Tailor
ac04983c5b working prototype ready for styling 2020-04-18 14:05:17 -07:00
25 changed files with 490 additions and 89 deletions

View File

@@ -182,7 +182,7 @@ The following guidelines are provided for anyone contributing source code to the
1. Avoid the use of "magic" values.
eg.
```JavaScript
Const UNAUTHORIZED = 401
const UNAUTHORIZED = 401;
if (responseCode === UNAUTHORIZED)
```
is preferable to

View File

@@ -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",

View File

@@ -1,6 +1,6 @@
{
"name": "openmct",
"version": "1.4.1-SNAPSHOT",
"version": "1.5.0-SNAPSHOT",
"description": "The Open MCT core platform",
"dependencies": {},
"devDependencies": {

View 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);
});
});
});

View File

@@ -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];

View File

@@ -344,6 +344,11 @@ export default {
const layoutItem = selectionItem[0].context.layoutItem;
const isChildItem = selectionItem.length > 1;
if (!item && !layoutItem) {
// cases where selection is used for table cells
return;
}
if (!isChildItem) {
domainObject = item;
itemStyle = getApplicableStylesForItem(item);

View File

@@ -22,6 +22,7 @@
import { createOpenMct, resetApplicationState } from "utils/testing";
import ConditionPlugin from "./plugin";
import stylesManager from '@/ui/inspector/styles/StylesManager';
import StylesView from "./components/inspector/StylesView.vue";
import Vue from 'vue';
import {getApplicableStylesForItem} from "./utils/styleUtils";
@@ -402,7 +403,8 @@ describe('the plugin', function () {
component = new Vue({
provide: {
openmct: openmct,
selection: selection
selection: selection,
stylesManager
},
el: viewContainer,
components: {

View File

@@ -104,7 +104,7 @@ export function getConsolidatedStyleValues(multipleItemStyles) {
const properties = Object.keys(styleProps);
properties.forEach((property) => {
const values = aggregatedStyleValues[property];
if (values.length) {
if (values && values.length) {
if (values.every(value => value === values[0])) {
styleValues[property] = values[0];
} else {

View File

@@ -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"

View File

@@ -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();
@@ -78,8 +79,9 @@ export default class DuplicateTask {
*/
async buildDuplicationPlan() {
let domainObjectClone = await this.duplicateObject(this.domainObject);
if (domainObjectClone !== this.domainObject) {
domainObjectClone.location = this.getId(this.parent);
domainObjectClone.location = this.getKeyString(this.parent);
}
this.firstClone = domainObjectClone;
@@ -96,13 +98,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 +113,7 @@ export default class DuplicateTask {
}));
await clonesDone;
dialog.dismiss();
this.openmct.notifications.info(`Duplicated ${this.persisted} objects.`);
@@ -141,10 +145,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 +153,6 @@ export default class DuplicateTask {
composees = await composeesCollection.load();
}
// Recursively duplicate children
return this.duplicateComposees(clone, composees);
}
@@ -160,36 +160,6 @@ export default class DuplicateTask {
return originalObject;
}
/**
* Update identifiers in a cloned object model (or part of
* a cloned object model) to reflect new identifiers after
* duplicating.
* @private
*/
rewriteIdentifiers(obj, idMap) {
function lookupValue(value) {
return (typeof value === 'string' && idMap[value]) || value;
}
if (Array.isArray(obj)) {
obj.forEach((value, index) => {
obj[index] = lookupValue(value);
this.rewriteIdentifiers(obj[index], idMap);
});
} else if (obj && typeof obj === 'object') {
Object.keys(obj).forEach((key) => {
let value = obj[key];
obj[key] = lookupValue(value);
if (idMap[key]) {
delete obj[key];
obj[idMap[key]] = value;
}
this.rewriteIdentifiers(value, idMap);
});
}
}
/**
* Given an array of objects composed by a parent, clone them, then
* add them to the parent.
@@ -197,34 +167,67 @@ export default class DuplicateTask {
* @returns {*}
*/
async duplicateComposees(clonedParent, composees = []) {
let idMap = {};
let idMappings = [];
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);
if (clonedComposee) {
idMappings.push({
newId: clonedComposee.identifier,
oldId: nextComposee.identifier
});
this.composeChild(clonedComposee, clonedParent, clonedComposee !== nextComposee);
}
return;
}, Promise.resolve());
await allComposeesDuplicated;
this.rewriteIdentifiers(clonedParent, idMap);
clonedParent = this.rewriteIdentifiers(clonedParent, idMappings);
this.clones.push(clonedParent);
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);
/**
* Update identifiers in a cloned object model (or part of
* a cloned object model) to reflect new identifiers after
* duplicating.
* @private
*/
rewriteIdentifiers(clonedParent, childIdMappings) {
for (let { newId, oldId } of childIdMappings) {
let newIdKeyString = this.openmct.objects.makeKeyString(newId);
let oldIdKeyString = this.openmct.objects.makeKeyString(oldId);
// regex replace keystrings
clonedParent = JSON.stringify(clonedParent).replace(new RegExp(oldIdKeyString, 'g'), newIdKeyString);
// parse reviver to replace identifiers
clonedParent = JSON.parse(clonedParent, (key, value) => {
if (Object.prototype.hasOwnProperty.call(value, 'key')
&& Object.prototype.hasOwnProperty.call(value, 'namespace')
&& value.key === oldId.key
&& value.namespace === oldId.namespace) {
return newId;
} else {
return value;
}
});
}
return clonedParent;
}
composeChild(child, parent, setLocation) {
parent.composition.push(child.identifier);
//If a location is not specified, set it.
if (setLocation && child.location === undefined) {
let parentKeyString = this.getId(parent);
let parentKeyString = this.getKeyString(parent);
child.location = parentKeyString;
}
}
@@ -239,7 +242,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) {
@@ -260,7 +263,7 @@ export default class DuplicateTask {
return clone;
}
getId(domainObject) {
getKeyString(domainObject) {
return this.openmct.objects.makeKeyString(domainObject.identifier);
}

View File

@@ -36,7 +36,7 @@
<div class="c-imagery__main-image__bg"
:class="{'paused unnsynced': isPaused,'stale':false }"
>
<div class="c-imagery__main-image__image"
<div class="c-imagery__main-image__image js-imageryView-image"
:style="{
'background-image': imageUrl ? `url(${imageUrl})` : 'none',
'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`

View File

@@ -143,7 +143,9 @@ export default {
this.openmct.notifications.alert(message);
}
window.location.hash = hash;
const relativeHash = hash.slice(hash.indexOf('#'));
const url = new URL(relativeHash, `${location.protocol}//${location.host}${location.pathname}`);
window.location.hash = url.hash;
},
formatTime(unixTime, timeFormat) {
return Moment.utc(unixTime).format(timeFormat);

View File

@@ -7,15 +7,14 @@ export default class Snapshot {
constructor(openmct) {
this.openmct = openmct;
this.snapshotContainer = new SnapshotContainer(openmct);
this.exportImageService = openmct.$injector.get('exportImageService');
this.dialogService = openmct.$injector.get('dialogService');
this.capture = this.capture.bind(this);
this._saveSnapShot = this._saveSnapShot.bind(this);
}
capture(snapshotMeta, notebookType, domElement) {
this.exportImageService.exportPNGtoSRC(domElement, 's-status-taking-snapshot')
const exportImageService = this.openmct.$injector.get('exportImageService');
exportImageService.exportPNGtoSRC(domElement, 's-status-taking-snapshot')
.then(function (blob) {
const reader = new window.FileReader();
reader.readAsDataURL(blob);

View File

@@ -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;

View File

@@ -94,6 +94,7 @@ define([
initialize() {
if (this.domainObject.type === 'table') {
this.filterObserver = this.openmct.objects.observe(this.domainObject, 'configuration.filters', this.updateFilters);
this.filters = this.domainObject.configuration.filters;
this.loadComposition();
} else {
this.addTelemetryObject(this.domainObject);
@@ -138,7 +139,18 @@ define([
this.emit('object-added', telemetryObject);
}
updateFilters() {
updateFilters(updatedFilters) {
let deepCopiedFilters = JSON.parse(JSON.stringify(updatedFilters));
if (this.filters && !_.isEqual(this.filters, deepCopiedFilters)) {
this.filters = deepCopiedFilters;
this.clearAndResubscribe();
} else {
this.filters = deepCopiedFilters;
}
}
clearAndResubscribe() {
this.filteredRows.clear();
this.boundedRows.clear();
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);

View File

@@ -46,6 +46,7 @@ define(
filter = filter.trim().toLowerCase();
let rowsToFilter = this.getRowsToFilter(columnKey, filter);
if (filter.length === 0) {
delete this.columnFilters[columnKey];
} else {
@@ -56,6 +57,16 @@ define(
this.emit('filter');
}
setColumnRegexFilter(columnKey, filter) {
filter = filter.trim();
let rowsToFilter = this.masterCollection.getRows();
this.columnFilters[columnKey] = new RegExp(filter);
this.rows = rowsToFilter.filter(this.matchesFilters, this);
this.emit('filter');
}
/**
* @private
*/
@@ -71,6 +82,10 @@ define(
* @private
*/
isSubsetOfCurrentFilter(columnKey, filter) {
if (this.columnFilters[columnKey] instanceof RegExp) {
return false;
}
return this.columnFilters[columnKey]
&& filter.startsWith(this.columnFilters[columnKey])
// startsWith check will otherwise fail when filter cleared
@@ -97,7 +112,11 @@ define(
return false;
}
doesMatchFilters = formattedValue.toLowerCase().indexOf(this.columnFilters[key]) !== -1;
if (this.columnFilters[key] instanceof RegExp) {
doesMatchFilters = this.columnFilters[key].test(formattedValue);
} else {
doesMatchFilters = formattedValue.toLowerCase().indexOf(this.columnFilters[key]) !== -1;
}
});
return doesMatchFilters;

View File

@@ -188,7 +188,17 @@
class="c-table__search"
@input="filterChanged(key)"
@clear="clearFilter(key)"
/>
>
<button
class="c-search__use-regex"
:class="{ 'is-active': enableRegexSearch[key] }"
title="Click to enable regex: enter a string with slashes, like this: /regex_exp/"
@click="toggleRegex(key)"
>
/R/
</button>
</search>
</table-column-header>
</tr>
</thead>
@@ -361,6 +371,7 @@ export default {
paused: false,
markedRows: [],
isShowingMarkedRowsOnly: false,
enableRegexSearch: {},
hideHeaders: configuration.hideHeaders,
totalNumberOfRows: 0
};
@@ -618,7 +629,16 @@ export default {
this.headersHolderEl.scrollLeft = this.scrollable.scrollLeft;
},
filterChanged(columnKey) {
this.table.filteredRows.setColumnFilter(columnKey, this.filters[columnKey]);
if (this.enableRegexSearch[columnKey]) {
if (this.isCompleteRegex(this.filters[columnKey])) {
this.table.filteredRows.setColumnRegexFilter(columnKey, this.filters[columnKey].slice(1, -1));
} else {
return;
}
} else {
this.table.filteredRows.setColumnFilter(columnKey, this.filters[columnKey]);
}
this.setHeight();
},
clearFilter(columnKey) {
@@ -956,6 +976,18 @@ export default {
this.$nextTick().then(this.calculateColumnWidths);
},
toggleRegex(key) {
this.$set(this.filters, key, '');
if (this.enableRegexSearch[key] === undefined) {
this.$set(this.enableRegexSearch, key, true);
} else {
this.$set(this.enableRegexSearch, key, !this.enableRegexSearch[key]);
}
},
isCompleteRegex(string) {
return (string.length > 2 && string[0] === '/' && string[string.length - 1] === '/');
},
getViewContext() {
return {
type: 'telemetry-table',

View File

@@ -98,6 +98,10 @@ describe('the plugin', function () {
beforeEach((done) => {
planDomainObject = {
identifier: {
key: 'test-object',
namespace: ''
},
type: 'plan',
id: "test-object",
selectFile: {

View File

@@ -482,7 +482,7 @@ select {
transition: $transIn;
white-space: nowrap;
&:hover {
@include hover {
background: $colorMenuHovBg;
color: $colorMenuHovFg;
&:before {
@@ -500,6 +500,10 @@ select {
&:not([class*='icon']):before {
content: ''; // Enable :before so that menu items without an icon still indent properly
}
.menus-no-icon & {
&:before { display: none; }
}
}
}
@@ -731,7 +735,6 @@ select {
.c-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
> * {
// First level items

View File

@@ -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 {

View File

@@ -552,7 +552,8 @@
}
}
&[class*='--menus-left'] {
&[class*='--menus-left'],
&[class*='menus-to-left'] {
.c-menu {
left: auto; right: 0;
}

View File

@@ -1,6 +1,11 @@
@mixin visibleRegexButton {
opacity: 1;
padding: 1px 3px;
width: 24px;
}
.c-search {
@include wrappedInput();
padding-top: 2px;
padding-bottom: 2px;
@@ -9,11 +14,46 @@
content: $glyph-icon-magnify;
}
&__use-regex {
// Button
$c: $colorBodyFg;
background: rgba($c, 0.2);
border: 1px solid rgba($c, 0.3);
color: $c;
border-radius: $controlCr;
font-weight: bold;
letter-spacing: 1px;
font-size: 0.8em;
margin-left: $interiorMarginSm;
min-width: 0;
opacity: 0;
order: 2;
overflow: hidden;
padding: 1px 0;
transform-origin: left;
transition: $transOut;
width: 0;
&.is-active {
$c: $colorBtnActiveBg;
@include visibleRegexButton();
background: rgba($c, 0.3);
border-color: $c;
color: $c;
}
}
&__clear-input {
display: none;
order: 99;
padding: 1px 0;
}
&.is-active {
.c-search__use-regex {
margin-left: 0;
}
.c-search__clear-input {
display: block;
}
@@ -21,6 +61,15 @@
input[type='text'],
input[type='search'] {
margin-left: $interiorMargin;
order: 3;
text-align: left;
}
&:hover {
.c-search__use-regex {
@include visibleRegexButton();
transition: $transIn;
}
}
}

View File

@@ -15,6 +15,7 @@
class="c-search__clear-input icon-x-in-circle"
@click="clearInput"
></a>
<slot></slot>
</div>
</template>

View File

@@ -3,11 +3,13 @@
<toolbar-select-menu
:options="fontSizeMenuOptions"
@change="setFontSize"
class="menus-to-left menus-no-icon"
/>
<div class="c-toolbar__separator"></div>
<toolbar-select-menu
:options="fontMenuOptions"
@change="setFont"
class="menus-to-left menus-no-icon"
/>
</div>
</template>

View File

@@ -279,10 +279,12 @@
}
&__toolbar {
// Toolbar in the main shell, used by Display Layouts
$p: $interiorMargin;
background: $editUIBaseColor;
border-radius: $basicCr;
height: $p + 24px; // Need to standardize the height
justify-content: space-between;
padding: $p;
}
}