diff --git a/src/plugins/condition/StyleRuleManager.js b/src/plugins/condition/StyleRuleManager.js
index a1ab1b6d4c..e1866f9191 100644
--- a/src/plugins/condition/StyleRuleManager.js
+++ b/src/plugins/condition/StyleRuleManager.js
@@ -109,7 +109,7 @@ export default class StyleRuleManager extends EventEmitter {
if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
this.initialize(styleConfiguration || {});
this.applyStaticStyle();
- this.destroy();
+ this.destroy(true);
} else {
let isNewConditionSet = !this.conditionSetIdentifier
|| !this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
@@ -180,15 +180,17 @@ export default class StyleRuleManager extends EventEmitter {
this.updateDomainObjectStyle();
}
- destroy() {
+ destroy(skipEventListeners) {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
delete this.stopProvidingTelemetry;
}
- this.openmct.time.off("bounds", this.refreshData);
- this.openmct.editor.off('isEditing', this.toggleSubscription);
+ if (!skipEventListeners) {
+ this.openmct.time.off("bounds", this.refreshData);
+ this.openmct.editor.off('isEditing', this.toggleSubscription);
+ }
this.conditionSetIdentifier = undefined;
}
diff --git a/src/plugins/condition/components/inspector/ConditionalStylesView.vue b/src/plugins/condition/components/inspector/ConditionalStylesView.vue
index 062ac9f35e..e69de29bb2 100644
--- a/src/plugins/condition/components/inspector/ConditionalStylesView.vue
+++ b/src/plugins/condition/components/inspector/ConditionalStylesView.vue
@@ -1,475 +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.
-*****************************************************************************/
-
-
-
-
-
-
-
-
-
-
- Use Conditional Styling...
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/plugins/condition/components/inspector/MultiSelectStylesView.vue b/src/plugins/condition/components/inspector/MultiSelectStylesView.vue
deleted file mode 100644
index d1628fe7d5..0000000000
--- a/src/plugins/condition/components/inspector/MultiSelectStylesView.vue
+++ /dev/null
@@ -1,280 +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.
-*****************************************************************************/
-
-
-
-
-
-
- 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.
-
-
-
-
-
-
-
-
-
diff --git a/src/plugins/condition/components/inspector/StylesView.vue b/src/plugins/condition/components/inspector/StylesView.vue
index b7d1d695ab..5f856c7d10 100644
--- a/src/plugins/condition/components/inspector/StylesView.vue
+++ b/src/plugins/condition/components/inspector/StylesView.vue
@@ -63,7 +63,7 @@
-
+
+
+
+
+
+ Use Condition Set output as label
+
+
+
+ Condition Set output as label:
+ Yes No
+
+
+
object.type === 'conditionWidget');
+
+ return (hasConditionWidgetObjects || (this.domainObject && this.domainObject.type === 'conditionWidget'));
+ },
styleableFontItems() {
return this.selection.filter(selectionPath => {
const item = selectionPath[0].context.item;
@@ -205,28 +232,6 @@ 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 [];
@@ -247,6 +252,8 @@ 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);
@@ -264,6 +271,12 @@ 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) {
@@ -487,13 +500,14 @@ 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);
+ foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style, output);
conditionalStyles.push(foundStyle);
} else {
conditionalStyles.splice(index, 0, {
conditionId: conditionConfiguration.id,
- style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles)
+ style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, output)
});
}
});
@@ -715,6 +729,12 @@ 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];
});
@@ -731,10 +751,21 @@ export default {
}
});
} else {
- domainObjectStyles = {
- ...domainObjectStyles,
- ...objectStyle
- };
+ 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
+ };
+ }
}
return domainObjectStyles;
@@ -743,6 +774,17 @@ 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);
},
@@ -863,6 +905,10 @@ 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();
}
}
};
diff --git a/src/plugins/condition/components/inspector/conditional-styles.scss b/src/plugins/condition/components/inspector/conditional-styles.scss
index 9679bdddd6..f046a41429 100644
--- a/src/plugins/condition/components/inspector/conditional-styles.scss
+++ b/src/plugins/condition/components/inspector/conditional-styles.scss
@@ -39,12 +39,15 @@
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;
diff --git a/src/plugins/condition/pluginSpec.js b/src/plugins/condition/pluginSpec.js
index e148872ce2..a6ba1d748e 100644
--- a/src/plugins/condition/pluginSpec.js
+++ b/src/plugins/condition/pluginSpec.js
@@ -133,6 +133,168 @@ 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: ' '
+ });
+
+ 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;
@@ -449,6 +611,10 @@ 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]);
});
diff --git a/src/plugins/conditionWidget/components/ConditionWidget.vue b/src/plugins/conditionWidget/components/ConditionWidget.vue
index bc37fb736a..ea3fcc9ee7 100644
--- a/src/plugins/conditionWidget/components/ConditionWidget.vue
+++ b/src/plugins/conditionWidget/components/ConditionWidget.vue
@@ -26,7 +26,7 @@
:href="url"
>
- {{ internalDomainObject.label }}
+ {{ internalDomainObject.conditionalLabel || internalDomainObject.label }}
diff --git a/src/plugins/conditionWidget/plugin.js b/src/plugins/conditionWidget/plugin.js
index 9b9440aed5..deb9d9dc70 100644
--- a/src/plugins/conditionWidget/plugin.js
+++ b/src/plugins/conditionWidget/plugin.js
@@ -27,12 +27,15 @@ 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: [
{
diff --git a/src/plugins/conditionWidget/pluginSpec.js b/src/plugins/conditionWidget/pluginSpec.js
new file mode 100644
index 0000000000..228e2a8c2d
--- /dev/null
+++ b/src/plugins/conditionWidget/pluginSpec.js
@@ -0,0 +1,103 @@
+import { createOpenMct, resetApplicationState } from "utils/testing";
+import ConditionWidgetPlugin from "./plugin";
+import Vue from 'vue';
+
+describe('the plugin', function () {
+ let objectDef;
+ let element;
+ let child;
+ let openmct;
+ let mockObjectPath;
+
+ beforeEach((done) => {
+ mockObjectPath = [
+ {
+ name: 'mock folder',
+ type: 'fake-folder',
+ identifier: {
+ key: 'mock-folder',
+ namespace: ''
+ }
+ },
+ {
+ name: 'mock parent folder',
+ type: 'conditionWidget',
+ identifier: {
+ key: 'mock-parent-folder',
+ namespace: ''
+ }
+ }
+ ];
+
+ 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);
+ });
+
+ let mockObject = {
+ name: 'Condition Widget',
+ key: 'conditionWidget',
+ creatable: true
+ };
+
+ it('defines a conditionWidget object type with the correct key', () => {
+ expect(objectDef.key).toEqual(mockObject.key);
+ });
+
+ describe('the conditionWidget object', () => {
+ it('is creatable', () => {
+ expect(objectDef.creatable).toEqual(mockObject.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, mockObjectPath);
+ 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();
+ });
+ });
+});
diff --git a/src/plugins/displayLayout/mixins/objectStyles-mixin.js b/src/plugins/displayLayout/mixins/objectStyles-mixin.js
index 76323f81b3..08cca1ca92 100644
--- a/src/plugins/displayLayout/mixins/objectStyles-mixin.js
+++ b/src/plugins/displayLayout/mixins/objectStyles-mixin.js
@@ -38,10 +38,14 @@ export default {
this.objectStyle = this.getObjectStyleForItem(this.parentDomainObject.configuration.objectStyles);
this.initObjectStyles();
},
- destroyed() {
+ beforeDestroy() {
if (this.stopListeningObjectStyles) {
this.stopListeningObjectStyles();
}
+
+ if (this.styleRuleManager) {
+ this.styleRuleManager.destroy();
+ }
},
methods: {
getObjectStyleForItem(objectStyle) {
diff --git a/src/ui/components/ObjectView.vue b/src/ui/components/ObjectView.vue
index 50420c2bc1..a7104a5c5c 100644
--- a/src/ui/components/ObjectView.vue
+++ b/src/ui/components/ObjectView.vue
@@ -191,6 +191,12 @@ 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();