Compare commits

..

5 Commits

Author SHA1 Message Date
Joe Pea
e52ace77c3 Merge branch 'master' into remove-unused-code 2022-01-05 11:39:00 -08:00
Andrew Henry
6f3f43a555 Merge branch 'master' into remove-unused-code 2022-01-04 16:35:30 -08:00
Joe Pea
08792d0113 Merge branch 'master' into remove-unused-code 2021-12-17 11:31:54 -08:00
Joe Pea
59a4d05a0b remove ImplementationLoader 2021-12-17 00:06:00 -08:00
Joe Pea
f663a6a5b1 remove .frag files 2021-12-17 00:05:43 -08:00
29 changed files with 984 additions and 1016 deletions

View File

@@ -2,7 +2,7 @@ version: 2.1
executors:
pw-focal-development:
docker:
- image: mcr.microsoft.com/playwright:v1.17.2-focal
- image: mcr.microsoft.com/playwright:focal
environment:
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
parameters:
@@ -76,14 +76,14 @@ jobs:
node-version: <<parameters.node-version>>
- run: npm audit --audit-level=low
- generate_and_store_version_and_filesystem_artifacts
node14-lint:
parameters:
node-version:
type: string
node10-lint:
executor: pw-focal-development
steps:
- build_and_install:
node-version: <<parameters.node-version>>
- checkout
- node/install:
install-npm: false #Cannot install latest npm version with node10.
node-version: lts/dubnium
- run: npm install
- run: npm run lint
- generate_and_store_version_and_filesystem_artifacts
unit-test:
@@ -141,8 +141,7 @@ jobs:
workflows:
overall-circleci-commit-status: #These jobs run on every commit
jobs:
- node14-lint:
node-version: lts/fermium
- node10-lint
- unit-test:
name: node12-chrome
node-version: lts/erbium
@@ -159,6 +158,10 @@ workflows:
suite: ci
the-nightly: #These jobs do not run on PRs, but against master at night
jobs:
- unit-test:
name: node10-chrome-nightly
node-version: lts/dubnium
browser: ChromeHeadless
- unit-test:
name: node12-firefoxESR-nightly
node-version: lts/erbium

View File

@@ -75,36 +75,8 @@
const TWO_HOURS = ONE_HOUR * 2;
const ONE_DAY = ONE_HOUR * 24;
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.LocalStorage('namespace1', 'namespace1'));
openmct.install(openmct.plugins.LocalStorage('namespace2', 'namespace2'));
openmct.install((openmct) => {
openmct.objects.addRoot({
namespace: 'namespace1',
key: 'root'
});
openmct.objects.addRoot({
namespace: 'namespace2',
key: 'root'
});
openmct.objects.addGetInterceptor({
appliesTo: (identifier, object) => {
return (identifier.namespace === 'namespace1'
|| identifier.namespace === 'namespace2')
&& identifier.key === 'root'
&& (object === undefined || object.type === 'missing');
},
invoke: (identifier, domainObject) => {
return {
identifier: identifier,
type: 'folder',
location: 'mine',
name: identifier.namespace,
composition: []
}
}
});
});
openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.MyItems());

View File

@@ -4,16 +4,16 @@
"description": "The Open MCT core platform",
"devDependencies": {
"@braintree/sanitize-url": "^5.0.2",
"@percy/cli": "^1.0.0-beta.71",
"@percy/cli": "^1.0.0-beta.70",
"@percy/playwright": "^1.0.1",
"@playwright/test": "^1.17.2",
"@playwright/test": "^1.16.3",
"allure-playwright": "^2.0.0-beta.14",
"angular": ">=1.8.0",
"angular-route": "1.4.14",
"babel-eslint": "10.1.0",
"comma-separated-values": "^3.6.4",
"concurrently": "^3.6.1",
"copy-webpack-plugin": "^10.2.0",
"copy-webpack-plugin": "^9.0.0",
"cross-env": "^6.0.3",
"css-loader": "^4.0.0",
"d3-axis": "1.0.x",
@@ -36,7 +36,7 @@
"istanbul-instrumenter-loader": "^3.0.1",
"jasmine-core": "^4.0.0",
"jsdoc": "^3.3.2",
"karma": "6.3.10",
"karma": "6.3.9",
"karma-chrome-launcher": "3.1.0",
"karma-cli": "2.0.0",
"karma-coverage": "2.1.0",
@@ -45,20 +45,20 @@
"karma-jasmine": "4.0.1",
"karma-junit-reporter": "2.0.1",
"karma-sourcemap-loader": "0.3.8",
"karma-spec-reporter": "0.0.33",
"karma-spec-reporter": "0.0.32",
"karma-webpack": "^5.0.0",
"location-bar": "^3.0.1",
"lodash": "^4.17.12",
"markdown-toc": "^0.11.7",
"marked": "^0.3.5",
"mini-css-extract-plugin": "2.4.5",
"mini-css-extract-plugin": "^1.6.0",
"minimist": "^1.2.5",
"moment": "2.25.3",
"moment-duration-format": "^2.2.2",
"moment-timezone": "0.5.28",
"node-bourbon": "^4.2.3",
"painterro": "^1.2.56",
"playwright": "^1.17.2",
"playwright": "^1.16.3",
"plotly.js-basic-dist": "^2.5.0",
"plotly.js-gl2d-dist": "^2.5.0",
"printj": "^1.2.1",
@@ -76,7 +76,7 @@
"vue-eslint-parser": "8.0.1",
"vue-loader": "15.9.8",
"vue-template-compiler": "2.5.6",
"webpack": "^5.65.0",
"webpack": "^5.53.0",
"webpack-cli": "^4.0.0",
"webpack-dev-middleware": "^3.1.3",
"webpack-hot-middleware": "^2.22.3",
@@ -112,7 +112,7 @@
"url": "https://github.com/nasa/openmct.git"
},
"engines": {
"node": ">=12.0.1 <15.0.0"
"node": ">=10.12.2 <16.0.0"
},
"author": "",
"license": "Apache-2.0",

View File

@@ -20,12 +20,18 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
// TODO delete me!
class ImplementationLoader {
load() {
return Promise.resolve({});
}
}
define([
'./Constants',
'./FrameworkInitializer',
'./LogLevel',
'./load/BundleLoader',
'./resolve/ImplementationLoader',
'./resolve/ExtensionResolver',
'./resolve/BundleResolver',
'./register/CustomRegistrars',
@@ -37,7 +43,6 @@ define([
FrameworkInitializer,
LogLevel,
BundleLoader,
ImplementationLoader,
ExtensionResolver,
BundleResolver,
CustomRegistrars,
@@ -62,7 +67,7 @@ define([
loader = new BundleLoader($http, $log, openmct.legacyRegistry),
resolver = new BundleResolver(
new ExtensionResolver(
new ImplementationLoader({}),
new ImplementationLoader(),
$log
),
$log

View File

@@ -1,64 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/**
* Module defining ImplementationLoader. Created by vwoeltje on 11/3/14.
*/
define(
[],
function () {
/**
* Responsible for loading extension implementations
* (AMD modules.) Acts as a wrapper around RequireJS to
* provide a promise-like API.
* @memberof platform/framework
* @constructor
* @param {*} require RequireJS, or an object with similar API
* @param {*} $log Angular's logging service
*/
function ImplementationLoader(require) {
this.require = require;
}
/**
* Load an extension's implementation; or, equivalently,
* load an AMD module. This is fundamentally similar
* to a call to RequireJS, except that the result is
* wrapped in a promise. The promise will be fulfilled
* with the loaded module, or rejected with the error
* reported by Require.
*
* @param {string} path the path to the module to load
* @returns {Promise} a promise for the specified module.
*/
ImplementationLoader.prototype.load = function loadModule(path) {
var require = this.require;
return new Promise(function (fulfill, reject) {
require([path], fulfill, reject);
});
};
return ImplementationLoader;
}
);

View File

@@ -1,88 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/**
* ImplementationLoaderSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/resolve/ImplementationLoader"],
function (ImplementationLoader) {
describe("The implementation loader", function () {
var required,
loader;
function mockRequire(names, fulfill, reject) {
required = {
names: names,
fulfill: fulfill,
reject: reject
};
}
beforeEach(function () {
required = undefined;
loader = new ImplementationLoader(mockRequire);
});
it("passes script names to require", function () {
loader.load("xyz.js");
expect(required.names).toEqual(["xyz.js"]);
});
it("wraps require results in a Promise that can resolve", function () {
// Load and get the result
var promise = loader.load("xyz.js").then(function (result) {
expect(result).toEqual("test result");
});
required.fulfill("test result");
return promise;
});
it("wraps require results in a Promise that can reject", function () {
var result,
rejection;
// Load and get the result
var promise = loader.load("xyz.js").then(
function (v) {
result = v;
},
function (v) {
rejection = v;
});
expect(result).toBeUndefined();
required.reject("test result");
return promise.then(function () {
expect(result).toBeUndefined();
expect(rejection).toEqual("test result");
});
});
});
}
);

View File

@@ -1,2 +0,0 @@
return require('openmct');
}));

View File

@@ -62,14 +62,12 @@ export default {
}
},
mounted() {
this.refreshData = this.refreshData.bind(this);
this.setTimeContext();
this.loadComposition();
this.openmct.time.on('bounds', this.refreshData);
},
beforeDestroy() {
this.stopFollowingTimeContext();
this.openmct.time.off('bounds', this.refreshData);
this.removeAllSubscriptions();
@@ -81,21 +79,6 @@ export default {
this.composition.off('remove', this.removeTelemetryObject);
},
methods: {
setTimeContext() {
this.stopFollowingTimeContext();
this.timeContext = this.openmct.time.getContextForView(this.path);
this.followTimeContext();
},
followTimeContext() {
this.timeContext.on('bounds', this.refreshData);
},
stopFollowingTimeContext() {
if (this.timeContext) {
this.timeContext.off('bounds', this.refreshData);
}
},
addTelemetryObject(telemetryObject) {
// grab information we need from the added telmetry object
const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
@@ -164,7 +147,7 @@ export default {
};
},
getOptions() {
const { start, end } = this.timeContext.bounds();
const { start, end } = this.openmct.time.bounds();
return {
end,
@@ -264,10 +247,10 @@ export default {
this.addTrace(trace, key);
},
isDataInTimeRange(datum, key) {
const timeSystemKey = this.timeContext.timeSystem().key;
const timeSystemKey = this.openmct.time.timeSystem().key;
let currentTimestamp = this.parse(key, timeSystemKey, datum);
return currentTimestamp && this.timeContext.bounds().end >= currentTimestamp;
return currentTimestamp && this.openmct.time.bounds().end >= currentTimestamp;
},
format(telemetryObjectKey, metadataKey, data) {
const formats = this.telemetryObjectFormats[telemetryObjectKey];

View File

@@ -48,7 +48,7 @@ define([
let indicator = {
element: component.$mount().$el,
key: 'global-clear-indicator',
key: 'clear-data-indicator',
priority: openmct.priority.DEFAULT
};

View File

@@ -1,228 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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 ClearDataPlugin from './plugin.js';
import Vue from 'vue';
import { createOpenMct, resetApplicationState, createMouseEvent } from 'utils/testing';
describe('The Clear Data Plugin:', () => {
let clearDataPlugin;
describe('The clear data action:', () => {
let openmct;
let selection;
let mockObjectPath;
let clearDataAction;
let testViewObject;
beforeEach((done) => {
openmct = createOpenMct();
clearDataPlugin = new ClearDataPlugin(
['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
{indicator: true}
);
openmct.install(clearDataPlugin);
clearDataAction = openmct.actions.getAction('clear-data-action');
testViewObject = [{
identifier: {
key: "foo-table",
namespace: ''
},
type: "table"
}];
openmct.router.path = testViewObject;
mockObjectPath = [
{
name: 'Mock Table',
type: 'table',
identifier: {
key: "foo-table",
namespace: ''
}
}
];
selection = [
{
context: {
item: mockObjectPath[0]
}
}
];
openmct.selection.select(selection);
openmct.on('start', done);
openmct.startHeadless();
});
afterEach(() => {
openmct.router.path = null;
return resetApplicationState(openmct);
});
it('is installed', () => {
expect(clearDataAction).toBeDefined();
});
it('is applicable on applicable objects', () => {
const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
expect(gatheredActions.applicableActions['clear-data-action']).toBeDefined();
});
it('is not applicable on inapplicable objects', () => {
testViewObject = [{
identifier: {
key: "foo-widget",
namespace: ''
},
type: "widget"
}];
mockObjectPath = [
{
name: 'Mock Widget',
type: 'widget',
identifier: {
key: "foo-widget",
namespace: ''
}
}
];
selection = [
{
context: {
item: mockObjectPath[0]
}
}
];
openmct.selection.select(selection);
const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
expect(gatheredActions.applicableActions['clear-data-action']).toBeUndefined();
});
it('is not applicable if object not in the selection path and not a layout', () => {
selection = [
{
context: {
item: {
name: 'Some Random Widget',
type: 'not-in-path-widget',
identifier: {
key: "something-else-widget",
namespace: ''
}
}
}
}
];
openmct.selection.select(selection);
const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
expect(gatheredActions.applicableActions['clear-data-action']).toBeUndefined();
});
it('is applicable if object not in the selection path and is a layout', () => {
selection = [
{
context: {
item: {
name: 'Some Random Widget',
type: 'not-in-path-widget',
identifier: {
key: "something-else-widget",
namespace: ''
}
}
}
}
];
openmct.selection.select(selection);
testViewObject = [{
identifier: {
key: "foo-layout",
namespace: ''
},
type: "layout"
}];
openmct.router.path = testViewObject;
const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
expect(gatheredActions.applicableActions['clear-data-action']).toBeDefined();
});
it('fires an event upon invocation', (done) => {
openmct.objectViews.on('clearData', (domainObject) => {
expect(domainObject).toEqual(testViewObject[0]);
done();
});
clearDataAction.invoke(testViewObject);
});
});
describe('The clear data indicator:', () => {
let openmct;
let appHolder;
beforeEach((done) => {
openmct = createOpenMct();
clearDataPlugin = new ClearDataPlugin(
['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
{indicator: true}
);
openmct.install(clearDataPlugin);
appHolder = document.createElement('div');
document.body.appendChild(appHolder);
openmct.on('start', done);
openmct.start(appHolder);
});
it('installs', () => {
const globalClearIndicator = openmct.indicators.indicatorObjects
.find(indicator => indicator.key === 'global-clear-indicator').element;
expect(globalClearIndicator).toBeDefined();
});
it("renders its major elements", async () => {
await Vue.nextTick();
const indicatorClass = appHolder.querySelector('.c-indicator');
const iconClass = appHolder.querySelector('.icon-clear-data');
const indicatorLabel = appHolder.querySelector('.c-indicator__label');
const buttonElement = indicatorLabel.querySelector('button');
const hasMajorElements = Boolean(indicatorClass && iconClass && buttonElement);
expect(hasMajorElements).toBe(true);
expect(buttonElement.innerText).toEqual('Clear Data');
});
it("clicking the button fires the global clear", (done) => {
const indicatorLabel = appHolder.querySelector('.c-indicator__label');
const buttonElement = indicatorLabel.querySelector('button');
const clickEvent = createMouseEvent('click');
openmct.objectViews.on('clearData', () => {
// when we click the button, this event should fire
done();
});
buttonElement.dispatchEvent(clickEvent);
});
});
});

View File

@@ -0,0 +1,143 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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 ClearDataActionPlugin from '../plugin.js';
import ClearDataAction from '../ClearDataAction.js';
describe('When the Clear Data Plugin is installed,', () => {
const mockObjectViews = jasmine.createSpyObj('objectViews', ['emit']);
const mockIndicatorProvider = jasmine.createSpyObj('indicators', ['add']);
const mockActionsProvider = jasmine.createSpyObj('actions', ['register']);
const goodMockSelectionPath = [[{
context: {
item: {
identifier: {
key: 'apple',
namespace: ''
}
}
}
}]];
const openmct = {
objectViews: mockObjectViews,
indicators: mockIndicatorProvider,
priority: {
DEFAULT: 0
},
actions: mockActionsProvider,
install: function (plugin) {
plugin(this);
},
selection: {
get: function () {
return goodMockSelectionPath;
}
},
objects: {
areIdsEqual: function (obj1, obj2) {
return true;
}
}
};
const mockObjectPath = [
{
name: 'mockObject1',
type: 'apple'
},
{
name: 'mockObject2',
type: 'banana'
}
];
it('Global Clear Indicator is installed', () => {
openmct.install(ClearDataActionPlugin(openmct, {indicator: true}));
expect(mockIndicatorProvider.add).toHaveBeenCalled();
});
it('Clear Data context menu action is installed', () => {
openmct.install(ClearDataActionPlugin(openmct, []));
expect(mockActionsProvider.register).toHaveBeenCalled();
});
it('clear data action emits a clearData event when invoked', () => {
const action = new ClearDataAction(openmct);
action.invoke(mockObjectPath);
expect(mockObjectViews.emit).toHaveBeenCalledWith('clearData', mockObjectPath[0]);
});
it('clears data on applicable objects', () => {
let action = new ClearDataAction(openmct, ['apple']);
const actionApplies = action.appliesTo(mockObjectPath);
expect(actionApplies).toBe(true);
});
it('does not clear data on inapplicable objects', () => {
let action = new ClearDataAction(openmct, ['pineapple']);
const actionApplies = action.appliesTo(mockObjectPath);
expect(actionApplies).toBe(false);
});
it('does not clear data if not in the selection path and not a layout', () => {
openmct.objects = {
areIdsEqual: function (obj1, obj2) {
return false;
}
};
openmct.router = {
path: [{type: 'not-a-layout'}]
};
let action = new ClearDataAction(openmct, ['apple']);
const actionApplies = action.appliesTo(mockObjectPath);
expect(actionApplies).toBe(false);
});
it('does clear data if not in the selection path and is a layout', () => {
openmct.objects = {
areIdsEqual: function (obj1, obj2) {
return false;
}
};
openmct.router = {
path: [{type: 'layout'}]
};
let action = new ClearDataAction(openmct, ['apple']);
const actionApplies = action.appliesTo(mockObjectPath);
expect(actionApplies).toBe(true);
});
});

View File

@@ -109,7 +109,7 @@ export default class StyleRuleManager extends EventEmitter {
if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
this.initialize(styleConfiguration || {});
this.applyStaticStyle();
this.destroy(true);
this.destroy();
} else {
let isNewConditionSet = !this.conditionSetIdentifier
|| !this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
@@ -180,17 +180,15 @@ export default class StyleRuleManager extends EventEmitter {
this.updateDomainObjectStyle();
}
destroy(skipEventListeners) {
destroy() {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
delete this.stopProvidingTelemetry;
}
if (!skipEventListeners) {
this.openmct.time.off("bounds", this.refreshData);
this.openmct.editor.off('isEditing', this.toggleSubscription);
}
this.openmct.time.off("bounds", this.refreshData);
this.openmct.editor.off('isEditing', this.toggleSubscription);
this.conditionSetIdentifier = undefined;
}

View File

@@ -0,0 +1,475 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
<template>
<div class="c-inspector__styles c-inspect-styles">
<template v-if="!conditionSetDomainObject">
<div class="c-inspect-styles__header">
Object Style
</div>
<div class="c-inspect-styles__content">
<div v-if="staticStyle"
class="c-inspect-styles__style"
>
<StyleEditor class="c-inspect-styles__editor"
:style-item="staticStyle"
:is-editing="isEditing"
@persist="updateStaticStyle"
/>
</div>
<button
id="addConditionSet"
class="c-button c-button--major c-toggle-styling-button labeled"
@click="addConditionSet"
>
<span class="c-cs-button__label">Use Conditional Styling...</span>
</button>
</div>
</template>
<template v-else>
<div class="c-inspect-styles__header">
Conditional Object Styles
</div>
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
<a v-if="conditionSetDomainObject"
class="c-object-label icon-conditional"
@click="navigateOrPreview"
>
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
</a>
<template v-if="isEditing">
<button
id="changeConditionSet"
class="c-button labeled"
@click="addConditionSet"
>
<span class="c-button__label">Change...</span>
</button>
<button class="c-click-icon icon-x"
title="Remove conditional styles"
@click="removeConditionSet"
></button>
</template>
</div>
<div v-if="conditionsLoaded"
class="c-inspect-styles__conditions"
>
<div v-for="(conditionStyle, index) in conditionalStyles"
:key="index"
class="c-inspect-styles__condition"
:class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
@click="applySelectedConditionStyle(conditionStyle.conditionId)"
>
<condition-error :show-label="true"
:condition="getCondition(conditionStyle.conditionId)"
/>
<condition-description :show-label="true"
:condition="getCondition(conditionStyle.conditionId)"
/>
<StyleEditor class="c-inspect-styles__editor"
:style-item="conditionStyle"
:is-editing="isEditing"
@persist="updateConditionalStyle"
/>
</div>
</div>
</template>
</div>
</template>
<script>
import StyleEditor from "./StyleEditor.vue";
import SelectorDialogTree from '@/ui/components/SelectorDialogTree.vue';
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
import Vue from 'vue';
import PreviewAction from "@/ui/preview/PreviewAction.js";
import { getApplicableStylesForItem } from "@/plugins/condition/utils/styleUtils";
import isEmpty from 'lodash/isEmpty';
export default {
name: 'ConditionalStylesView',
components: {
ConditionDescription,
ConditionError,
StyleEditor
},
inject: [
'openmct',
'selection'
],
data() {
return {
conditionalStyles: [],
staticStyle: undefined,
conditionSetDomainObject: undefined,
isEditing: this.openmct.editor.isEditing(),
conditions: undefined,
conditionsLoaded: false,
navigateToPath: '',
selectedConditionId: ''
};
},
destroyed() {
this.removeListeners();
},
mounted() {
this.itemId = '';
this.getDomainObjectFromSelection();
this.previewAction = new PreviewAction(this.openmct);
if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
let objectStyles = this.itemId ? this.domainObject.configuration.objectStyles[this.itemId] : this.domainObject.configuration.objectStyles;
this.initializeStaticStyle(objectStyles);
if (objectStyles && objectStyles.conditionSetIdentifier) {
this.openmct.objects.get(objectStyles.conditionSetIdentifier).then(this.initialize);
this.conditionalStyles = objectStyles.styles;
}
} else {
this.initializeStaticStyle();
}
this.openmct.editor.on('isEditing', this.setEditState);
},
methods: {
isItemType(type, item) {
return item && (item.type === type);
},
getDomainObjectFromSelection() {
let layoutItem;
let domainObject;
if (this.selection[0].length > 1) {
//If there are more than 1 items in the this.selection[0] list, the first one could either be a sub domain object OR a layout drawing control.
//The second item in the this.selection[0] list is the container object (usually a layout)
layoutItem = this.selection[0][0].context.layoutItem;
const item = this.selection[0][0].context.item;
this.canHide = true;
if (item
&& (!layoutItem || (this.isItemType('subobject-view', layoutItem)))) {
domainObject = item;
} else {
domainObject = this.selection[0][1].context.item;
if (layoutItem) {
this.itemId = layoutItem.id;
}
}
} else {
domainObject = this.selection[0][0].context.item;
}
this.domainObject = domainObject;
this.initialStyles = getApplicableStylesForItem(domainObject, layoutItem);
this.$nextTick(() => {
this.removeListeners();
if (this.domainObject) {
this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
}
});
},
removeListeners() {
if (this.stopObserving) {
this.stopObserving();
}
if (this.stopObservingItems) {
this.stopObservingItems();
}
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
delete this.stopProvidingTelemetry;
}
},
initialize(conditionSetDomainObject) {
//If there are new conditions in the conditionSet we need to set those styles to default
this.conditionSetDomainObject = conditionSetDomainObject;
this.enableConditionSetNav();
this.initializeConditionalStyles();
},
setEditState(isEditing) {
this.isEditing = isEditing;
if (this.isEditing) {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
delete this.stopProvidingTelemetry;
}
} else {
this.subscribeToConditionSet();
}
},
addConditionSet() {
let conditionSetDomainObject;
let self = this;
function handleItemSelection({ item }) {
if (item) {
conditionSetDomainObject = item;
}
}
function dismissDialog(overlay, initialize) {
overlay.dismiss();
if (initialize && conditionSetDomainObject) {
self.conditionSetDomainObject = conditionSetDomainObject;
self.conditionalStyles = [];
self.initializeConditionalStyles();
}
}
let vm = new Vue({
components: { SelectorDialogTree },
provide: {
openmct: this.openmct
},
data() {
return {
handleItemSelection,
title: 'Select Condition Set'
};
},
template: '<selector-dialog-tree :title="title" @treeItemSelected="handleItemSelection"></selector-dialog-tree>'
}).$mount();
let overlay = this.openmct.overlays.overlay({
element: vm.$el,
size: 'small',
buttons: [
{
label: 'OK',
emphasis: 'true',
callback: () => dismissDialog(overlay, true)
},
{
label: 'Cancel',
callback: () => dismissDialog(overlay, false)
}
],
onDestroy: () => vm.$destroy()
});
},
enableConditionSetNav() {
this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
(objectPath) => {
this.objectPath = objectPath;
this.navigateToPath = '#/browse/' + this.openmct.objects.getRelativePath(this.objectPath);
}
);
},
navigateOrPreview(event) {
// If editing, display condition set in Preview overlay; otherwise nav to it while browsing
if (this.openmct.editor.isEditing()) {
event.preventDefault();
this.previewAction.invoke(this.objectPath);
} else {
this.openmct.router.navigate(this.navigateToPath);
}
},
removeConditionSet() {
this.conditionSetDomainObject = undefined;
this.conditionalStyles = [];
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
if (this.itemId) {
domainObjectStyles[this.itemId].conditionSetIdentifier = undefined;
domainObjectStyles[this.itemId].selectedConditionId = undefined;
domainObjectStyles[this.itemId].defaultConditionId = undefined;
delete domainObjectStyles[this.itemId].conditionSetIdentifier;
domainObjectStyles[this.itemId].styles = undefined;
delete domainObjectStyles[this.itemId].styles;
if (isEmpty(domainObjectStyles[this.itemId])) {
delete domainObjectStyles[this.itemId];
}
} else {
domainObjectStyles.conditionSetIdentifier = undefined;
domainObjectStyles.selectedConditionId = undefined;
domainObjectStyles.defaultConditionId = undefined;
delete domainObjectStyles.conditionSetIdentifier;
domainObjectStyles.styles = undefined;
delete domainObjectStyles.styles;
}
if (isEmpty(domainObjectStyles)) {
domainObjectStyles = undefined;
}
this.persist(domainObjectStyles);
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
delete this.stopProvidingTelemetry;
}
},
updateDomainObjectItemStyles(newItems) {
//check that all items that have been styles still exist. Otherwise delete those styles
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
let itemsToRemove = [];
let keys = Object.keys(domainObjectStyles);
//TODO: Need an easier way to find which properties are itemIds
keys.forEach((key) => {
const keyIsItemId = (key !== 'styles')
&& (key !== 'staticStyle')
&& (key !== 'defaultConditionId')
&& (key !== 'selectedConditionId')
&& (key !== 'conditionSetIdentifier');
if (keyIsItemId) {
if (!(newItems.find(item => item.id === key))) {
itemsToRemove.push(key);
}
}
});
if (itemsToRemove.length) {
this.removeItemStyles(itemsToRemove, domainObjectStyles);
}
},
removeItemStyles(itemIds, domainObjectStyles) {
itemIds.forEach(itemId => {
if (domainObjectStyles[itemId]) {
domainObjectStyles[itemId] = undefined;
delete domainObjectStyles[this.itemId];
}
});
if (isEmpty(domainObjectStyles)) {
domainObjectStyles = undefined;
}
this.persist(domainObjectStyles);
},
initializeConditionalStyles() {
if (!this.conditions) {
this.conditions = {};
}
let conditionalStyles = [];
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
if (conditionConfiguration.isDefault) {
this.selectedConditionId = conditionConfiguration.id;
}
this.conditions[conditionConfiguration.id] = conditionConfiguration;
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
if (foundStyle) {
foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style);
conditionalStyles.push(foundStyle);
} else {
conditionalStyles.splice(index, 0, {
conditionId: conditionConfiguration.id,
style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles)
});
}
});
//we're doing this so that we remove styles for any conditions that have been removed from the condition set
this.conditionalStyles = conditionalStyles;
this.conditionsLoaded = true;
this.persist(this.getDomainObjectConditionalStyle(this.selectedConditionId));
if (!this.isEditing) {
this.subscribeToConditionSet();
}
},
subscribeToConditionSet() {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
delete this.stopProvidingTelemetry;
}
if (this.conditionSetDomainObject) {
this.openmct.telemetry.request(this.conditionSetDomainObject)
.then(output => {
if (output && output.length) {
this.handleConditionSetResultUpdated(output[0]);
}
});
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(this.conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
}
},
handleConditionSetResultUpdated(resultData) {
this.selectedConditionId = resultData ? resultData.conditionId : '';
},
initializeStaticStyle(objectStyles) {
let staticStyle = objectStyles && objectStyles.staticStyle;
if (staticStyle) {
this.staticStyle = {
style: Object.assign({}, this.initialStyles, staticStyle.style)
};
} else {
this.staticStyle = {
style: Object.assign({}, this.initialStyles)
};
}
},
findStyleByConditionId(id) {
return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
},
updateStaticStyle(staticStyle) {
this.staticStyle = staticStyle;
this.persist(this.getDomainObjectConditionalStyle());
},
updateConditionalStyle(conditionStyle) {
let found = this.findStyleByConditionId(conditionStyle.conditionId);
if (found) {
found.style = conditionStyle.style;
this.selectedConditionId = found.conditionId;
this.persist(this.getDomainObjectConditionalStyle());
}
},
getDomainObjectConditionalStyle(defaultConditionId) {
let objectStyle = {
styles: this.conditionalStyles,
staticStyle: this.staticStyle,
selectedConditionId: this.selectedConditionId
};
if (defaultConditionId) {
objectStyle.defaultConditionId = defaultConditionId;
}
if (this.conditionSetDomainObject) {
objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
}
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
if (this.itemId) {
domainObjectStyles[this.itemId] = objectStyle;
} else {
//we're deconstructing here to ensure that if an item within a domainObject already had a style we don't lose it
domainObjectStyles = {
...domainObjectStyles,
...objectStyle
};
}
return domainObjectStyles;
},
getCondition(id) {
return this.conditions ? this.conditions[id] : {};
},
applySelectedConditionStyle(conditionId) {
this.selectedConditionId = conditionId;
this.persist(this.getDomainObjectConditionalStyle());
},
persist(style) {
this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
}
}
};
</script>

View File

@@ -0,0 +1,280 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
<template>
<div class="c-inspector__styles c-inspect-styles">
<div class="c-inspect-styles__header">
Object Style
</div>
<div class="c-inspect-styles__content">
<div v-if="isStaticAndConditionalStyles"
class="c-inspect-styles__mixed-static-and-conditional u-alert u-alert--block u-alert--with-icon"
>
Your selection includes one or more items that use Conditional Styling. Applying a static style below will replace any Conditional Styling with the new choice.
</div>
<div v-if="staticStyle"
class="c-inspect-styles__style"
>
<style-editor class="c-inspect-styles__editor"
:style-item="staticStyle"
:is-editing="isEditing"
:mixed-styles="mixedStyles"
@persist="updateStaticStyle"
/>
</div>
</div>
</div>
</template>
<script>
import StyleEditor from "./StyleEditor.vue";
import PreviewAction from "@/ui/preview/PreviewAction.js";
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionalStyleForItem } from "@/plugins/condition/utils/styleUtils";
import isEmpty from 'lodash/isEmpty';
export default {
name: 'MultiSelectStylesView',
components: {
StyleEditor
},
inject: [
'openmct',
'selection'
],
data() {
return {
staticStyle: undefined,
isEditing: this.openmct.editor.isEditing(),
mixedStyles: [],
isStaticAndConditionalStyles: false
};
},
destroyed() {
this.removeListeners();
},
mounted() {
this.items = [];
this.previewAction = new PreviewAction(this.openmct);
this.getObjectsAndItemsFromSelection();
this.initializeStaticStyle();
this.openmct.editor.on('isEditing', this.setEditState);
},
methods: {
isItemType(type, item) {
return item && (item.type === type);
},
hasConditionalStyles(domainObject, id) {
return getConditionalStyleForItem(domainObject, id) !== undefined;
},
getObjectsAndItemsFromSelection() {
let domainObject;
let subObjects = [];
//multiple selection
let itemInitialStyles = [];
let itemStyle;
this.selection.forEach((selectionItem) => {
const item = selectionItem[0].context.item;
const layoutItem = selectionItem[0].context.layoutItem;
if (item && this.isItemType('subobject-view', layoutItem)) {
subObjects.push(item);
itemStyle = getApplicableStylesForItem(item);
if (!this.isStaticAndConditionalStyles) {
this.isStaticAndConditionalStyles = this.hasConditionalStyles(item);
}
} else {
domainObject = selectionItem[1].context.item;
itemStyle = getApplicableStylesForItem(domainObject, layoutItem || item);
this.items.push({
id: layoutItem.id,
applicableStyles: itemStyle
});
if (!this.isStaticAndConditionalStyles) {
this.isStaticAndConditionalStyles = this.hasConditionalStyles(domainObject, layoutItem.id);
}
}
itemInitialStyles.push(itemStyle);
});
const {styles, mixedStyles} = getConsolidatedStyleValues(itemInitialStyles);
this.initialStyles = styles;
this.mixedStyles = mixedStyles;
this.domainObject = domainObject;
this.removeListeners();
if (this.domainObject) {
this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
}
subObjects.forEach(this.registerListener);
},
updateDomainObjectItemStyles(newItems) {
//check that all items that have been styles still exist. Otherwise delete those styles
let keys = Object.keys(this.domainObject.configuration.objectStyles || {});
keys.forEach((key) => {
if ((key !== 'styles')
&& (key !== 'staticStyle')
&& (key !== 'conditionSetIdentifier')) {
if (!(newItems.find(item => item.id === key))) {
this.removeItemStyles(key);
}
}
});
},
registerListener(domainObject) {
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
if (!this.domainObjectsById) {
this.domainObjectsById = {};
}
if (!this.domainObjectsById[id]) {
this.domainObjectsById[id] = domainObject;
this.observeObject(domainObject, id);
}
},
observeObject(domainObject, id) {
let unobserveObject = this.openmct.objects.observe(domainObject, '*', function (newObject) {
this.domainObjectsById[id] = JSON.parse(JSON.stringify(newObject));
}.bind(this));
this.unObserveObjects.push(unobserveObject);
},
removeListeners() {
if (this.stopObserving) {
this.stopObserving();
}
if (this.stopObservingItems) {
this.stopObservingItems();
}
if (this.unObserveObjects) {
this.unObserveObjects.forEach((unObserveObject) => {
unObserveObject();
});
}
this.unObserveObjects = [];
},
removeItemStyles(itemId) {
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
if (itemId && domainObjectStyles[itemId]) {
domainObjectStyles[itemId] = undefined;
delete domainObjectStyles[this.itemId];
if (isEmpty(domainObjectStyles)) {
domainObjectStyles = undefined;
}
this.persist(this.domainObject, domainObjectStyles);
}
},
removeConditionalStyles(domainObjectStyles, itemId) {
if (itemId) {
domainObjectStyles[itemId].conditionSetIdentifier = undefined;
delete domainObjectStyles[itemId].conditionSetIdentifier;
domainObjectStyles[itemId].styles = undefined;
delete domainObjectStyles[itemId].styles;
} else {
domainObjectStyles.conditionSetIdentifier = undefined;
delete domainObjectStyles.conditionSetIdentifier;
domainObjectStyles.styles = undefined;
delete domainObjectStyles.styles;
}
},
setEditState(isEditing) {
this.isEditing = isEditing;
},
initializeStaticStyle() {
this.staticStyle = {
style: Object.assign({}, this.initialStyles)
};
},
updateStaticStyle(staticStyle, property) {
//update the static style for each of the layoutItems as well as each sub object item
this.staticStyle = staticStyle;
this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, this.items));
if (this.domainObjectsById) {
const keys = Object.keys(this.domainObjectsById);
keys.forEach(key => {
let domainObject = this.domainObjectsById[key];
this.persist(domainObject, this.getDomainObjectStyle(domainObject, property));
});
}
this.isStaticAndConditionalStyles = false;
let foundIndex = this.mixedStyles.indexOf(property);
if (foundIndex > -1) {
this.mixedStyles.splice(foundIndex, 1);
}
},
getDomainObjectStyle(domainObject, property, items) {
let domainObjectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
if (items) {
items.forEach(item => {
let itemStaticStyle = {};
if (domainObjectStyles[item.id] && domainObjectStyles[item.id].staticStyle) {
itemStaticStyle = domainObjectStyles[item.id].staticStyle.style;
}
Object.keys(item.applicableStyles).forEach(key => {
if (property === key) {
itemStaticStyle[key] = this.staticStyle.style[key];
}
});
if (this.isStaticAndConditionalStyles) {
this.removeConditionalStyles(domainObjectStyles, item.id);
}
if (isEmpty(itemStaticStyle)) {
itemStaticStyle = undefined;
domainObjectStyles[item.id] = undefined;
} else {
domainObjectStyles[item.id] = Object.assign({}, { staticStyle: { style: itemStaticStyle } });
}
});
} else {
if (!domainObjectStyles.staticStyle) {
domainObjectStyles.staticStyle = {
style: {}
};
}
if (this.isStaticAndConditionalStyles) {
this.removeConditionalStyles(domainObjectStyles);
}
domainObjectStyles.staticStyle.style[property] = this.staticStyle.style[property];
}
return domainObjectStyles;
},
persist(domainObject, style) {
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
}
}
};
</script>

View File

@@ -63,7 +63,7 @@
<div class="c-inspect-styles__header">
Conditional Object Styles
</div>
<div class="c-inspect-styles__content c-inspect-styles__condition-set c-inspect-styles__elem">
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
<a v-if="conditionSetDomainObject"
class="c-object-label"
@click="navigateOrPreview"
@@ -87,27 +87,6 @@
</template>
</div>
<div v-if="isConditionWidget && allowEditing"
class="c-inspect-styles__elem c-inspect-styles__output-label-toggle"
>
<label class="c-toggle-switch">
<input
type="checkbox"
:checked="useConditionSetOutputAsLabel"
@change="updateConditionSetOutputLabel"
>
<span class="c-toggle-switch__slider"></span>
<span class="c-toggle-switch__label">Use Condition Set output as label</span>
</label>
</div>
<div v-if="isConditionWidget && !allowEditing"
class="c-inspect-styles__elem"
>
<span class="c-toggle-switch__label">Condition Set output as label:
<span v-if="useConditionSetOutputAsLabel"> Yes</span><span v-else> No</span>
</span>
</div>
<FontStyleEditor
v-if="canStyleFont"
:font-style="consolidatedFontStyle"
@@ -193,8 +172,7 @@ export default {
selectedConditionId: '',
items: [],
domainObject: undefined,
consolidatedFontStyle: {},
useConditionSetOutputAsLabel: false
consolidatedFontStyle: {}
};
},
computed: {
@@ -209,11 +187,6 @@ export default {
allowEditing() {
return this.isEditing && !this.locked;
},
isConditionWidget() {
const hasConditionWidgetObjects = this.domainObjectsById && Object.values(this.domainObjectsById).some((object) => object.type === 'conditionWidget');
return (hasConditionWidgetObjects || (this.domainObject && this.domainObject.type === 'conditionWidget'));
},
styleableFontItems() {
return this.selection.filter(selectionPath => {
const item = selectionPath[0].context.item;
@@ -232,6 +205,28 @@ export default {
return true;
});
},
computedconsolidatedFontStyle() {
let consolidatedFontStyle;
const styles = [];
this.styleableFontItems.forEach(styleable => {
const fontStyle = this.getFontStyle(styleable[0]);
styles.push(fontStyle);
});
if (styles.length) {
const hasConsolidatedFontSize = styles.length && styles.every((fontStyle, i, arr) => fontStyle.fontSize === arr[0].fontSize);
const hasConsolidatedFont = styles.length && styles.every((fontStyle, i, arr) => fontStyle.font === arr[0].font);
consolidatedFontStyle = {
fontSize: hasConsolidatedFontSize ? styles[0].fontSize : NON_SPECIFIC,
font: hasConsolidatedFont ? styles[0].font : NON_SPECIFIC
};
}
return consolidatedFontStyle;
},
nonSpecificFontProperties() {
if (!this.consolidatedFontStyle) {
return [];
@@ -252,8 +247,6 @@ export default {
this.previewAction = new PreviewAction(this.openmct);
this.isMultipleSelection = this.selection.length > 1;
this.getObjectsAndItemsFromSelection();
this.useConditionSetOutputAsLabel = this.getConfigurationForLabel();
if (!this.isMultipleSelection) {
let objectStyles = this.getObjectStyles();
this.initializeStaticStyle(objectStyles);
@@ -271,12 +264,6 @@ export default {
this.stylesManager.on('styleSelected', this.applyStyleToSelection);
},
methods: {
getConfigurationForLabel() {
const childObjectUsesLabels = Object.values(this.domainObjectsById || {}).some((object) => object.configuration && object.configuration.useConditionSetOutputAsLabel);
const domainObjectUsesLabels = this.domainObject && this.domainObject.configuration && this.domainObject.configuration.useConditionSetOutputAsLabel;
return childObjectUsesLabels || domainObjectUsesLabels;
},
getObjectStyles() {
let objectStyles;
if (this.domainObjectsById) {
@@ -500,14 +487,13 @@ export default {
this.conditions[conditionConfiguration.id] = conditionConfiguration;
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
let output = { output: conditionConfiguration.configuration.output };
if (foundStyle) {
foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style, output);
foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style);
conditionalStyles.push(foundStyle);
} else {
conditionalStyles.splice(index, 0, {
conditionId: conditionConfiguration.id,
style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, output)
style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles)
});
}
});
@@ -729,12 +715,6 @@ export default {
} else {
objectStyle.styles.forEach((conditionalStyle, index) => {
let style = {};
if (domainObject.configuration.useConditionSetOutputAsLabel) {
style.output = conditionalStyle.style.output;
} else {
style.output = '';
}
Object.keys(item.applicableStyles).concat(['isStyleInvisible']).forEach(key => {
style[key] = conditionalStyle.style[key];
});
@@ -751,21 +731,10 @@ export default {
}
});
} else {
if (domainObject.configuration.useConditionSetOutputAsLabel !== true) {
let objectConditionStyle = JSON.parse(JSON.stringify(objectStyle));
objectConditionStyle.styles.forEach((conditionalStyle) => {
conditionalStyle.style.output = '';
});
domainObjectStyles = {
...domainObjectStyles,
...objectConditionStyle
};
} else {
domainObjectStyles = {
...domainObjectStyles,
...objectStyle
};
}
domainObjectStyles = {
...domainObjectStyles,
...objectStyle
};
}
return domainObjectStyles;
@@ -774,17 +743,6 @@ export default {
this.selectedConditionId = conditionId;
this.getAndPersistStyles();
},
persistLabelConfiguration() {
if (this.domainObjectsById) {
Object.values(this.domainObjectsById).forEach((object) => {
this.openmct.objects.mutate(object, 'configuration.useConditionSetOutputAsLabel', this.useConditionSetOutputAsLabel);
});
} else {
this.openmct.objects.mutate(this.domainObject, 'configuration.useConditionSetOutputAsLabel', this.useConditionSetOutputAsLabel);
}
this.getAndPersistStyles();
},
persist(domainObject, style) {
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
},
@@ -905,10 +863,6 @@ export default {
const layoutItemType = selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.type;
return layoutItemType && layoutItemType !== 'subobject-view';
},
updateConditionSetOutputLabel(event) {
this.useConditionSetOutputAsLabel = event.target.checked === true;
this.persistLabelConfiguration();
}
}
};

View File

@@ -39,15 +39,12 @@
flex-direction: column;
}
&__elem {
border-bottom: 1px solid $colorInteriorBorder;
padding-bottom: $interiorMargin;
}
&__condition-set {
align-items: baseline;
border-bottom: 1px solid $colorInteriorBorder;
display: flex;
flex-direction: row;
padding-bottom: $interiorMargin;
.c-object-label {
flex: 1 1 auto;

View File

@@ -133,168 +133,6 @@ describe('the plugin', function () {
});
});
describe('the condition set usage for condition widgets', () => {
let conditionWidgetItem;
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"
}
};
beforeEach(() => {
conditionWidgetItem = {
"label": "Condition Widget",
"conditionalLabel": "",
"configuration": {
},
"name": "Condition Widget",
"type": "conditionWidget",
"identifier": {
"namespace": "",
"key": "c5e636c1-6771-4c9c-b933-8665cab189b3"
}
};
selection = [
[{
context: {
"item": conditionWidgetItem,
"supportsMultiSelect": false
}
}]
];
let viewContainer = document.createElement('div');
child.append(viewContainer);
component = new Vue({
el: viewContainer,
components: {
StylesView
},
provide: {
openmct: openmct,
selection: selection,
stylesManager
},
template: '<styles-view/>'
});
return Vue.nextTick().then(() => {
styleViewComponentObject = component.$root.$children[0];
styleViewComponentObject.setEditState(true);
});
});
afterEach(() => {
component.$destroy();
});
it('does not include the output label when the flag is disabled', () => {
styleViewComponentObject.conditionSetDomainObject = conditionSetDomainObject;
styleViewComponentObject.conditionalStyles = [];
styleViewComponentObject.initializeConditionalStyles();
expect(styleViewComponentObject.conditionalStyles.length).toBe(2);
return Vue.nextTick().then(() => {
const hasNoOutput = styleViewComponentObject.domainObject.configuration.objectStyles.styles.every((style) => {
return style.style.output === '' || style.style.output === undefined;
});
expect(hasNoOutput).toBeTrue();
});
});
it('includes the output label when the flag is enabled', () => {
styleViewComponentObject.conditionSetDomainObject = conditionSetDomainObject;
styleViewComponentObject.conditionalStyles = [];
styleViewComponentObject.initializeConditionalStyles();
expect(styleViewComponentObject.conditionalStyles.length).toBe(2);
styleViewComponentObject.useConditionSetOutputAsLabel = true;
styleViewComponentObject.persistLabelConfiguration();
return Vue.nextTick().then(() => {
const outputs = styleViewComponentObject.domainObject.configuration.objectStyles.styles.map((style) => {
return style.style.output;
});
expect(outputs.join(',')).toEqual('Sine > 0,Default');
});
});
});
describe('the condition set usage for multiple display layout items', () => {
let displayLayoutItem;
let lineLayoutItem;
@@ -611,10 +449,6 @@ describe('the plugin', function () {
const applicableStyles = getApplicableStylesForItem(styleViewComponentObject.domainObject, item);
const applicableStylesKeys = Object.keys(applicableStyles).concat(['isStyleInvisible']);
Object.keys(foundStyle.style).forEach((key) => {
if (key === 'output') {
return;
}
expect(applicableStylesKeys.indexOf(key)).toBeGreaterThan(-1);
expect(foundStyle.style[key]).toEqual(conditionalStyle.style[key]);
});

View File

@@ -26,7 +26,7 @@
:href="url"
>
<div class="c-condition-widget__label">
{{ internalDomainObject.conditionalLabel || internalDomainObject.label }}
{{ internalDomainObject.label }}
</div>
</component>
</template>

View File

@@ -27,15 +27,12 @@ export default function plugin() {
openmct.objectViews.addProvider(new ConditionWidgetViewProvider(openmct));
openmct.types.addType('conditionWidget', {
key: 'conditionWidget',
name: "Condition Widget",
description: "A button that can be used on its own, or dynamically styled with a Condition Set.",
creatable: true,
cssClass: 'icon-condition-widget',
initialize(domainObject) {
domainObject.configuration = {};
domainObject.label = 'Condition Widget';
domainObject.conditionalLabel = '';
},
form: [
{

View File

@@ -1,195 +0,0 @@
import { createOpenMct, resetApplicationState } from "utils/testing";
import ConditionWidgetPlugin from "./plugin";
import Vue from 'vue';
describe('the plugin', function () {
const CONDITION_WIDGET_KEY = 'conditionWidget';
let objectDef;
let element;
let child;
let openmct;
let mockConditionObjectDefinition;
let mockConditionObject;
let mockConditionObjectPath;
beforeEach((done) => {
mockConditionObjectPath = [
{
name: 'mock folder',
type: 'fake-folder',
identifier: {
key: 'mock-folder',
namespace: ''
}
},
{
name: 'mock parent folder',
type: 'conditionWidget',
identifier: {
key: 'mock-parent-folder',
namespace: ''
}
}
];
mockConditionObjectDefinition = {
name: 'Condition Widget',
key: 'conditionWidget',
creatable: true
};
mockConditionObject = {
"conditionWidget": {
"identifier": {
"namespace": "",
"key": "condition-widget-object"
},
"url": "https://nasa.github.io/openmct/",
"label": "Foo Widget",
"type": "conditionWidget",
"composition": []
},
"telemetry": {
"identifier": {
"namespace": "",
"key": "telemetry-object"
},
"type": "test-telemetry-object",
"name": "Test Telemetry Object",
"telemetry": {
"values": [
{
"key": "name",
"name": "Name",
"format": "string"
},
{
"key": "utc",
"name": "Time",
"format": "utc",
"hints": {
"domain": 1
}
},
{
"name": "Some attribute 1",
"key": "some-key-1",
"hints": {
"range": 1
}
},
{
"name": "Some attribute 2",
"key": "some-key-2"
}
]
}
}
};
const timeSystem = {
timeSystemKey: 'utc',
bounds: {
start: 1597160002854,
end: 1597181232854
}
};
openmct = createOpenMct(timeSystem);
openmct.install(new ConditionWidgetPlugin());
objectDef = openmct.types.get('conditionWidget').definition;
element = document.createElement('div');
element.style.width = '640px';
element.style.height = '480px';
child = document.createElement('div');
child.style.width = '640px';
child.style.height = '480px';
element.appendChild(child);
openmct.on('start', done);
openmct.startHeadless();
});
afterEach(() => {
return resetApplicationState(openmct);
});
it('defines a conditionWidget object type with the correct key', () => {
expect(objectDef.key).toEqual(mockConditionObjectDefinition.key);
});
describe('the conditionWidget object', () => {
it('is creatable', () => {
expect(objectDef.creatable).toEqual(mockConditionObjectDefinition.creatable);
});
});
describe('the view', () => {
let conditionWidgetView;
let testViewObject;
beforeEach(() => {
testViewObject = {
id: "test-object",
identifier: {
key: "test-object",
namespace: ''
},
type: "conditionWidget"
};
const applicableViews = openmct.objectViews.get(testViewObject, mockConditionObjectPath);
conditionWidgetView = applicableViews.find((viewProvider) => viewProvider.key === 'conditionWidget');
let view = conditionWidgetView.view(testViewObject, element);
view.show(child, true);
return Vue.nextTick();
});
it('provides a view', () => {
expect(conditionWidgetView).toBeDefined();
});
});
it("should have a view provider for condition widget objects", () => {
const applicableViews = openmct.objectViews.get(mockConditionObject[CONDITION_WIDGET_KEY], []);
const conditionWidgetViewProvider = applicableViews.find(
(viewProvider) => viewProvider.key === CONDITION_WIDGET_KEY
);
expect(applicableViews.length).toEqual(1);
expect(conditionWidgetViewProvider).toBeDefined();
});
it("should render a view with a URL and label", async () => {
const urlParent = document.createElement('div');
const urlChild = document.createElement('div');
urlParent.appendChild(urlChild);
const applicableViews = openmct.objectViews.get(mockConditionObject[CONDITION_WIDGET_KEY], []);
const conditionWidgetViewProvider = applicableViews.find(
(viewProvider) => viewProvider.key === CONDITION_WIDGET_KEY
);
const conditionWidgetView = conditionWidgetViewProvider.view(mockConditionObject[CONDITION_WIDGET_KEY], [mockConditionObject[CONDITION_WIDGET_KEY]]);
conditionWidgetView.show(urlChild);
await Vue.nextTick();
const domainUrl = mockConditionObject[CONDITION_WIDGET_KEY].url;
expect(urlParent.innerHTML).toContain(`<a href="${domainUrl}"`);
const conditionWidgetRender = urlParent.querySelector('.c-condition-widget');
expect(conditionWidgetRender).toBeDefined();
expect(conditionWidgetRender.innerHTML).toContain('<div class="c-condition-widget__label">');
const conditionWidgetLabel = conditionWidgetRender.querySelector('.c-condition-widget__label');
expect(conditionWidgetLabel).toBeDefined();
const domainLabel = mockConditionObject[CONDITION_WIDGET_KEY].label;
expect(conditionWidgetLabel.textContent).toContain(domainLabel);
});
});

View File

@@ -38,14 +38,10 @@ export default {
this.objectStyle = this.getObjectStyleForItem(this.parentDomainObject.configuration.objectStyles);
this.initObjectStyles();
},
beforeDestroy() {
destroyed() {
if (this.stopListeningObjectStyles) {
this.stopListeningObjectStyles();
}
if (this.styleRuleManager) {
this.styleRuleManager.destroy();
}
},
methods: {
getObjectStyleForItem(objectStyle) {

View File

@@ -106,7 +106,7 @@ export default class CreateAction extends PropertiesAction {
}
const url = '#/browse/' + objectPath
.map(object => object && this.openmct.objects.makeKeyString(object.identifier))
.map(object => object && this.openmct.objects.makeKeyString(object.identifier.key))
.reverse()
.join('/');

View File

@@ -175,7 +175,7 @@ export default {
focusEntryId: null,
search: '',
searchResults: [],
showTime: this.domainObject.configuration.showTime || 0,
showTime: 0,
showNav: false,
sidebarCoversEntries: false
};
@@ -239,12 +239,6 @@ export default {
watch: {
search() {
this.getSearchResults();
},
defaultSort() {
mutateObject(this.openmct, this.domainObject, 'configuration.defaultSort', this.defaultSort);
},
showTime() {
mutateObject(this.openmct, this.domainObject, 'configuration.showTime', this.showTime);
}
},
beforeMount() {

View File

@@ -185,11 +185,6 @@
&__inputs,
&__time-bounds {
display: flex;
.c-toggle-switch {
// Used in independent Time Conductor
flex: 0 0 auto;
}
}
&__inputs {

View File

@@ -1,45 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*
* Open MCT https://nasa.github.io/openmct/
* Version: @@version
* Built: @@timestamp
* Revision: @@revision
* Branch: @@branch
*
* @preserve
*****************************************************************************/
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.openmct = factory();
}
}(this, function() {
var BUILD_CONSTANTS = {
version: "@@version",
timestamp: "@@timestamp",
revision: "@@revision",
branch: "@@branch"
};

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
export const COLOR_PALETTE = [
[0x43, 0xB0, 0xFF],
[0x00, 0x37, 0xFF],
[0xF0, 0x60, 0x00],
[0x00, 0x70, 0x40],
[0xFB, 0x49, 0x49],
@@ -30,25 +30,25 @@ export const COLOR_PALETTE = [
[0xFF, 0xA6, 0x3D],
[0x05, 0xA3, 0x00],
[0xF0, 0x00, 0x6C],
[0xAC, 0x54, 0xAE],
[0x77, 0x17, 0x7A],
[0x23, 0xA9, 0xDB],
[0xC7, 0xBE, 0x52],
[0x5A, 0xBD, 0x56],
[0xFA, 0xF0, 0x6F],
[0x4E, 0xF0, 0x48],
[0xAD, 0x50, 0x72],
[0x94, 0x25, 0xEA],
[0x21, 0x87, 0x82],
[0x8F, 0x6E, 0x47],
[0xf0, 0x59, 0xcb],
[0x34, 0xB6, 0x7D],
[0x7F, 0x52, 0xFF],
[0x46, 0xC7, 0xC0],
[0x6A, 0x36, 0xFF],
[0x56, 0xF0, 0xE8],
[0xA1, 0x8C, 0x1C],
[0x95, 0xB1, 0x26],
[0xCB, 0xE1, 0x44],
[0xFF, 0x84, 0x9E],
[0xB7, 0x79, 0xE7],
[0x8C, 0xC9, 0xFD],
[0xDB, 0xAA, 0x6E],
[0x93, 0xB5, 0x77],
[0xB8, 0xDF, 0x97],
[0xFF, 0xBC, 0xDA],
[0xD3, 0xB6, 0xDE]
];

View File

@@ -1,6 +1,6 @@
<template>
<div>
<div v-if="supportsIndependentTime"
<div v-if="domainObject && domainObject.type === 'time-strip'"
class="c-conductor-holder--compact l-shell__main-independent-time-conductor"
>
<independent-time-conductor :domain-object="domainObject"
@@ -20,12 +20,6 @@ import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
import IndependentTimeConductor from '@/plugins/timeConductor/independent/IndependentTimeConductor.vue';
const SupportedViewTypes = [
'plot-stacked',
'plot-overlay',
'bar-graph.view',
'time-strip.view'
];
export default {
components: {
IndependentTimeConductor
@@ -70,11 +64,6 @@ export default {
},
font() {
return this.objectFontStyle ? this.objectFontStyle.font : this.layoutFont;
},
supportsIndependentTime() {
const viewKey = this.getViewKey();
return this.domainObject && SupportedViewTypes.includes(viewKey);
}
},
destroyed() {
@@ -202,12 +191,6 @@ export default {
}
}
});
if (this.domainObject && this.domainObject.type === 'conditionWidget' && keys.includes('output')) {
this.openmct.objects.mutate(this.domainObject, 'conditionalLabel', styleObj.output);
} else {
this.openmct.objects.mutate(this.domainObject, 'conditionalLabel', '');
}
},
updateView(immediatelySelect) {
this.clear();

View File

@@ -1,13 +0,0 @@
/* Note: Open MCT does not intend to support the entire Typescript ecosystem at this time.
* This file is intended to add Intellisense for IDEs like VSCode. For more information
* about Typescript, please discuss in https://github.com/nasa/openmct/discussions/4693
*/
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"strict": true,
"module": "esnext",
"moduleResolution": "node"
}
}

View File

@@ -1,5 +1,3 @@
/* global __dirname */
const path = require('path');
const packageDefinition = require('./package.json');
const CopyWebpackPlugin = require('copy-webpack-plugin');
@@ -14,8 +12,7 @@ const gitBranch = require('child_process')
.execSync('git rev-parse --abbrev-ref HEAD')
.toString().trim();
/** @type {import('webpack').Configuration} */
const config = {
module.exports = {
entry: {
openmct: './openmct.js',
couchDBChangesFeed: './src/plugins/persistence/couch/CouchChangesFeed.js',
@@ -30,7 +27,6 @@ const config = {
library: '[name]',
libraryTarget: 'umd',
publicPath: '',
hashFunction: 'xxhash64',
clean: true
},
resolve: {
@@ -133,5 +129,3 @@ const config = {
},
stats: 'errors-warnings'
};
module.exports = config;