Compare commits
9 Commits
plotly-tes
...
context-me
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8493b481dd | ||
|
|
28723b59b7 | ||
|
|
9fa7de0b77 | ||
|
|
54bfc84ada | ||
|
|
6137700c82 | ||
|
|
91a1b3f31d | ||
|
|
357b25a76b | ||
|
|
bab53ad9bd | ||
|
|
d0d4579f13 |
@@ -136,7 +136,7 @@ this repository. This is verified by the command line build.
|
||||
|
||||
#### Code Guidelines
|
||||
|
||||
JavaScript sources in Open MCT should:
|
||||
The following guidelines are provided for anyone contributing source code to the Open MCT project:
|
||||
|
||||
1. Write clean code. Here’s a good summary - https://github.com/ryanmcdermott/clean-code-javascript.
|
||||
1. Include JSDoc for any exposed API (e.g. public methods, classes).
|
||||
|
||||
@@ -240,7 +240,7 @@ define([
|
||||
|
||||
this.overlays = new OverlayAPI.default();
|
||||
|
||||
this.contextMenu = new api.ContextMenuRegistry();
|
||||
this.menus = new api.MenuAPI(this);
|
||||
|
||||
this.router = new ApplicationRouter();
|
||||
|
||||
|
||||
@@ -33,5 +33,5 @@ export default function LegacyActionAdapter(openmct, legacyActions) {
|
||||
|
||||
legacyActions.filter(contextualCategoryOnly)
|
||||
.map(LegacyAction => new LegacyContextMenuAction(openmct, LegacyAction))
|
||||
.forEach(openmct.contextMenu.registerAction);
|
||||
.forEach(openmct.menus.registerObjectAction);
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ define([
|
||||
'./telemetry/TelemetryAPI',
|
||||
'./indicators/IndicatorAPI',
|
||||
'./notifications/NotificationAPI',
|
||||
'./contextMenu/ContextMenuAPI',
|
||||
'./Editor'
|
||||
'./Editor',
|
||||
'./menu/MenuAPI'
|
||||
|
||||
], function (
|
||||
TimeAPI,
|
||||
@@ -39,8 +39,8 @@ define([
|
||||
TelemetryAPI,
|
||||
IndicatorAPI,
|
||||
NotificationAPI,
|
||||
ContextMenuAPI,
|
||||
EditorAPI
|
||||
EditorAPI,
|
||||
MenuAPI
|
||||
) {
|
||||
return {
|
||||
TimeAPI: TimeAPI,
|
||||
@@ -51,6 +51,6 @@ define([
|
||||
IndicatorAPI: IndicatorAPI,
|
||||
NotificationAPI: NotificationAPI.default,
|
||||
EditorAPI: EditorAPI,
|
||||
ContextMenuRegistry: ContextMenuAPI.default
|
||||
MenuAPI: MenuAPI.default
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
@@ -20,29 +20,32 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import ContextMenuComponent from './ContextMenu.vue';
|
||||
import Vue from 'vue';
|
||||
import Menu from './menu.js';
|
||||
|
||||
/**
|
||||
* The ContextMenuAPI allows the addition of new context menu actions, and for the context menu to be launched from
|
||||
* The MenuAPI allows the addition of new context menu actions, and for the context menu to be launched from
|
||||
* custom HTML elements.
|
||||
* @interface ContextMenuAPI
|
||||
* @interface MenuAPI
|
||||
* @memberof module:openmct
|
||||
*/
|
||||
class ContextMenuAPI {
|
||||
constructor() {
|
||||
this._allActions = [];
|
||||
this._activeContextMenu = undefined;
|
||||
|
||||
this._hideActiveContextMenu = this._hideActiveContextMenu.bind(this);
|
||||
this.registerAction = this.registerAction.bind(this);
|
||||
class MenuAPI {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
this._allObjectActions = [];
|
||||
|
||||
this.showMenu = this.showMenu.bind(this);
|
||||
this.registerObjectAction = this.registerObjectAction.bind(this);
|
||||
this._clearMenuComponent = this._clearMenuComponent.bind(this);
|
||||
this._applicableObjectActions = this._applicableObjectActions.bind(this);
|
||||
this._showObjectMenu = this._showObjectMenu.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an item to be added to context menus. Allows specification of text, appearance, and behavior when
|
||||
* selected. Applicabilioty can be restricted by specification of an `appliesTo` function.
|
||||
*
|
||||
* @interface ContextMenuAction
|
||||
* @interface ObjectAction
|
||||
* @memberof module:openmct
|
||||
* @property {string} name the human-readable name of this view
|
||||
* @property {string} description a longer-form description (typically
|
||||
@@ -68,17 +71,32 @@ class ContextMenuAPI {
|
||||
/**
|
||||
* @param {ContextMenuAction} actionDefinition
|
||||
*/
|
||||
registerAction(actionDefinition) {
|
||||
this._allActions.push(actionDefinition);
|
||||
registerObjectAction(actionDefinition) {
|
||||
this._allObjectActions.push(actionDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_showContextMenuForObjectPath(objectPath, x, y, actionsToBeIncluded) {
|
||||
showMenu(x, y, actions) {
|
||||
if (this.menuComponent) {
|
||||
this.menuComponent.dismiss();
|
||||
}
|
||||
|
||||
let applicableActions = this._allActions.filter((action) => {
|
||||
let options = {
|
||||
x,
|
||||
y,
|
||||
actions
|
||||
}
|
||||
|
||||
this.menuComponent = new Menu(options);
|
||||
this.menuComponent.on('destroy', this._clearMenuComponent);
|
||||
}
|
||||
|
||||
_clearMenuComponent() {
|
||||
this.menuComponent = undefined;
|
||||
delete this.menuComponent;
|
||||
}
|
||||
|
||||
_applicableObjectActions(objectPath, actionsToBeIncluded) {
|
||||
let applicableActions = this._allObjectActions.filter((action) => {
|
||||
if (actionsToBeIncluded) {
|
||||
if (action.appliesTo === undefined && actionsToBeIncluded.includes(action.key)) {
|
||||
return true;
|
||||
@@ -92,66 +110,19 @@ class ContextMenuAPI {
|
||||
}
|
||||
});
|
||||
|
||||
if (this._activeContextMenu) {
|
||||
this._hideActiveContextMenu();
|
||||
}
|
||||
|
||||
this._activeContextMenu = this._createContextMenuForObject(objectPath, applicableActions);
|
||||
this._activeContextMenu.$mount();
|
||||
document.body.appendChild(this._activeContextMenu.$el);
|
||||
|
||||
let position = this._calculatePopupPosition(x, y, this._activeContextMenu.$el);
|
||||
this._activeContextMenu.$el.style.left = `${position.x}px`;
|
||||
this._activeContextMenu.$el.style.top = `${position.y}px`;
|
||||
|
||||
document.addEventListener('click', this._hideActiveContextMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_calculatePopupPosition(eventPosX, eventPosY, menuElement) {
|
||||
let menuDimensions = menuElement.getBoundingClientRect();
|
||||
let overflowX = (eventPosX + menuDimensions.width) - document.body.clientWidth;
|
||||
let overflowY = (eventPosY + menuDimensions.height) - document.body.clientHeight;
|
||||
|
||||
if (overflowX > 0) {
|
||||
eventPosX = eventPosX - overflowX;
|
||||
}
|
||||
|
||||
if (overflowY > 0) {
|
||||
eventPosY = eventPosY - overflowY;
|
||||
}
|
||||
|
||||
return {
|
||||
x: eventPosX,
|
||||
y: eventPosY
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_hideActiveContextMenu() {
|
||||
document.removeEventListener('click', this._hideActiveContextMenu);
|
||||
document.body.removeChild(this._activeContextMenu.$el);
|
||||
this._activeContextMenu.$destroy();
|
||||
this._activeContextMenu = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_createContextMenuForObject(objectPath, actions) {
|
||||
return new Vue({
|
||||
components: {
|
||||
ContextMenu: ContextMenuComponent
|
||||
},
|
||||
provide: {
|
||||
actions: actions,
|
||||
objectPath: objectPath
|
||||
},
|
||||
template: '<ContextMenu></ContextMenu>'
|
||||
applicableActions.forEach(action => {
|
||||
action.callBack = () => {
|
||||
return action.invoke(objectPath);
|
||||
};
|
||||
});
|
||||
|
||||
return applicableActions;
|
||||
}
|
||||
|
||||
_showObjectMenu(objectPath, x, y, actionsToBeIncluded) {
|
||||
let applicableActions = this._applicableObjectActions(objectPath, actionsToBeIncluded);
|
||||
|
||||
this.showMenu(x, y, applicableActions);
|
||||
}
|
||||
}
|
||||
export default ContextMenuAPI;
|
||||
export default MenuAPI;
|
||||
@@ -6,7 +6,7 @@
|
||||
:key="action.name"
|
||||
:class="action.cssClass"
|
||||
:title="action.description"
|
||||
@click="action.invoke(objectPath)"
|
||||
@click="action.callBack"
|
||||
>
|
||||
{{ action.name }}
|
||||
</li>
|
||||
@@ -19,6 +19,6 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['actions', 'objectPath']
|
||||
inject: ['actions']
|
||||
}
|
||||
</script>
|
||||
94
src/api/menu/menu.js
Normal file
94
src/api/menu/menu.js
Normal file
@@ -0,0 +1,94 @@
|
||||
/*****************************************************************************
|
||||
* 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 EventEmitter from 'EventEmitter';
|
||||
import MenuComponent from './components/Menu.vue';
|
||||
import Vue from 'vue';
|
||||
|
||||
class Menu extends EventEmitter {
|
||||
constructor(options) {
|
||||
super();
|
||||
|
||||
this.options = options;
|
||||
|
||||
this.component = new Vue({
|
||||
provide: {
|
||||
actions: options.actions
|
||||
},
|
||||
components: {
|
||||
MenuComponent
|
||||
},
|
||||
template: '<menu-component />'
|
||||
});
|
||||
|
||||
if (options.onDestroy) {
|
||||
this.once('destroy', options.onDestroy);
|
||||
}
|
||||
|
||||
this.dismiss = this.dismiss.bind(this);
|
||||
this.show = this.show.bind(this);
|
||||
|
||||
this.show();
|
||||
}
|
||||
|
||||
dismiss() {
|
||||
this.emit('destroy');
|
||||
document.body.removeChild(this.component.$el);
|
||||
document.removeEventListener('click', this.dismiss);
|
||||
this.component.$destroy();
|
||||
}
|
||||
|
||||
show() {
|
||||
this.component.$mount();
|
||||
document.body.appendChild(this.component.$el);
|
||||
|
||||
let position = this._calculatePopupPosition(this.options.x, this.options.y, this.component.$el);
|
||||
|
||||
this.component.$el.style.left = `${position.x}px`;
|
||||
this.component.$el.style.top = `${position.y}px`;
|
||||
|
||||
document.addEventListener('click', this.dismiss);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_calculatePopupPosition(eventPosX, eventPosY, menuElement) {
|
||||
let menuDimensions = menuElement.getBoundingClientRect();
|
||||
let overflowX = (eventPosX + menuDimensions.width) - document.body.clientWidth;
|
||||
let overflowY = (eventPosY + menuDimensions.height) - document.body.clientHeight;
|
||||
|
||||
if (overflowX > 0) {
|
||||
eventPosX = eventPosX - overflowX;
|
||||
}
|
||||
|
||||
if (overflowY > 0) {
|
||||
eventPosY = eventPosY - overflowY;
|
||||
}
|
||||
|
||||
return {
|
||||
x: eventPosX,
|
||||
y: eventPosY
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Menu;
|
||||
@@ -22,6 +22,7 @@ class OverlayAPI {
|
||||
this.dismissLastOverlay();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,6 +129,7 @@ class OverlayAPI {
|
||||
|
||||
return progressDialog;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default OverlayAPI;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
@@ -24,10 +24,8 @@
|
||||
<template>
|
||||
<tr @contextmenu.prevent="showContextMenu">
|
||||
<td>{{ name }}</td>
|
||||
<td>{{ timestamp }}</td>
|
||||
<td :class="valueClass">
|
||||
{{ value }}
|
||||
</td>
|
||||
<td>{{ formattedTimestamp }}</td>
|
||||
<td :class="valueClass">{{ value }}</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
@@ -52,16 +50,22 @@ export default {
|
||||
|
||||
return {
|
||||
name: this.domainObject.name,
|
||||
timestamp: '---',
|
||||
timestamp: undefined,
|
||||
value: '---',
|
||||
valueClass: '',
|
||||
currentObjectPath
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formattedTimestamp() {
|
||||
return this.timestamp !== undefined ? this.formats[this.timestampKey].format(this.timestamp) : '---';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
this.bounds = this.openmct.time.bounds();
|
||||
|
||||
this.limitEvaluator = this.openmct
|
||||
.telemetry
|
||||
@@ -76,6 +80,7 @@ export default {
|
||||
);
|
||||
|
||||
this.openmct.time.on('timeSystem', this.updateTimeSystem);
|
||||
this.openmct.time.on('bounds', this.updateBounds);
|
||||
|
||||
this.timestampKey = this.openmct.time.timeSystem().key;
|
||||
|
||||
@@ -89,46 +94,66 @@ export default {
|
||||
.telemetry
|
||||
.subscribe(this.domainObject, this.updateValues);
|
||||
|
||||
this.openmct
|
||||
.telemetry
|
||||
.request(this.domainObject, {strategy: 'latest'})
|
||||
.then((array) => this.updateValues(array[array.length - 1]));
|
||||
this.requestHistory();
|
||||
},
|
||||
destroyed() {
|
||||
this.stopWatchingMutation();
|
||||
this.unsubscribe();
|
||||
this.openmct.off('timeSystem', this.updateTimeSystem);
|
||||
this.openmct.time.off('timeSystem', this.updateTimeSystem);
|
||||
this.openmct.time.off('bounds', this.updateBounds);
|
||||
},
|
||||
methods: {
|
||||
updateValues(datum) {
|
||||
this.timestamp = this.formats[this.timestampKey].format(datum);
|
||||
this.value = this.formats[this.valueKey].format(datum);
|
||||
let newTimestamp = this.formats[this.timestampKey].parse(datum),
|
||||
limit;
|
||||
|
||||
var limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
|
||||
|
||||
if (limit) {
|
||||
this.valueClass = limit.cssClass;
|
||||
} else {
|
||||
this.valueClass = '';
|
||||
if(this.shouldUpdate(newTimestamp)) {
|
||||
this.timestamp = this.formats[this.timestampKey].parse(datum);
|
||||
this.value = this.formats[this.valueKey].format(datum);
|
||||
limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
|
||||
if (limit) {
|
||||
this.valueClass = limit.cssClass;
|
||||
} else {
|
||||
this.valueClass = '';
|
||||
}
|
||||
}
|
||||
},
|
||||
shouldUpdate(newTimestamp) {
|
||||
return (this.timestamp === undefined) ||
|
||||
(this.inBounds(newTimestamp) &&
|
||||
newTimestamp > this.timestamp);
|
||||
},
|
||||
requestHistory() {
|
||||
this.openmct
|
||||
.telemetry
|
||||
.request(this.domainObject, {
|
||||
start: this.bounds.start,
|
||||
end: this.bounds.end,
|
||||
size: 1,
|
||||
strategy: 'latest'
|
||||
})
|
||||
.then((array) => this.updateValues(array[array.length - 1]));
|
||||
},
|
||||
updateName(name) {
|
||||
this.name = name;
|
||||
},
|
||||
updateBounds(bounds, isTick) {
|
||||
this.bounds = bounds;
|
||||
if(!isTick) {
|
||||
this.requestHistory();
|
||||
}
|
||||
},
|
||||
inBounds(timestamp) {
|
||||
return timestamp >= this.bounds.start && timestamp <= this.bounds.end;
|
||||
},
|
||||
updateTimeSystem(timeSystem) {
|
||||
this.value = '---';
|
||||
this.timestamp = '---';
|
||||
this.valueClass = '';
|
||||
this.timestampKey = timeSystem.key;
|
||||
|
||||
this.openmct
|
||||
.telemetry
|
||||
.request(this.domainObject, {strategy: 'latest'})
|
||||
.then((array) => this.updateValues(array[array.length - 1]));
|
||||
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
this.openmct.menus._showObjectMenu(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ define([
|
||||
|
||||
openmct.indicators.add(indicator);
|
||||
|
||||
openmct.contextMenu.registerAction(new ClearDataAction.default(openmct, appliesToObjects));
|
||||
openmct.menus.registerObjectAction(new ClearDataAction.default(openmct, appliesToObjects));
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
@@ -457,7 +457,7 @@ export default {
|
||||
domainObjects.forEach(domainObject => {
|
||||
let objectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
|
||||
this.removeConditionalStyles(objectStyles);
|
||||
if (Object.keys(objectStyles).length <= 0) {
|
||||
if (objectStyles && Object.keys(objectStyles).length <= 0) {
|
||||
objectStyles = undefined;
|
||||
}
|
||||
this.persist(domainObject, objectStyles);
|
||||
@@ -467,14 +467,14 @@ export default {
|
||||
this.items.forEach((item) => {
|
||||
const itemId = item.id;
|
||||
this.removeConditionalStyles(domainObjectStyles, itemId);
|
||||
if (Object.keys(domainObjectStyles[itemId]).length <= 0) {
|
||||
if (domainObjectStyles[itemId] && Object.keys(domainObjectStyles[itemId]).length <= 0) {
|
||||
delete domainObjectStyles[itemId];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.removeConditionalStyles(domainObjectStyles);
|
||||
}
|
||||
if (Object.keys(domainObjectStyles).length <= 0) {
|
||||
if (domainObjectStyles && Object.keys(domainObjectStyles).length <= 0) {
|
||||
domainObjectStyles = undefined;
|
||||
}
|
||||
this.persist(this.domainObject, domainObjectStyles);
|
||||
@@ -553,9 +553,9 @@ export default {
|
||||
let itemConditionalStyle = { styles: []};
|
||||
if (!this.conditionSetDomainObject) {
|
||||
if (domainObjectStyles[item.id] && domainObjectStyles[item.id].staticStyle) {
|
||||
itemStaticStyle = domainObjectStyles[item.id].staticStyle.style;
|
||||
itemStaticStyle = Object.assign({}, domainObjectStyles[item.id].staticStyle.style);
|
||||
}
|
||||
if (item.applicableStyles[property]) {
|
||||
if (item.applicableStyles[property] !== undefined) {
|
||||
itemStaticStyle[property] = this.staticStyle.style[property];
|
||||
}
|
||||
if (Object.keys(itemStaticStyle).length <= 0) {
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
|
||||
import { createOpenMct } from "testUtils";
|
||||
import ConditionPlugin from "./plugin";
|
||||
import StylesView from "./components/inspector/StylesView.vue";
|
||||
import Vue from 'vue';
|
||||
import {getApplicableStylesForItem} from "./utils/styleUtils";
|
||||
|
||||
describe('the plugin', function () {
|
||||
let conditionSetDefinition;
|
||||
@@ -30,7 +33,7 @@ describe('the plugin', function () {
|
||||
let child;
|
||||
let openmct;
|
||||
|
||||
beforeAll((done) => {
|
||||
beforeEach((done) => {
|
||||
openmct = createOpenMct();
|
||||
openmct.install(new ConditionPlugin());
|
||||
|
||||
@@ -90,4 +93,259 @@ describe('the plugin', function () {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('the condition set usage for multiple display layout items', () => {
|
||||
let displayLayoutItem;
|
||||
let lineLayoutItem;
|
||||
let boxLayoutItem;
|
||||
let selection;
|
||||
let component;
|
||||
let styleViewComponentObject;
|
||||
const conditionSetDomainObject = {
|
||||
"configuration":{
|
||||
"conditionTestData":[
|
||||
{
|
||||
"telemetry":"",
|
||||
"metadata":"",
|
||||
"input":""
|
||||
}
|
||||
],
|
||||
"conditionCollection":[
|
||||
{
|
||||
"id":"39584410-cbf9-499e-96dc-76f27e69885d",
|
||||
"configuration":{
|
||||
"name":"Unnamed Condition",
|
||||
"output":"Sine > 0",
|
||||
"trigger":"all",
|
||||
"criteria":[
|
||||
{
|
||||
"id":"85fbb2f7-7595-42bd-9767-a932266c5225",
|
||||
"telemetry":{
|
||||
"namespace":"",
|
||||
"key":"be0ba97f-b510-4f40-a18d-4ff121d5ea1a"
|
||||
},
|
||||
"operation":"greaterThan",
|
||||
"input":[
|
||||
"0"
|
||||
],
|
||||
"metadata":"sin"
|
||||
},
|
||||
{
|
||||
"id":"35400132-63b0-425c-ac30-8197df7d5862",
|
||||
"telemetry":"any",
|
||||
"operation":"enumValueIs",
|
||||
"input":[
|
||||
"0"
|
||||
],
|
||||
"metadata":"state"
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary":"Match if all criteria are met: Sine Wave Generator Sine > 0 and any telemetry State is OFF "
|
||||
},
|
||||
{
|
||||
"isDefault":true,
|
||||
"id":"2532d90a-e0d6-4935-b546-3123522da2de",
|
||||
"configuration":{
|
||||
"name":"Default",
|
||||
"output":"Default",
|
||||
"trigger":"all",
|
||||
"criteria":[
|
||||
]
|
||||
},
|
||||
"summary":""
|
||||
}
|
||||
]
|
||||
},
|
||||
"composition":[
|
||||
{
|
||||
"namespace":"",
|
||||
"key":"be0ba97f-b510-4f40-a18d-4ff121d5ea1a"
|
||||
},
|
||||
{
|
||||
"namespace":"",
|
||||
"key":"077ffa67-e78f-4e99-80e0-522ac33a3888"
|
||||
}
|
||||
],
|
||||
"telemetry":{
|
||||
},
|
||||
"name":"Condition Set",
|
||||
"type":"conditionSet",
|
||||
"identifier":{
|
||||
"namespace":"",
|
||||
"key":"863012c1-f6ca-4ab0-aed7-fd43d5e4cd12"
|
||||
}
|
||||
|
||||
};
|
||||
const staticStyle = {
|
||||
"style":{
|
||||
"backgroundColor":"#717171",
|
||||
"border":"1px solid #00ffff"
|
||||
}
|
||||
};
|
||||
const conditionalStyle = {
|
||||
"conditionId":"39584410-cbf9-499e-96dc-76f27e69885d",
|
||||
"style":{
|
||||
"isStyleInvisible":"",
|
||||
"backgroundColor":"#717171",
|
||||
"border":"1px solid #ffff00"
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
displayLayoutItem = {
|
||||
"composition":[
|
||||
],
|
||||
"configuration":{
|
||||
"items":[
|
||||
{
|
||||
"fill":"#717171",
|
||||
"stroke":"",
|
||||
"x":1,
|
||||
"y":1,
|
||||
"width":10,
|
||||
"height":5,
|
||||
"type":"box-view",
|
||||
"id":"89b88746-d325-487b-aec4-11b79afff9e8"
|
||||
},
|
||||
{
|
||||
"x":18,
|
||||
"y":9,
|
||||
"x2":23,
|
||||
"y2":4,
|
||||
"stroke":"#717171",
|
||||
"type":"line-view",
|
||||
"id":"57d49a28-7863-43bd-9593-6570758916f0"
|
||||
}
|
||||
],
|
||||
"layoutGrid":[
|
||||
10,
|
||||
10
|
||||
]
|
||||
},
|
||||
"name":"Display Layout",
|
||||
"type":"layout",
|
||||
"identifier":{
|
||||
"namespace":"",
|
||||
"key":"c5e636c1-6771-4c9c-b933-8665cab189b3"
|
||||
}
|
||||
};
|
||||
lineLayoutItem = {
|
||||
"x":18,
|
||||
"y":9,
|
||||
"x2":23,
|
||||
"y2":4,
|
||||
"stroke":"#717171",
|
||||
"type":"line-view",
|
||||
"id":"57d49a28-7863-43bd-9593-6570758916f0"
|
||||
};
|
||||
boxLayoutItem = {
|
||||
"fill": "#717171",
|
||||
"stroke": "",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"width": 10,
|
||||
"height": 5,
|
||||
"type": "box-view",
|
||||
"id": "89b88746-d325-487b-aec4-11b79afff9e8"
|
||||
};
|
||||
selection = [
|
||||
[{
|
||||
context: {
|
||||
"layoutItem": lineLayoutItem,
|
||||
"index":1
|
||||
}
|
||||
},
|
||||
{
|
||||
context: {
|
||||
"item": displayLayoutItem,
|
||||
"supportsMultiSelect":true
|
||||
}
|
||||
}],
|
||||
[{
|
||||
context: {
|
||||
"layoutItem": boxLayoutItem,
|
||||
"index": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
context: {
|
||||
item: displayLayoutItem,
|
||||
"supportsMultiSelect":true
|
||||
}
|
||||
}]
|
||||
];
|
||||
let viewContainer = document.createElement('div');
|
||||
child.append(viewContainer);
|
||||
component = new Vue({
|
||||
provide: {
|
||||
openmct: openmct,
|
||||
selection: selection
|
||||
},
|
||||
el: viewContainer,
|
||||
components: {
|
||||
StylesView
|
||||
},
|
||||
template: '<styles-view/>'
|
||||
});
|
||||
return Vue.nextTick().then(() => {
|
||||
styleViewComponentObject = component.$root.$children[0];
|
||||
styleViewComponentObject.setEditState(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('initializes the items in the view', () => {
|
||||
expect(styleViewComponentObject.items.length).toBe(2);
|
||||
});
|
||||
|
||||
it('initializes conditional styles', () => {
|
||||
styleViewComponentObject.conditionSetDomainObject = conditionSetDomainObject;
|
||||
styleViewComponentObject.conditionalStyles = [];
|
||||
styleViewComponentObject.initializeConditionalStyles();
|
||||
expect(styleViewComponentObject.conditionalStyles.length).toBe(2);
|
||||
});
|
||||
|
||||
it('updates applicable conditional styles', () => {
|
||||
styleViewComponentObject.conditionSetDomainObject = conditionSetDomainObject;
|
||||
styleViewComponentObject.conditionalStyles = [];
|
||||
styleViewComponentObject.initializeConditionalStyles();
|
||||
expect(styleViewComponentObject.conditionalStyles.length).toBe(2);
|
||||
styleViewComponentObject.updateConditionalStyle(conditionalStyle, 'border');
|
||||
return Vue.nextTick().then(() => {
|
||||
expect(styleViewComponentObject.domainObject.configuration.objectStyles).toBeDefined();
|
||||
[boxLayoutItem, lineLayoutItem].forEach((item) => {
|
||||
const itemStyles = styleViewComponentObject.domainObject.configuration.objectStyles[item.id].styles;
|
||||
expect(itemStyles.length).toBe(2);
|
||||
const foundStyle = itemStyles.find((style) => {
|
||||
return style.conditionId === conditionalStyle.conditionId;
|
||||
});
|
||||
expect(foundStyle).toBeDefined();
|
||||
const applicableStyles = getApplicableStylesForItem(styleViewComponentObject.domainObject, item);
|
||||
const applicableStylesKeys = Object.keys(applicableStyles).concat(['isStyleInvisible']);
|
||||
Object.keys(foundStyle.style).forEach((key) => {
|
||||
expect(applicableStylesKeys.indexOf(key)).toBeGreaterThan(-1);
|
||||
expect(foundStyle.style[key]).toEqual(conditionalStyle.style[key]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('updates applicable static styles', () => {
|
||||
styleViewComponentObject.updateStaticStyle(staticStyle, 'border');
|
||||
return Vue.nextTick().then(() => {
|
||||
expect(styleViewComponentObject.domainObject.configuration.objectStyles).toBeDefined();
|
||||
[boxLayoutItem, lineLayoutItem].forEach((item) => {
|
||||
const itemStyle = styleViewComponentObject.domainObject.configuration.objectStyles[item.id].staticStyle;
|
||||
expect(itemStyle).toBeDefined();
|
||||
const applicableStyles = getApplicableStylesForItem(styleViewComponentObject.domainObject, item);
|
||||
const applicableStylesKeys = Object.keys(applicableStyles).concat(['isStyleInvisible']);
|
||||
Object.keys(itemStyle.style).forEach((key) => {
|
||||
expect(applicableStylesKeys.indexOf(key)).toBeGreaterThan(-1);
|
||||
expect(itemStyle.style[key]).toEqual(staticStyle.style[key]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -248,7 +248,7 @@ export default {
|
||||
this.$emit('formatChanged', this.item, format);
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
this.openmct.menus._showObjectMenu(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,6 @@ import GoToOriginalAction from './goToOriginalAction';
|
||||
|
||||
export default function () {
|
||||
return function (openmct) {
|
||||
openmct.contextMenu.registerAction(new GoToOriginalAction(openmct));
|
||||
openmct.menus.registerObjectAction(new GoToOriginalAction(openmct));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,6 +23,6 @@ import RemoveAction from "./RemoveAction";
|
||||
|
||||
export default function () {
|
||||
return function (openmct) {
|
||||
openmct.contextMenu.registerAction(new RemoveAction(openmct));
|
||||
openmct.menus.registerObjectAction(new RemoveAction(openmct));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="c-inspect-properties">
|
||||
<template v-if="isEditing">
|
||||
<div class="c-inspect-properties__header">
|
||||
Table Column Size
|
||||
Table Layout
|
||||
</div>
|
||||
<ul class="c-inspect-properties__section">
|
||||
<li class="c-inspect-properties__row">
|
||||
@@ -21,6 +21,22 @@
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
<li class="c-inspect-properties__row">
|
||||
<div
|
||||
class="c-inspect-properties__label"
|
||||
title="Show or hide headers"
|
||||
>
|
||||
<label for="header-visibility">Hide Header</label>
|
||||
</div>
|
||||
<div class="c-inspect-properties__value">
|
||||
<input
|
||||
id="header-visibility"
|
||||
type="checkbox"
|
||||
:checked="configuration.hideHeaders === true"
|
||||
@change="toggleHeaderVisibility"
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="c-inspect-properties__header">
|
||||
Table Column Visibility
|
||||
@@ -120,6 +136,12 @@ export default {
|
||||
let column = new TelemetryTableColumn(this.openmct, metadatum);
|
||||
this.tableConfiguration.addSingleColumnForObject(telemetryObject, column);
|
||||
});
|
||||
},
|
||||
toggleHeaderVisibility() {
|
||||
let hideHeaders = this.configuration.hideHeaders;
|
||||
|
||||
this.configuration.hideHeaders = !hideHeaders;
|
||||
this.tableConfiguration.updateConfiguration(this.configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ export default {
|
||||
let contextualObjectPath = this.objectPath.slice();
|
||||
contextualObjectPath.unshift(domainObject);
|
||||
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(contextualObjectPath, event.x, event.y, this.row.getContextMenuActions());
|
||||
this.openmct.menus._showObjectMenu(contextualObjectPath, event.x, event.y, this.row.getContextMenuActions());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@
|
||||
></div>
|
||||
<!-- Headers table -->
|
||||
<div
|
||||
v-show="!hideHeaders"
|
||||
ref="headersTable"
|
||||
class="c-telemetry-table__headers-w js-table__headers-w"
|
||||
:style="{ 'max-width': widthWithScroll}"
|
||||
@@ -336,7 +337,8 @@ export default {
|
||||
markCounter: 0,
|
||||
paused: false,
|
||||
markedRows: [],
|
||||
isShowingMarkedRowsOnly: false
|
||||
isShowingMarkedRowsOnly: false,
|
||||
hideHeaders: configuration.hideHeaders
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -615,6 +617,7 @@ export default {
|
||||
},
|
||||
updateConfiguration(configuration) {
|
||||
this.isAutosizeEnabled = configuration.autosize;
|
||||
this.hideHeaders = configuration.hideHeaders;
|
||||
|
||||
this.updateHeaders();
|
||||
this.$nextTick().then(this.calculateColumnWidths);
|
||||
|
||||
@@ -267,7 +267,7 @@ export default {
|
||||
});
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.openmct.router.path, event.clientX, event.clientY);
|
||||
this.openmct.menus._showObjectMenu(this.openmct.router.path, event.clientX, event.clientY);
|
||||
},
|
||||
goToParent() {
|
||||
window.location.hash = this.parentUrl;
|
||||
|
||||
@@ -30,7 +30,7 @@ export default {
|
||||
showContextMenu(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.objectPath, event.clientX, event.clientY);
|
||||
this.openmct.menus._showObjectMenu(this.objectPath, event.clientX, event.clientY);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ import ViewHistoricalDataAction from './ViewHistoricalDataAction';
|
||||
|
||||
export default function () {
|
||||
return function (openmct) {
|
||||
openmct.contextMenu.registerAction(new PreviewAction(openmct));
|
||||
openmct.contextMenu.registerAction(new ViewHistoricalDataAction(openmct));
|
||||
openmct.menus.registerObjectAction(new PreviewAction(openmct));
|
||||
openmct.menus.registerObjectAction(new ViewHistoricalDataAction(openmct));
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user