From 86e5d10fc172e5a3c9627df5371cd632651470ea Mon Sep 17 00:00:00 2001 From: John Hill Date: Tue, 4 Jan 2022 11:41:19 -0800 Subject: [PATCH 001/283] Add npm badge for the lazy (#4619) Co-authored-by: Shefali Joshi --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dbc04ecf77..2dcbfb2074 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Open MCT [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0) [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/nasa/openmct.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/nasa/openmct/context:javascript) [![codecov](https://codecov.io/gh/nasa/openmct/branch/master/graph/badge.svg?token=7DQIipp3ej)](https://codecov.io/gh/nasa/openmct) [![This project is using Percy.io for visual regression testing.](https://percy.io/static/images/percy-badge.svg)](https://percy.io/b2e34b17/openmct) +# Open MCT [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0) [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/nasa/openmct.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/nasa/openmct/context:javascript) [![codecov](https://codecov.io/gh/nasa/openmct/branch/master/graph/badge.svg?token=7DQIipp3ej)](https://codecov.io/gh/nasa/openmct) [![This project is using Percy.io for visual regression testing.](https://percy.io/static/images/percy-badge.svg)](https://percy.io/b2e34b17/openmct) [![npm version](https://img.shields.io/npm/v/openmct.svg)](https://www.npmjs.com/package/openmct) Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data. From d53ca3ec9a2a0d11a2b69b2d38ad704297971e5f Mon Sep 17 00:00:00 2001 From: David 'Epper' Marshall Date: Tue, 4 Jan 2022 23:46:11 +0000 Subject: [PATCH 002/283] grid toggle (#4632) Co-authored-by: Nikhil Co-authored-by: Joe Pea Co-authored-by: John Hill --- src/styles/_global.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/_global.scss b/src/styles/_global.scss index 7b4ff1870c..ae66fa8323 100644 --- a/src/styles/_global.scss +++ b/src/styles/_global.scss @@ -229,7 +229,7 @@ body.desktop .has-local-controls { @include abs(); } -.c-grid { +.c-grid .c-grid { pointer-events: none; &__x { @include bgTicks($editUIGridColorFg, 'x'); } From 2fc0d34b8f04804a76214728b2c133d913799125 Mon Sep 17 00:00:00 2001 From: Jamie V Date: Tue, 4 Jan 2022 16:34:48 -0800 Subject: [PATCH 003/283] [Root Objects] Order by specified priority (#4658) * Updated objectAPI to support root priority * Updated to new ES6 module for root registry and updated docs for new priority API and root object priority * Set "My Items" to default priority of low, for root object order Co-authored-by: Andrew Henry --- API.md | 46 ++++++- src/api/objects/ObjectAPI.js | 13 +- src/api/objects/RootRegistry.js | 58 +++++---- src/api/objects/object-utils.js | 1 + src/api/objects/test/RootRegistrySpec.js | 158 ++++++++++++++--------- src/plugins/myItems/plugin.js | 8 +- 6 files changed, 179 insertions(+), 105 deletions(-) diff --git a/API.md b/API.md index c5f27843c2..a56a0de187 100644 --- a/API.md +++ b/API.md @@ -52,6 +52,8 @@ - [The URL Status Indicator](#the-url-status-indicator) - [Creating a Simple Indicator](#creating-a-simple-indicator) - [Custom Indicators](#custom-indicators) + - [Priority API](#priority-api) + - [Priority Types](#priority-types) @@ -247,16 +249,24 @@ To do so, use the `addRoot` method of the object API. eg. ```javascript openmct.objects.addRoot({ - namespace: "example.namespace", - key: "my-key" - }); + namespace: "example.namespace", + key: "my-key" +}, +openmct.priority.HIGH); ``` -The `addRoot` function takes a single [object identifier](#domain-objects-and-identifiers) -as an argument. +The `addRoot` function takes a two arguments, the first can be an [object identifier](#domain-objects-and-identifiers) for a root level object, or an array of identifiers for root +level objects, or a function that returns a promise for an identifier or an array of root level objects, the second is a [priority](#priority-api) or numeric value. -Root objects are loaded just like any other objects, i.e. via an object -provider. +When using the `getAll` method of the object API, they will be returned in order of priority. + +eg. +```javascript +openmct.objects.addRoot(identifier, openmct.priority.LOW); // low = -1000, will appear last in composition or tree +openmct.objects.addRoot(otherIdentifier, openmct.priority.HIGH); // high = 1000, will appear first in composition or tree +``` + +Root objects are loaded just like any other objects, i.e. via an object provider. ## Object Providers @@ -1051,3 +1061,25 @@ A completely custom indicator can be added by simply providing a DOM element to element: domNode }); ``` + +## Priority API + +Open MCT provides some built-in priority values that can be used in the application for view providers, indicators, root object order, and more. + +### Priority Types + +Currently, the Open MCT Priority API provides (type: numeric value): +- HIGH: 1000 +- Default: 0 +- LOW: -1000 + +View provider Example: + +``` javascript + class ViewProvider { + ... + priority() { + return openmct.priority.HIGH; + } +} +``` \ No newline at end of file diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index 56fcde50e7..4444f085b2 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -41,7 +41,7 @@ function ObjectAPI(typeRegistry, openmct) { this.typeRegistry = typeRegistry; this.eventEmitter = new EventEmitter(); this.providers = {}; - this.rootRegistry = new RootRegistry(); + this.rootRegistry = new RootRegistry(openmct); this.inMemorySearchProvider = new InMemorySearchProvider(openmct); this.rootProvider = new RootObjectProvider(this.rootRegistry); @@ -367,14 +367,17 @@ ObjectAPI.prototype.endTransaction = function () { /** * Add a root-level object. - * @param {module:openmct.ObjectAPI~Identifier|function} an array of - * identifiers for root level objects, or a function that returns a + * @param {module:openmct.ObjectAPI~Identifier|array|function} identifier an identifier or + * an array of identifiers for root level objects, or a function that returns a * promise for an identifier or an array of root level objects. + * @param {module:openmct.PriorityAPI~priority|Number} priority a number representing + * this item(s) position in the root object's composition (example: order in object tree). + * For arrays, they are treated as blocks. * @method addRoot * @memberof module:openmct.ObjectAPI# */ -ObjectAPI.prototype.addRoot = function (key) { - this.rootRegistry.addRoot(key); +ObjectAPI.prototype.addRoot = function (identifier, priority) { + this.rootRegistry.addRoot(identifier, priority); }; /** diff --git a/src/api/objects/RootRegistry.js b/src/api/objects/RootRegistry.js index a2406c14f1..bfb682f9dc 100644 --- a/src/api/objects/RootRegistry.js +++ b/src/api/objects/RootRegistry.js @@ -20,39 +20,43 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([ - 'lodash' -], function ( - _ -) { +import utils from './object-utils'; - function RootRegistry() { - this.providers = []; +export default class RootRegistry { + + constructor(openmct) { + this._rootItems = []; + this._openmct = openmct; } - RootRegistry.prototype.getRoots = function () { - const promises = this.providers.map(function (provider) { - return provider(); - }); + getRoots() { + const sortedItems = this._rootItems.sort((a, b) => b.priority - a.priority); + const promises = sortedItems.map((rootItem) => rootItem.provider()); - return Promise.all(promises) - .then(_.flatten); - }; - - function isKey(key) { - return _.isObject(key) && _.has(key, 'key') && _.has(key, 'namespace'); + return Promise.all(promises).then(rootItems => rootItems.flat()); } - RootRegistry.prototype.addRoot = function (key) { - if (isKey(key) || (Array.isArray(key) && key.every(isKey))) { - this.providers.push(function () { - return key; - }); - } else if (typeof key === "function") { - this.providers.push(key); + addRoot(rootItem, priority) { + + if (!this._isValid(rootItem)) { + return; } - }; - return RootRegistry; + this._rootItems.push({ + priority: priority || this._openmct.priority.DEFAULT, + provider: typeof rootItem === 'function' ? rootItem : () => rootItem + }); + } -}); + _isValid(rootItem) { + if (utils.isIdentifier(rootItem) || typeof rootItem === 'function') { + return true; + } + + if (Array.isArray(rootItem)) { + return rootItem.every(utils.isIdentifier); + } + + return false; + } +} diff --git a/src/api/objects/object-utils.js b/src/api/objects/object-utils.js index 41e556974e..a3464aebaa 100644 --- a/src/api/objects/object-utils.js +++ b/src/api/objects/object-utils.js @@ -172,6 +172,7 @@ define([ } return { + isIdentifier: isIdentifier, toOldFormat: toOldFormat, toNewFormat: toNewFormat, makeKeyString: makeKeyString, diff --git a/src/api/objects/test/RootRegistrySpec.js b/src/api/objects/test/RootRegistrySpec.js index b10e955e9c..8cc0c77146 100644 --- a/src/api/objects/test/RootRegistrySpec.js +++ b/src/api/objects/test/RootRegistrySpec.js @@ -19,83 +19,113 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -define([ - '../RootRegistry' -], function ( - RootRegistry -) { - describe('RootRegistry', function () { - let idA; - let idB; - let idC; - let registry; - beforeEach(function () { - idA = { - key: 'keyA', - namespace: 'something' - }; - idB = { - key: 'keyB', - namespace: 'something' - }; - idC = { - key: 'keyC', - namespace: 'something' - }; - registry = new RootRegistry(); - }); +import { createOpenMct, resetApplicationState } from '../../../utils/testing'; - it('can register a root by key', function () { - registry.addRoot(idA); +describe('RootRegistry', () => { + let openmct; + let idA; + let idB; + let idC; + let idD; - return registry.getRoots() - .then(function (roots) { - expect(roots).toEqual([idA]); - }); - }); + beforeEach((done) => { + openmct = createOpenMct(); + idA = { + key: 'keyA', + namespace: 'something' + }; + idB = { + key: 'keyB', + namespace: 'something' + }; + idC = { + key: 'keyC', + namespace: 'something' + }; + idD = { + key: 'keyD', + namespace: 'something' + }; - it('can register multiple roots by key', function () { - registry.addRoot([idA, idB]); + openmct.on('start', done); + openmct.startHeadless(); + }); - return registry.getRoots() - .then(function (roots) { - expect(roots).toEqual([idA, idB]); - }); - }); + afterEach(async () => { + await resetApplicationState(openmct); + }); - it('can register an asynchronous root ', function () { - registry.addRoot(function () { - return Promise.resolve(idA); + it('can register a root by identifier', () => { + openmct.objects.addRoot(idA); + + return openmct.objects.getRoot() + .then((rootObject) => { + expect(rootObject.composition).toEqual([idA]); }); + }); - return registry.getRoots() - .then(function (roots) { - expect(roots).toEqual([idA]); - }); - }); + it('can register multiple roots by identifier', () => { + openmct.objects.addRoot([idA, idB]); - it('can register multiple asynchronous roots', function () { - registry.addRoot(function () { - return Promise.resolve([idA, idB]); + return openmct.objects.getRoot() + .then((rootObject) => { + expect(rootObject.composition).toEqual([idA, idB]); }); + }); - return registry.getRoots() - .then(function (roots) { - expect(roots).toEqual([idA, idB]); - }); - }); + it('can register an asynchronous root ', () => { + openmct.objects.addRoot(() => Promise.resolve(idA)); - it('can combine different types of registration', function () { - registry.addRoot([idA, idB]); - registry.addRoot(function () { - return Promise.resolve([idC]); + return openmct.objects.getRoot() + .then((rootObject) => { + expect(rootObject.composition).toEqual([idA]); }); + }); - return registry.getRoots() - .then(function (roots) { - expect(roots).toEqual([idA, idB, idC]); - }); - }); + it('can register multiple asynchronous roots', () => { + openmct.objects.addRoot(() => Promise.resolve([idA, idB])); + + return openmct.objects.getRoot() + .then((rootObject) => { + expect(rootObject.composition).toEqual([idA, idB]); + }); + }); + + it('can combine different types of registration', () => { + openmct.objects.addRoot([idA, idB]); + openmct.objects.addRoot(() => Promise.resolve([idC])); + + return openmct.objects.getRoot() + .then((rootObject) => { + expect(rootObject.composition).toEqual([idA, idB, idC]); + }); + }); + + it('supports priority ordering for identifiers', () => { + openmct.objects.addRoot(idA, openmct.priority.LOW); + openmct.objects.addRoot(idB, openmct.priority.HIGH); + openmct.objects.addRoot(idC); // DEFAULT + + return openmct.objects.getRoot() + .then((rootObject) => { + expect(rootObject.composition[0]).toEqual(idB); + expect(rootObject.composition[1]).toEqual(idC); + expect(rootObject.composition[2]).toEqual(idA); + }); + }); + + it('supports priority ordering for different types of registration', () => { + openmct.objects.addRoot(() => Promise.resolve([idC]), openmct.priority.LOW); + openmct.objects.addRoot(idB, openmct.priority.HIGH); + openmct.objects.addRoot([idA, idD]); // default + + return openmct.objects.getRoot() + .then((rootObject) => { + expect(rootObject.composition[0]).toEqual(idB); + expect(rootObject.composition[1]).toEqual(idA); + expect(rootObject.composition[2]).toEqual(idD); + expect(rootObject.composition[3]).toEqual(idC); + }); }); }); diff --git a/src/plugins/myItems/plugin.js b/src/plugins/myItems/plugin.js index 2b95fa8791..cce0f5113a 100644 --- a/src/plugins/myItems/plugin.js +++ b/src/plugins/myItems/plugin.js @@ -25,11 +25,15 @@ import myItemsInterceptor from "./myItemsInterceptor"; const MY_ITEMS_DEFAULT_NAME = 'My Items'; -export default function MyItemsPlugin(name = MY_ITEMS_DEFAULT_NAME, namespace = '') { +export default function MyItemsPlugin(name = MY_ITEMS_DEFAULT_NAME, namespace = '', priority = undefined) { return function install(openmct) { const identifier = createMyItemsIdentifier(namespace); + if (priority === undefined) { + priority = openmct.priority.LOW; + } + openmct.objects.addGetInterceptor(myItemsInterceptor(openmct, identifier, name)); - openmct.objects.addRoot(identifier); + openmct.objects.addRoot(identifier, priority); }; } From 88a94c80be66dcd74c53e378faad0500c85f28a5 Mon Sep 17 00:00:00 2001 From: Scott Bell Date: Wed, 5 Jan 2022 18:57:25 +0100 Subject: [PATCH 004/283] Unable to create domain objects (#4672) * Run full regression suite on PR * rename job * specify new testsuites to run * use newer objects types * Limit concurrency to 2 workers * CI! Co-authored-by: John Hill Co-authored-by: John Hill --- .circleci/config.yml | 2 +- e2e/playwright-ci.config.js | 1 + package.json | 2 +- src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js | 5 +---- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 368984a985..ded3baf46d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -153,7 +153,7 @@ workflows: post-steps: - upload_code_covio - e2e-test: - name: e2e-smoke + name: e2e-ci node-version: lts/fermium suite: ci the-nightly: #These jobs do not run on PRs, but against master at night diff --git a/e2e/playwright-ci.config.js b/e2e/playwright-ci.config.js index a6d0d62a31..852d395683 100644 --- a/e2e/playwright-ci.config.js +++ b/e2e/playwright-ci.config.js @@ -13,6 +13,7 @@ const config = { timeout: 200 * 1000, reuseExistingServer: !process.env.CI }, + workers: 2, //Limit to 2 for CircleCI Agent use: { browserName: "chromium", baseURL: 'http://localhost:8080/', diff --git a/package.json b/package.json index 2157be1c1d..0fbda6f121 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "test:debug": "cross-env NODE_ENV=debug karma start --no-single-run", "test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run", "test:coverage:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless", - "test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js smoke", + "test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js smoke default condition.e2e", "test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js", "test:e2e:visual": "percy exec -- npx playwright test --config=e2e/playwright-visual.config.js default", "test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js", diff --git a/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js b/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js index 16c703555d..c7fbd8cdef 100644 --- a/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js +++ b/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js @@ -29,10 +29,7 @@ define( } SummaryWidgetsCompositionPolicy.prototype.allow = function (parent, child) { - const parentType = parent.getCapability('type'); - const newStyleChild = child.useCapability('adapter'); - - if (parentType.instanceOf('summary-widget') && !this.openmct.telemetry.isTelemetryObject(newStyleChild)) { + if (parent.type === 'summary-widget' && !this.openmct.telemetry.isTelemetryObject(child)) { return false; } From 36207609919134c0a511ea51cb9ec39e4bf5a062 Mon Sep 17 00:00:00 2001 From: Shefali Joshi Date: Wed, 5 Jan 2022 13:54:11 -0800 Subject: [PATCH 005/283] Condition set output label (#4233) * Show condition set label for condition widgets * CSS changes * Ensure condition set output as labels also works when condition widget is part of a display layout * Adds tests for conditionWidget * Tests for condition label output. Fix breaking tests. * Don't remove event listeners when conditionset changes Co-authored-by: Charles Hacskaylo Co-authored-by: Andrew Henry Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com> --- src/plugins/condition/StyleRuleManager.js | 10 +- .../inspector/ConditionalStylesView.vue | 475 ------------------ .../inspector/MultiSelectStylesView.vue | 280 ----------- .../components/inspector/StylesView.vue | 106 ++-- .../inspector/conditional-styles.scss | 7 +- src/plugins/condition/pluginSpec.js | 166 ++++++ .../components/ConditionWidget.vue | 2 +- src/plugins/conditionWidget/plugin.js | 3 + src/plugins/conditionWidget/pluginSpec.js | 103 ++++ .../mixins/objectStyles-mixin.js | 6 +- src/ui/components/ObjectView.vue | 6 + 11 files changed, 371 insertions(+), 793 deletions(-) delete mode 100644 src/plugins/condition/components/inspector/MultiSelectStylesView.vue create mode 100644 src/plugins/conditionWidget/pluginSpec.js 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. -*****************************************************************************/ - - - - 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. -*****************************************************************************/ - - - - 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 @@
Conditional Object Styles
-
+ +
+ +
+
+ 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(); From 22a7537974aa758262b18caf5986e0d0960cfc71 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 6 Jan 2022 11:19:51 -0800 Subject: [PATCH 006/283] Fix default plot color palette (#4621) --- src/ui/color/ColorHelper.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ui/color/ColorHelper.js b/src/ui/color/ColorHelper.js index d91a50679c..b921587470 100644 --- a/src/ui/color/ColorHelper.js +++ b/src/ui/color/ColorHelper.js @@ -21,7 +21,7 @@ *****************************************************************************/ export const COLOR_PALETTE = [ - [0x00, 0x37, 0xFF], + [0x43, 0xB0, 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], - [0x77, 0x17, 0x7A], + [0xAC, 0x54, 0xAE], [0x23, 0xA9, 0xDB], - [0xFA, 0xF0, 0x6F], - [0x4E, 0xF0, 0x48], + [0xC7, 0xBE, 0x52], + [0x5A, 0xBD, 0x56], [0xAD, 0x50, 0x72], [0x94, 0x25, 0xEA], [0x21, 0x87, 0x82], [0x8F, 0x6E, 0x47], [0xf0, 0x59, 0xcb], [0x34, 0xB6, 0x7D], - [0x6A, 0x36, 0xFF], - [0x56, 0xF0, 0xE8], + [0x7F, 0x52, 0xFF], + [0x46, 0xC7, 0xC0], [0xA1, 0x8C, 0x1C], - [0xCB, 0xE1, 0x44], + [0x95, 0xB1, 0x26], [0xFF, 0x84, 0x9E], [0xB7, 0x79, 0xE7], [0x8C, 0xC9, 0xFD], [0xDB, 0xAA, 0x6E], - [0xB8, 0xDF, 0x97], + [0x93, 0xB5, 0x77], [0xFF, 0xBC, 0xDA], [0xD3, 0xB6, 0xDE] ]; From f6934a43c9e3d4c4cb9c5f23e3a9a4f0b0080e1c Mon Sep 17 00:00:00 2001 From: Shefali Joshi Date: Fri, 7 Jan 2022 10:17:20 -0800 Subject: [PATCH 007/283] Enable independent time conductor for stacked plot and overlay plot and bar graphs (#4646) * Enable independent time conductor for stacked plot and overlay plot. * Lint fixes * Fixes for #4503 and #4606 - Added `flex: 0 0 auto` to toggle switch when in ITC to prevent element from being crunched when window or frame is very small; * Add independent time conductor to bar graphs * Add timeContext to bar graphs Co-authored-by: Charles Hacskaylo --- src/plugins/charts/BarGraphView.vue | 27 +++++++++++++++++++----- src/plugins/timeConductor/conductor.scss | 5 +++++ src/ui/components/ObjectView.vue | 13 +++++++++++- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/plugins/charts/BarGraphView.vue b/src/plugins/charts/BarGraphView.vue index 20997f1170..d9b49bd6d8 100644 --- a/src/plugins/charts/BarGraphView.vue +++ b/src/plugins/charts/BarGraphView.vue @@ -62,12 +62,14 @@ export default { } }, mounted() { + this.refreshData = this.refreshData.bind(this); + this.setTimeContext(); + this.loadComposition(); - this.openmct.time.on('bounds', this.refreshData); }, beforeDestroy() { - this.openmct.time.off('bounds', this.refreshData); + this.stopFollowingTimeContext(); this.removeAllSubscriptions(); @@ -79,6 +81,21 @@ 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); @@ -147,7 +164,7 @@ export default { }; }, getOptions() { - const { start, end } = this.openmct.time.bounds(); + const { start, end } = this.timeContext.bounds(); return { end, @@ -247,10 +264,10 @@ export default { this.addTrace(trace, key); }, isDataInTimeRange(datum, key) { - const timeSystemKey = this.openmct.time.timeSystem().key; + const timeSystemKey = this.timeContext.timeSystem().key; let currentTimestamp = this.parse(key, timeSystemKey, datum); - return currentTimestamp && this.openmct.time.bounds().end >= currentTimestamp; + return currentTimestamp && this.timeContext.bounds().end >= currentTimestamp; }, format(telemetryObjectKey, metadataKey, data) { const formats = this.telemetryObjectFormats[telemetryObjectKey]; diff --git a/src/plugins/timeConductor/conductor.scss b/src/plugins/timeConductor/conductor.scss index 453a7b2ac1..7eb6eec8ee 100644 --- a/src/plugins/timeConductor/conductor.scss +++ b/src/plugins/timeConductor/conductor.scss @@ -185,6 +185,11 @@ &__inputs, &__time-bounds { display: flex; + + .c-toggle-switch { + // Used in independent Time Conductor + flex: 0 0 auto; + } } &__inputs { diff --git a/src/ui/components/ObjectView.vue b/src/ui/components/ObjectView.vue index a7104a5c5c..cc462b61a0 100644 --- a/src/ui/components/ObjectView.vue +++ b/src/ui/components/ObjectView.vue @@ -1,6 +1,6 @@ diff --git a/src/MCT.js b/src/MCT.js index 3ea51a1d4e..0e46eab52e 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -242,8 +242,6 @@ define([ // Plugins that are installed by default this.install(this.plugins.Plot()); - this.install(this.plugins.ScatterPlot()); - this.install(this.plugins.BarChart()); this.install(this.plugins.TelemetryTable.default()); this.install(PreviewPlugin.default()); this.install(LicensesPlugin.default()); From 77804cff75a19b3fe79d4de970f63e246c2e9737 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 May 2022 19:06:32 +0000 Subject: [PATCH 200/283] Bump @percy/cli from 1.0.4 to 1.2.1 (#5244) Bumps [@percy/cli](https://github.com/percy/cli/tree/HEAD/packages/cli) from 1.0.4 to 1.2.1. - [Release notes](https://github.com/percy/cli/releases) - [Commits](https://github.com/percy/cli/commits/v1.2.1/packages/cli) --- updated-dependencies: - dependency-name: "@percy/cli" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: John Hill --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 91ecfed7c0..622b494fe1 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "devDependencies": { "@babel/eslint-parser": "7.16.3", "@braintree/sanitize-url": "6.0.0", - "@percy/cli": "1.0.4", + "@percy/cli": "1.2.1", "@percy/playwright": "1.0.3", "@playwright/test": "1.21.1", "@types/eventemitter3": "^1.0.0", From 7bb108c36b124695f976a4da5476c6a93efd53d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 May 2022 19:28:04 +0000 Subject: [PATCH 201/283] Bump webpack-dev-middleware from 5.3.1 to 5.3.3 (#5242) Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.1 to 5.3.3. - [Release notes](https://github.com/webpack/webpack-dev-middleware/releases) - [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.1...v5.3.3) --- updated-dependencies: - dependency-name: webpack-dev-middleware dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: John Hill --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 622b494fe1..652d261ccf 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "vue-template-compiler": "2.6.14", "webpack": "5.68.0", "webpack-cli": "4.9.2", - "webpack-dev-middleware": "5.3.1", + "webpack-dev-middleware": "5.3.3", "webpack-hot-middleware": "2.25.1", "webpack-merge": "5.8.0", "zepto": "1.2.0" From d9ac0182c394903f3a7bd9d92c2c88a364924811 Mon Sep 17 00:00:00 2001 From: John Hill Date: Tue, 24 May 2022 13:10:14 -0700 Subject: [PATCH 202/283] Remove languages from bug report as we don't need it (#5213) Co-authored-by: Andrew Henry --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 7ba460118a..747ec79bb6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -27,7 +27,7 @@ assignees: '' #### Environment + * Open MCT Version: * Deployment Type: * OS: From f74a35f45ae903ef4bb9514fd73959b591c0bb52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 May 2022 16:09:08 -0500 Subject: [PATCH 203/283] Bump uuid from 3.3.3 to 8.3.2 (#5170) --- example/exampleUser/ExampleUserProvider.js | 2 +- package.json | 2 +- src/api/forms/components/FormProperties.vue | 2 +- src/api/forms/components/controls/ToggleSwitchField.vue | 2 +- src/api/objects/InMemorySearchProvider.js | 2 +- src/exporters/ImageExporter.js | 2 +- src/plugins/condition/Condition.js | 2 +- src/plugins/condition/ConditionManager.js | 2 +- src/plugins/condition/components/Condition.vue | 2 +- src/plugins/condition/plugin.js | 2 +- src/plugins/displayLayout/components/DisplayLayout.vue | 2 +- src/plugins/duplicate/DuplicateTask.js | 2 +- src/plugins/exportAsJSONAction/ExportAsJSONAction.js | 2 +- src/plugins/flexibleLayout/utils/container.js | 2 +- src/plugins/flexibleLayout/utils/frame.js | 2 +- src/plugins/formActions/CreateAction.js | 2 +- src/plugins/importFromJSONAction/ImportFromJSONAction.js | 2 +- src/plugins/notebook/components/Sidebar.vue | 2 +- src/plugins/notebook/utils/notebook-image.js | 2 +- src/plugins/plan/inspector/PlanActivitiesView.vue | 2 +- src/plugins/plan/inspector/PlanActivityView.vue | 2 +- src/plugins/timelist/Timelist.vue | 2 +- src/utils/textHighlight/TextHighlight.vue | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/example/exampleUser/ExampleUserProvider.js b/example/exampleUser/ExampleUserProvider.js index bf25d7aaed..7e17de98ef 100644 --- a/example/exampleUser/ExampleUserProvider.js +++ b/example/exampleUser/ExampleUserProvider.js @@ -21,7 +21,7 @@ *****************************************************************************/ import EventEmitter from 'EventEmitter'; -import uuid from 'uuid'; +import { v4 as uuid } from 'uuid'; import createExampleUser from './exampleUserCreator'; export default class ExampleUserProvider extends EventEmitter { diff --git a/package.json b/package.json index 652d261ccf..5c58eb927a 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "sass-loader": "12.6.0", "sinon": "14.0.0", "style-loader": "^1.0.1", - "uuid": "3.3.3", + "uuid": "8.3.2", "vue": "2.6.14", "vue-eslint-parser": "8.3.0", "vue-loader": "15.9.8", diff --git a/src/api/forms/components/FormProperties.vue b/src/api/forms/components/FormProperties.vue index c43dca08d4..eac28b2f7e 100644 --- a/src/api/forms/components/FormProperties.vue +++ b/src/api/forms/components/FormProperties.vue @@ -81,7 +81,7 @@ diff --git a/src/plugins/operatorStatus/operatorStatus/OperatorStatusIndicator.js b/src/plugins/operatorStatus/operatorStatus/OperatorStatusIndicator.js new file mode 100644 index 0000000000..9eb96e938c --- /dev/null +++ b/src/plugins/operatorStatus/operatorStatus/OperatorStatusIndicator.js @@ -0,0 +1,63 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, 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 Vue from 'vue'; + +import AbstractStatusIndicator from '../AbstractStatusIndicator'; +import OperatorStatusComponent from './OperatorStatus.vue'; + +export default class OperatorStatusIndicator extends AbstractStatusIndicator { + createPopupComponent() { + const indicator = this.getIndicator(); + const popupElement = new Vue({ + components: { + OperatorStatus: OperatorStatusComponent + }, + provide: { + openmct: this.openmct, + indicator: indicator, + configuration: this.getConfiguration() + }, + data() { + return { + positionX: 0, + positionY: 0 + }; + }, + template: '' + }).$mount(); + + return popupElement; + } + + createIndicator() { + const operatorIndicator = this.openmct.indicators.simpleIndicator(); + + operatorIndicator.text("My Operator Status"); + operatorIndicator.description("Set my operator status"); + operatorIndicator.iconClass('icon-status-poll-question-mark'); + operatorIndicator.element.classList.add("c-indicator--operator-status"); + operatorIndicator.element.classList.add("no-minify"); + operatorIndicator.on('click', this.showPopup); + + return operatorIndicator; + } +} diff --git a/src/plugins/operatorStatus/plugin.js b/src/plugins/operatorStatus/plugin.js new file mode 100644 index 0000000000..3d449d1ebd --- /dev/null +++ b/src/plugins/operatorStatus/plugin.js @@ -0,0 +1,50 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, 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 OperatorStatusIndicator from './operatorStatus/OperatorStatusIndicator'; +import PollQuestionIndicator from './pollQuestion/PollQuestionIndicator'; + +/** + * @param {import('@/api/user/UserAPI').UserAPIConfiguration} configuration + * @returns {function} The plugin install function + */ +export default function operatorStatusPlugin(configuration) { + return function install(openmct) { + + if (openmct.user.hasProvider()) { + openmct.user.status.canProvideStatusForCurrentUser().then(canProvideStatus => { + if (canProvideStatus) { + const operatorStatusIndicator = new OperatorStatusIndicator(openmct, configuration); + + operatorStatusIndicator.install(); + } + }); + + openmct.user.status.canSetPollQuestion().then(canSetPollQuestion => { + if (canSetPollQuestion) { + const pollQuestionIndicator = new PollQuestionIndicator(openmct, configuration); + + pollQuestionIndicator.install(); + } + }); + } + }; +} diff --git a/src/plugins/operatorStatus/pollQuestion/PollQuestion.vue b/src/plugins/operatorStatus/pollQuestion/PollQuestion.vue new file mode 100644 index 0000000000..f279e57975 --- /dev/null +++ b/src/plugins/operatorStatus/pollQuestion/PollQuestion.vue @@ -0,0 +1,184 @@ + + + + diff --git a/src/plugins/operatorStatus/pollQuestion/PollQuestionIndicator.js b/src/plugins/operatorStatus/pollQuestion/PollQuestionIndicator.js new file mode 100644 index 0000000000..ea85d5905d --- /dev/null +++ b/src/plugins/operatorStatus/pollQuestion/PollQuestionIndicator.js @@ -0,0 +1,63 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, 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 Vue from 'vue'; + +import AbstractStatusIndicator from '../AbstractStatusIndicator'; +import PollQuestionComponent from './PollQuestion.vue'; + +export default class PollQuestionIndicator extends AbstractStatusIndicator { + createPopupComponent() { + const indicator = this.getIndicator(); + const pollQuestionElement = new Vue({ + components: { + PollQuestion: PollQuestionComponent + }, + provide: { + openmct: this.openmct, + indicator: indicator, + configuration: this.getConfiguration() + }, + data() { + return { + positionX: 0, + positionY: 0 + }; + }, + template: '' + }).$mount(); + + return pollQuestionElement; + } + + createIndicator() { + const pollQuestionIndicator = this.openmct.indicators.simpleIndicator(); + + pollQuestionIndicator.text("Poll Question"); + pollQuestionIndicator.description("Set the current poll question"); + pollQuestionIndicator.iconClass('icon-status-poll-edit'); + pollQuestionIndicator.element.classList.add("c-indicator--operator-status"); + pollQuestionIndicator.element.classList.add("no-minify"); + pollQuestionIndicator.on('click', this.showPopup); + + return pollQuestionIndicator; + } +} diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index 7beccebd54..e53ac68433 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -78,6 +78,7 @@ define([ './userIndicator/plugin', '../../example/exampleUser/plugin', './localStorage/plugin', + './operatorStatus/plugin', './gauge/GaugePlugin', './timelist/plugin' ], function ( @@ -138,6 +139,7 @@ define([ UserIndicator, ExampleUser, LocalStorage, + OperatorStatus, GaugePlugin, TimeList ) { @@ -217,6 +219,7 @@ define([ plugins.DeviceClassifier = DeviceClassifier.default; plugins.UserIndicator = UserIndicator.default; plugins.LocalStorage = LocalStorage.default; + plugins.OperatorStatus = OperatorStatus.default; plugins.Gauge = GaugePlugin.default; plugins.Timelist = TimeList.default; diff --git a/src/styles/_constants.scss b/src/styles/_constants.scss index 8d2d34179e..8b4994756e 100755 --- a/src/styles/_constants.scss +++ b/src/styles/_constants.scss @@ -156,6 +156,13 @@ $glyph-icon-notebook-page: '\e92c'; $glyph-icon-unlocked: '\e92d'; $glyph-icon-circle: '\e92e'; $glyph-icon-draft: '\e92f'; +$glyph-icon-circle-slash: '\e930'; +$glyph-icon-question-mark: '\e931'; +$glyph-icon-status-poll-check: '\e932'; +$glyph-icon-status-poll-caution: '\e933'; +$glyph-icon-status-poll-circle-slash: '\e934'; +$glyph-icon-status-poll-question-mark: '\e935'; +$glyph-icon-status-poll-edit: '\e936'; $glyph-icon-arrows-right-left: '\ea00'; $glyph-icon-arrows-up-down: '\ea01'; $glyph-icon-bullet: '\ea02'; @@ -264,6 +271,7 @@ $glyph-icon-bar-chart: '\eb2c'; $glyph-icon-map: '\eb2d'; $glyph-icon-plan: '\eb2e'; $glyph-icon-timelist: '\eb2f'; +$glyph-icon-notebook-shift-log: '\eb31'; $glyph-icon-plot-scatter: '\eb30'; /************************** GLYPHS AS DATA URI */ @@ -317,4 +325,5 @@ $bg-icon-bar-chart: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://w $bg-icon-map: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M448 32.7 384 64v448l64-31.3c35.2-17.21 64-60.1 64-95.3v-320c0-35.2-28.8-49.91-64-32.7ZM160 456l193.6 48.4v-448L160 8v448zM129.6.4 128 0 64 31.3C28.8 48.51 0 91.4 0 126.6v320c0 35.2 28.8 49.91 64 32.7l64-31.3 1.6.4Z'/%3e%3c/svg%3e"); $bg-icon-plan: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cg data-name='Layer 2'%3e%3cg data-name='Layer 1'%3e%3cpath fill='%23000000' d='M128 96V64a64.19 64.19 0 0 1 64-64h128a64.19 64.19 0 0 1 64 64v32Z'/%3e%3cpath fill='%23000000' d='M416 64v64H96V64c-52.8 0-96 43.2-96 96v256c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V160c0-52.8-43.2-96-96-96ZM64 288v-64h128v64Zm256 128H128v-64h192Zm128 0h-64v-64h64Zm0-128H256v-64h192Z'/%3e%3c/g%3e%3c/g%3e%3c/svg%3e"); $bg-icon-timelist: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cg data-name='Layer 2'%3e%3cpath d='M448 0H64A64.19 64.19 0 0 0 0 64v384a64.19 64.19 0 0 0 64 64h384a64.19 64.19 0 0 0 64-64V64a64.19 64.19 0 0 0-64-64ZM213.47 266.73a24 24 0 0 1-32.2 10.74L104 238.83V128a24 24 0 0 1 48 0v81.17l50.73 25.36a24 24 0 0 1 10.74 32.2ZM448 448H288v-64h160Zm0-96H288v-64h160Zm0-96H288v-64h160Zm0-96H288V96h160Z' data-name='Layer 1'/%3e%3c/g%3e%3c/svg%3e"); +$bg-icon-notebook-shift-log: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M448 55.36c0-39.95-27.69-63.66-61.54-52.68L0 128h448V55.36ZM448 160H0v288c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V224c0-35.2-28.8-64-64-64ZM128 416H64v-64h64v64Zm0-96H64v-64h64v64Zm320 96H192v-64h256v64Zm0-96H192v-64h256v64Z'/%3e%3c/svg%3e"); $bg-icon-plot-scatter: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cg data-name='Layer 2'%3e%3cpath d='M96 0C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96ZM64 176a48 48 0 1 1 48 48 48 48 0 0 1-48-48Zm80 240a48 48 0 1 1 48-48 48 48 0 0 1-48 48Zm128-96a48 48 0 1 1 48-48 48 48 0 0 1-48 48Zm0-160a48 48 0 1 1 48-48 48 48 0 0 1-48 48Zm128 256a48 48 0 1 1 48-48 48 48 0 0 1-48 48Z' data-name='Layer 1'/%3e%3c/g%3e%3c/svg%3e"); diff --git a/src/styles/_glyphs.scss b/src/styles/_glyphs.scss index ac82d60060..80b43eb3de 100755 --- a/src/styles/_glyphs.scss +++ b/src/styles/_glyphs.scss @@ -87,6 +87,13 @@ .icon-unlocked { @include glyphBefore($glyph-icon-unlocked); } .icon-circle { @include glyphBefore($glyph-icon-circle); } .icon-draft { @include glyphBefore($glyph-icon-draft); } +.icon-question-mark { @include glyphBefore($glyph-icon-question-mark); } +.icon-circle-slash { @include glyphBefore($glyph-icon-circle-slash); } +.icon-status-poll-check { @include glyphBefore($glyph-icon-status-poll-check); } +.icon-status-poll-caution { @include glyphBefore($glyph-icon-status-poll-caution); } +.icon-status-poll-circle-slash { @include glyphBefore($glyph-icon-status-poll-circle-slash); } +.icon-status-poll-question-mark { @include glyphBefore($glyph-icon-status-poll-question-mark); } +.icon-status-poll-edit { @include glyphBefore($glyph-icon-status-poll-edit); } .icon-arrows-right-left { @include glyphBefore($glyph-icon-arrows-right-left); } .icon-arrows-up-down { @include glyphBefore($glyph-icon-arrows-up-down); } .icon-bullet { @include glyphBefore($glyph-icon-bullet); } @@ -195,6 +202,7 @@ .icon-map { @include glyphBefore($glyph-icon-map); } .icon-plan { @include glyphBefore($glyph-icon-plan); } .icon-timelist { @include glyphBefore($glyph-icon-timelist); } +.icon-notebook-shift-log { @include glyphBefore($glyph-icon-notebook-shift-log); } .icon-plot-scatter { @include glyphBefore($glyph-icon-plot-scatter); } /************************** 12 PX CLASSES */ @@ -256,4 +264,5 @@ .bg-icon-map { @include glyphBg($bg-icon-map); } .bg-icon-plan { @include glyphBg($bg-icon-plan); } .bg-icon-timelist { @include glyphBg($bg-icon-timelist); } +.bg-icon-notebook-shift-log { @include glyphBg($bg-icon-notebook-shift-log); } .bg-icon-plot-scatter { @include glyphBg($bg-icon-plot-scatter); } diff --git a/src/styles/fonts/Open MCT Symbols 16px.json b/src/styles/fonts/Open MCT Symbols 16px.json index 7e5d8a02a9..11cc387378 100644 --- a/src/styles/fonts/Open MCT Symbols 16px.json +++ b/src/styles/fonts/Open MCT Symbols 16px.json @@ -2,7 +2,7 @@ "metadata": { "name": "Open MCT Symbols 16px", "lastOpened": 0, - "created": 1650916650636 + "created": 1651949568729 }, "iconSets": [ { @@ -391,13 +391,69 @@ "code": 59695, "tempChar": "" }, + { + "order": 212, + "id": 183, + "name": "icon-circle-slash", + "prevSize": 16, + "code": 59696, + "tempChar": "" + }, + { + "order": 213, + "id": 182, + "name": "icon-question-mark", + "prevSize": 16, + "code": 59697, + "tempChar": "" + }, + { + "order": 206, + "id": 179, + "name": "icon-status-poll-check", + "prevSize": 16, + "code": 59698, + "tempChar": "" + }, + { + "order": 207, + "id": 178, + "name": "icon-status-poll-caution", + "prevSize": 16, + "code": 59699, + "tempChar": "" + }, + { + "order": 210, + "id": 180, + "name": "icon-status-poll-circle-slash", + "prevSize": 16, + "code": 59700, + "tempChar": "" + }, + { + "order": 211, + "id": 181, + "name": "icon-status-poll-question-mark", + "prevSize": 16, + "code": 59701, + "tempChar": "" + }, + { + "order": 209, + "id": 176, + "name": "icon-status-poll-edit", + "prevSize": 16, + "code": 59702, + "tempChar": "" + }, { "order": 27, "id": 105, "name": "icon-arrows-right-left", "prevSize": 16, "code": 59904, - "tempChar": "" + "tempChar": "" }, { "order": 26, @@ -405,7 +461,7 @@ "name": "icon-arrows-up-down", "prevSize": 16, "code": 59905, - "tempChar": "" + "tempChar": "" }, { "order": 68, @@ -413,7 +469,7 @@ "name": "icon-bullet", "prevSize": 16, "code": 59906, - "tempChar": "" + "tempChar": "" }, { "order": 150, @@ -421,7 +477,7 @@ "prevSize": 16, "code": 59907, "name": "icon-calendar", - "tempChar": "" + "tempChar": "" }, { "order": 45, @@ -429,7 +485,7 @@ "name": "icon-chain-links", "prevSize": 16, "code": 59908, - "tempChar": "" + "tempChar": "" }, { "order": 73, @@ -437,7 +493,7 @@ "name": "icon-download", "prevSize": 16, "code": 59909, - "tempChar": "" + "tempChar": "" }, { "order": 39, @@ -445,7 +501,7 @@ "name": "icon-duplicate", "prevSize": 16, "code": 59910, - "tempChar": "" + "tempChar": "" }, { "order": 50, @@ -453,7 +509,7 @@ "name": "icon-folder-new", "prevSize": 16, "code": 59911, - "tempChar": "" + "tempChar": "" }, { "order": 138, @@ -461,7 +517,7 @@ "name": "icon-fullscreen-collapse", "prevSize": 16, "code": 59912, - "tempChar": "" + "tempChar": "" }, { "order": 139, @@ -469,7 +525,7 @@ "name": "icon-fullscreen-expand", "prevSize": 16, "code": 59913, - "tempChar": "" + "tempChar": "" }, { "order": 122, @@ -477,7 +533,7 @@ "name": "icon-layers", "prevSize": 16, "code": 59914, - "tempChar": "" + "tempChar": "" }, { "order": 151, @@ -485,7 +541,7 @@ "name": "icon-line-horz", "prevSize": 16, "code": 59915, - "tempChar": "" + "tempChar": "" }, { "order": 100, @@ -493,7 +549,7 @@ "name": "icon-magnify", "prevSize": 16, "code": 59916, - "tempChar": "" + "tempChar": "" }, { "order": 99, @@ -501,7 +557,7 @@ "name": "icon-magnify-in", "prevSize": 16, "code": 59917, - "tempChar": "" + "tempChar": "" }, { "order": 101, @@ -509,7 +565,7 @@ "name": "icon-magnify-out-v2", "prevSize": 16, "code": 59918, - "tempChar": "" + "tempChar": "" }, { "order": 103, @@ -517,7 +573,7 @@ "name": "icon-menu", "prevSize": 16, "code": 59919, - "tempChar": "" + "tempChar": "" }, { "order": 124, @@ -525,7 +581,7 @@ "name": "icon-move", "prevSize": 16, "code": 59920, - "tempChar": "" + "tempChar": "" }, { "order": 7, @@ -533,7 +589,7 @@ "name": "icon-new-window", "prevSize": 16, "code": 59921, - "tempChar": "" + "tempChar": "" }, { "order": 63, @@ -541,7 +597,7 @@ "name": "icon-paint-bucket-v2", "prevSize": 16, "code": 59922, - "tempChar": "" + "tempChar": "" }, { "order": 15, @@ -549,7 +605,7 @@ "name": "icon-pencil", "prevSize": 16, "code": 59923, - "tempChar": "" + "tempChar": "" }, { "order": 54, @@ -557,7 +613,7 @@ "name": "icon-pencil-edit-in-place", "prevSize": 16, "code": 59924, - "tempChar": "" + "tempChar": "" }, { "order": 40, @@ -565,7 +621,7 @@ "name": "icon-play", "prevSize": 16, "code": 59925, - "tempChar": "" + "tempChar": "" }, { "order": 125, @@ -573,7 +629,7 @@ "name": "icon-pause", "prevSize": 16, "code": 59926, - "tempChar": "" + "tempChar": "" }, { "order": 119, @@ -581,7 +637,7 @@ "name": "icon-plot-resource", "prevSize": 16, "code": 59927, - "tempChar": "" + "tempChar": "" }, { "order": 48, @@ -589,7 +645,7 @@ "name": "icon-pointer-left", "prevSize": 16, "code": 59928, - "tempChar": "" + "tempChar": "" }, { "order": 47, @@ -597,7 +653,7 @@ "name": "icon-pointer-right", "prevSize": 16, "code": 59929, - "tempChar": "" + "tempChar": "" }, { "order": 85, @@ -605,7 +661,7 @@ "name": "icon-refresh", "prevSize": 16, "code": 59930, - "tempChar": "" + "tempChar": "" }, { "order": 55, @@ -613,7 +669,7 @@ "name": "icon-save", "prevSize": 16, "code": 59931, - "tempChar": "" + "tempChar": "" }, { "order": 56, @@ -621,7 +677,7 @@ "name": "icon-save-as", "prevSize": 16, "code": 59932, - "tempChar": "" + "tempChar": "" }, { "order": 58, @@ -629,7 +685,7 @@ "name": "icon-sine", "prevSize": 16, "code": 59933, - "tempChar": "" + "tempChar": "" }, { "order": 113, @@ -637,7 +693,7 @@ "name": "icon-font", "prevSize": 16, "code": 59934, - "tempChar": "" + "tempChar": "" }, { "order": 41, @@ -645,7 +701,7 @@ "name": "icon-thumbs-strip", "prevSize": 16, "code": 59935, - "tempChar": "" + "tempChar": "" }, { "order": 146, @@ -653,7 +709,7 @@ "name": "icon-two-parts-both", "prevSize": 16, "code": 59936, - "tempChar": "" + "tempChar": "" }, { "order": 145, @@ -661,7 +717,7 @@ "name": "icon-two-parts-one-only", "prevSize": 16, "code": 59937, - "tempChar": "" + "tempChar": "" }, { "order": 82, @@ -669,7 +725,7 @@ "name": "icon-resync", "prevSize": 16, "code": 59938, - "tempChar": "" + "tempChar": "" }, { "order": 86, @@ -677,7 +733,7 @@ "name": "icon-reset", "prevSize": 16, "code": 59939, - "tempChar": "" + "tempChar": "" }, { "order": 61, @@ -685,7 +741,7 @@ "name": "icon-x-in-circle", "prevSize": 16, "code": 59940, - "tempChar": "" + "tempChar": "" }, { "order": 84, @@ -693,7 +749,7 @@ "name": "icon-brightness", "prevSize": 16, "code": 59941, - "tempChar": "" + "tempChar": "" }, { "order": 83, @@ -701,7 +757,7 @@ "name": "icon-contrast", "prevSize": 16, "code": 59942, - "tempChar": "" + "tempChar": "" }, { "order": 87, @@ -709,7 +765,7 @@ "name": "icon-expand", "prevSize": 16, "code": 59943, - "tempChar": "" + "tempChar": "" }, { "order": 89, @@ -717,7 +773,7 @@ "name": "icon-list-view", "prevSize": 16, "code": 59944, - "tempChar": "" + "tempChar": "" }, { "order": 133, @@ -725,7 +781,7 @@ "name": "icon-grid-snap-to", "prevSize": 16, "code": 59945, - "tempChar": "" + "tempChar": "" }, { "order": 132, @@ -733,7 +789,7 @@ "name": "icon-grid-snap-no", "prevSize": 16, "code": 59946, - "tempChar": "" + "tempChar": "" }, { "order": 94, @@ -741,7 +797,7 @@ "name": "icon-frame-show", "prevSize": 16, "code": 59947, - "tempChar": "" + "tempChar": "" }, { "order": 95, @@ -749,7 +805,7 @@ "name": "icon-frame-hide", "prevSize": 16, "code": 59948, - "tempChar": "" + "tempChar": "" }, { "order": 97, @@ -757,7 +813,7 @@ "name": "icon-import", "prevSize": 16, "code": 59949, - "tempChar": "" + "tempChar": "" }, { "order": 96, @@ -765,7 +821,7 @@ "name": "icon-export", "prevSize": 16, "code": 59950, - "tempChar": "" + "tempChar": "" }, { "order": 194, @@ -773,7 +829,7 @@ "name": "icon-font-size", "prevSize": 16, "code": 59951, - "tempChar": "" + "tempChar": "" }, { "order": 163, @@ -781,7 +837,7 @@ "name": "icon-clear-data", "prevSize": 16, "code": 59952, - "tempChar": "" + "tempChar": "" }, { "order": 173, @@ -789,7 +845,7 @@ "name": "icon-history", "prevSize": 16, "code": 59953, - "tempChar": "" + "tempChar": "" }, { "order": 181, @@ -797,7 +853,7 @@ "name": "icon-arrow-up-to-parent", "prevSize": 16, "code": 59954, - "tempChar": "" + "tempChar": "" }, { "order": 184, @@ -805,7 +861,7 @@ "name": "icon-crosshair-in-circle", "prevSize": 16, "code": 59955, - "tempChar": "" + "tempChar": "" }, { "order": 185, @@ -813,7 +869,7 @@ "name": "icon-target", "prevSize": 16, "code": 59956, - "tempChar": "" + "tempChar": "" }, { "order": 187, @@ -821,7 +877,7 @@ "name": "icon-items-collapse", "prevSize": 16, "code": 59957, - "tempChar": "" + "tempChar": "" }, { "order": 188, @@ -829,7 +885,7 @@ "name": "icon-items-expand", "prevSize": 16, "code": 59958, - "tempChar": "" + "tempChar": "" }, { "order": 190, @@ -837,7 +893,7 @@ "name": "icon-3-dots", "prevSize": 16, "code": 59959, - "tempChar": "" + "tempChar": "" }, { "order": 193, @@ -845,7 +901,7 @@ "name": "icon-grid-on", "prevSize": 16, "code": 59960, - "tempChar": "" + "tempChar": "" }, { "order": 192, @@ -853,7 +909,7 @@ "name": "icon-grid-off", "prevSize": 16, "code": 59961, - "tempChar": "" + "tempChar": "" }, { "order": 191, @@ -861,7 +917,7 @@ "name": "icon-camera", "prevSize": 16, "code": 59962, - "tempChar": "" + "tempChar": "" }, { "order": 196, @@ -869,7 +925,7 @@ "name": "icon-folders-collapse", "prevSize": 16, "code": 59963, - "tempChar": "" + "tempChar": "" }, { "order": 144, @@ -877,7 +933,7 @@ "name": "icon-activity", "prevSize": 16, "code": 60160, - "tempChar": "" + "tempChar": "" }, { "order": 104, @@ -885,7 +941,7 @@ "name": "icon-activity-mode", "prevSize": 16, "code": 60161, - "tempChar": "" + "tempChar": "" }, { "order": 137, @@ -893,7 +949,7 @@ "name": "icon-autoflow-tabular", "prevSize": 16, "code": 60162, - "tempChar": "" + "tempChar": "" }, { "order": 115, @@ -901,7 +957,7 @@ "name": "icon-clock", "prevSize": 16, "code": 60163, - "tempChar": "" + "tempChar": "" }, { "order": 2, @@ -909,7 +965,7 @@ "name": "icon-database", "prevSize": 16, "code": 60164, - "tempChar": "" + "tempChar": "" }, { "order": 3, @@ -917,7 +973,7 @@ "name": "icon-database-query", "prevSize": 16, "code": 60165, - "tempChar": "" + "tempChar": "" }, { "order": 67, @@ -925,7 +981,7 @@ "name": "icon-dataset", "prevSize": 16, "code": 60166, - "tempChar": "" + "tempChar": "" }, { "order": 59, @@ -933,7 +989,7 @@ "name": "icon-datatable", "prevSize": 16, "code": 60167, - "tempChar": "" + "tempChar": "" }, { "order": 136, @@ -941,7 +997,7 @@ "name": "icon-dictionary", "prevSize": 16, "code": 60168, - "tempChar": "" + "tempChar": "" }, { "order": 51, @@ -949,7 +1005,7 @@ "name": "icon-folder", "prevSize": 16, "code": 60169, - "tempChar": "" + "tempChar": "" }, { "order": 147, @@ -957,7 +1013,7 @@ "name": "icon-image", "prevSize": 16, "code": 60170, - "tempChar": "" + "tempChar": "" }, { "order": 4, @@ -965,7 +1021,7 @@ "name": "icon-layout", "prevSize": 16, "code": 60171, - "tempChar": "" + "tempChar": "" }, { "order": 24, @@ -973,7 +1029,7 @@ "name": "icon-object", "prevSize": 16, "code": 60172, - "tempChar": "" + "tempChar": "" }, { "order": 52, @@ -981,7 +1037,7 @@ "name": "icon-object-unknown", "prevSize": 16, "code": 60173, - "tempChar": "" + "tempChar": "" }, { "order": 105, @@ -989,7 +1045,7 @@ "name": "icon-packet", "prevSize": 16, "code": 60174, - "tempChar": "" + "tempChar": "" }, { "order": 126, @@ -997,7 +1053,7 @@ "name": "icon-page", "prevSize": 16, "code": 60175, - "tempChar": "" + "tempChar": "" }, { "order": 130, @@ -1005,7 +1061,7 @@ "name": "icon-plot-overlay", "prevSize": 16, "code": 60176, - "tempChar": "" + "tempChar": "" }, { "order": 80, @@ -1013,7 +1069,7 @@ "name": "icon-plot-stacked", "prevSize": 16, "code": 60177, - "tempChar": "" + "tempChar": "" }, { "order": 134, @@ -1021,7 +1077,7 @@ "name": "icon-session", "prevSize": 16, "code": 60178, - "tempChar": "" + "tempChar": "" }, { "order": 109, @@ -1029,7 +1085,7 @@ "name": "icon-tabular", "prevSize": 16, "code": 60179, - "tempChar": "" + "tempChar": "" }, { "order": 107, @@ -1037,7 +1093,7 @@ "name": "icon-tabular-lad", "prevSize": 16, "code": 60180, - "tempChar": "" + "tempChar": "" }, { "order": 106, @@ -1045,7 +1101,7 @@ "name": "icon-tabular-lad-set", "prevSize": 16, "code": 60181, - "tempChar": "" + "tempChar": "" }, { "order": 70, @@ -1053,7 +1109,7 @@ "name": "icon-tabular-realtime", "prevSize": 16, "code": 60182, - "tempChar": "" + "tempChar": "" }, { "order": 60, @@ -1061,7 +1117,7 @@ "name": "icon-tabular-scrolling", "prevSize": 16, "code": 60183, - "tempChar": "" + "tempChar": "" }, { "order": 131, @@ -1069,7 +1125,7 @@ "name": "icon-telemetry", "prevSize": 16, "code": 60184, - "tempChar": "" + "tempChar": "" }, { "order": 202, @@ -1077,7 +1133,7 @@ "name": "icon-timeline", "prevSize": 16, "code": 60185, - "tempChar": "" + "tempChar": "" }, { "order": 81, @@ -1085,7 +1141,7 @@ "name": "icon-timer", "prevSize": 16, "code": 60186, - "tempChar": "" + "tempChar": "" }, { "order": 69, @@ -1093,7 +1149,7 @@ "name": "icon-topic", "prevSize": 16, "code": 60187, - "tempChar": "" + "tempChar": "" }, { "order": 79, @@ -1101,7 +1157,7 @@ "name": "icon-box-with-dashed-lines-v2", "prevSize": 16, "code": 60188, - "tempChar": "" + "tempChar": "" }, { "order": 90, @@ -1109,7 +1165,7 @@ "name": "icon-summary-widget", "prevSize": 16, "code": 60189, - "tempChar": "" + "tempChar": "" }, { "order": 92, @@ -1117,7 +1173,7 @@ "name": "icon-notebook", "prevSize": 16, "code": 60190, - "tempChar": "" + "tempChar": "" }, { "order": 168, @@ -1125,7 +1181,7 @@ "name": "icon-tabs-view", "prevSize": 16, "code": 60191, - "tempChar": "" + "tempChar": "" }, { "order": 117, @@ -1133,7 +1189,7 @@ "name": "icon-flexible-layout", "prevSize": 16, "code": 60192, - "tempChar": "" + "tempChar": "" }, { "order": 166, @@ -1141,7 +1197,7 @@ "name": "icon-generator-sine", "prevSize": 16, "code": 60193, - "tempChar": "" + "tempChar": "" }, { "order": 167, @@ -1149,7 +1205,7 @@ "name": "icon-generator-event", "prevSize": 16, "code": 60194, - "tempChar": "" + "tempChar": "" }, { "order": 165, @@ -1157,7 +1213,7 @@ "name": "icon-gauge-v2", "prevSize": 16, "code": 60195, - "tempChar": "" + "tempChar": "" }, { "order": 170, @@ -1165,7 +1221,7 @@ "name": "icon-spectra", "prevSize": 16, "code": 60196, - "tempChar": "" + "tempChar": "" }, { "order": 171, @@ -1173,7 +1229,7 @@ "name": "icon-telemetry-spectra", "prevSize": 16, "code": 60197, - "tempChar": "" + "tempChar": "" }, { "order": 172, @@ -1181,7 +1237,7 @@ "name": "icon-pushbutton", "prevSize": 16, "code": 60198, - "tempChar": "" + "tempChar": "" }, { "order": 174, @@ -1189,7 +1245,7 @@ "name": "icon-conditional", "prevSize": 16, "code": 60199, - "tempChar": "" + "tempChar": "" }, { "order": 178, @@ -1197,7 +1253,7 @@ "name": "icon-condition-widget", "prevSize": 16, "code": 60200, - "tempChar": "" + "tempChar": "" }, { "order": 180, @@ -1205,7 +1261,7 @@ "name": "icon-alphanumeric", "prevSize": 16, "code": 60201, - "tempChar": "" + "tempChar": "" }, { "order": 183, @@ -1213,7 +1269,7 @@ "name": "icon-image-telemetry", "prevSize": 16, "code": 60202, - "tempChar": "" + "tempChar": "" }, { "order": 198, @@ -1221,7 +1277,7 @@ "name": "icon-telemetry-aggregate", "prevSize": 16, "code": 60203, - "tempChar": "" + "tempChar": "" }, { "order": 199, @@ -1229,7 +1285,7 @@ "name": "icon-bar-graph", "prevSize": 16, "code": 60204, - "tempChar": "" + "tempChar": "" }, { "order": 200, @@ -1237,7 +1293,7 @@ "name": "icon-map", "prevSize": 16, "code": 60205, - "tempChar": "" + "tempChar": "" }, { "order": 203, @@ -1245,7 +1301,7 @@ "name": "icon-plan", "prevSize": 16, "code": 60206, - "tempChar": "" + "tempChar": "" }, { "order": 204, @@ -1253,7 +1309,15 @@ "name": "icon-timelist", "prevSize": 16, "code": 60207, - "tempChar": "" + "tempChar": "" + }, + { + "order": 214, + "id": 184, + "name": "icon-notebook-restricted", + "prevSize": 16, + "code": 60209, + "tempChar": "" }, { "order": 205, @@ -2107,6 +2171,162 @@ ] } }, + { + "id": 183, + "paths": [ + "M512 0c-282.78 0-512 229.22-512 512s229.22 512 512 512 512-229.22 512-512-229.22-512-512-512zM263.1 263.1c66.48-66.48 154.88-103.1 248.9-103.1 66.74 0 130.64 18.48 185.9 52.96l-484.94 484.94c-34.5-55.24-52.96-119.16-52.96-185.9 0-94.020 36.62-182.42 103.1-248.9zM760.9 760.9c-66.48 66.48-154.88 103.1-248.9 103.1-66.74 0-130.64-18.48-185.9-52.96l484.94-484.94c34.5 55.24 52.96 119.16 52.96 185.9 0 94.020-36.62 182.42-103.1 248.9z" + ], + "attrs": [ + {} + ], + "isMulticolor": false, + "isMulticolor2": false, + "grid": 16, + "tags": [ + "icon-circle-slash" + ], + "colorPermutations": { + "12552552551": [ + {} + ] + } + }, + { + "id": 182, + "paths": [ + "M136.86 52.26c54.080-34.82 120.58-52.26 199.44-52.26 103.6 0 189.7 24.76 258.24 74.28s102.82 122.88 102.82 220.060c0 59.6-14.86 109.8-44.58 150.6-17.38 24.76-50.76 56.4-100.14 94.9l-48.68 37.82c-26.54 20.64-44.14 44.7-52.82 72.2-5.5 17.44-8.46 44.48-8.92 81.14h-186.4c2.74-77.48 10.060-131 21.94-160.58s42.5-63.62 91.88-102.12l50.060-39.2c16.46-12.38 29.72-25.9 39.78-40.58 18.28-25.2 27.42-52.96 27.42-83.22 0-34.84-10.18-66.6-30.52-95.24-20.36-28.64-57.52-42.98-111.48-42.98s-90.68 17.66-112.88 52.96c-22.18 35.32-33.26 71.98-33.26 110.040h-198.76c5.5-130.64 51.12-223.24 136.86-277.82zM251.020 825.24h205.62v198.74h-205.62v-198.74z" + ], + "attrs": [ + {} + ], + "width": 697, + "isMulticolor": false, + "isMulticolor2": false, + "grid": 16, + "tags": [ + "icon-question-mark" + ], + "colorPermutations": { + "12552552551": [ + {} + ] + } + }, + { + "id": 179, + "paths": [ + "M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM768 448l-320 320-192-192v-192l192 192 320-320v192z" + ], + "attrs": [ + {} + ], + "isMulticolor": false, + "isMulticolor2": false, + "grid": 16, + "tags": [ + "icon-status-poll-check" + ], + "colorPermutations": { + "12552552551": [ + {} + ] + } + }, + { + "id": 178, + "paths": [ + "M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM781.36 704h-538.72c-44.96 0-63.5-31.94-41.2-70.98l270-472.48c22.3-39.040 58.82-39.040 81.12 0l269.98 472.48c22.3 39.040 3.78 70.98-41.2 70.98z", + "M457.14 417.86l24.2 122.64h61.32l24.2-122.64v-163.5h-109.72v163.5z", + "M471.12 581.36h81.76v81.76h-81.76v-81.76z" + ], + "attrs": [ + {}, + {}, + {} + ], + "isMulticolor": false, + "isMulticolor2": false, + "grid": 16, + "tags": [ + "icon-status-poll-caution" + ], + "colorPermutations": { + "12552552551": [ + {}, + {}, + {} + ] + } + }, + { + "id": 180, + "paths": [ + "M391.18 668.7c35.72 22.98 77.32 35.3 120.82 35.3 59.84 0 116.080-23.3 158.4-65.6 42.3-42.3 65.6-98.56 65.6-158.4 0-43.5-12.32-85.080-35.3-120.82l-309.52 309.52z", + "M512 256c-59.84 0-116.080 23.3-158.4 65.6-42.3 42.3-65.6 98.56-65.6 158.4 0 43.5 12.32 85.080 35.3 120.82l309.52-309.52c-35.72-22.98-77.32-35.3-120.82-35.3z", + "M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM512 800c-176.74 0-320-143.26-320-320s143.26-320 320-320 320 143.26 320 320-143.26 320-320 320z" + ], + "attrs": [ + {}, + {}, + {} + ], + "isMulticolor": false, + "isMulticolor2": false, + "grid": 16, + "tags": [ + "icon-status-poll-circle-slash" + ], + "colorPermutations": { + "12552552551": [ + {}, + {}, + {} + ] + } + }, + { + "id": 181, + "paths": [ + "M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM579.020 832h-141.36v-136.64h141.36v136.64zM713.84 433.9c-11.94 17.020-34.9 38.78-68.84 65.24l-33.48 26c-18.24 14.18-30.34 30.74-36.32 49.64-3.78 11.98-5.82 30.58-6.14 55.8h-128.12c1.88-53.26 6.92-90.060 15.080-110.4 8.18-20.34 29.22-43.74 63.16-70.22l34.42-26.94c11.3-8.52 20.42-17.8 27.34-27.9 12.56-17.34 18.86-36.4 18.86-57.2 0-23.94-7-45.78-20.98-65.48-14-19.7-39.54-29.54-76.64-29.54s-62.34 12.14-77.6 36.4c-15.24 24.28-22.88 49.48-22.88 75.64h-136.64c3.78-89.84 35.14-153.5 94.080-191.020 37.18-23.94 82.9-35.94 137.12-35.94 71.22 0 130.42 17.020 177.54 51.060s70.68 84.48 70.68 151.3c0 40.98-10.22 75.5-30.66 103.54z" + ], + "attrs": [ + {} + ], + "isMulticolor": false, + "isMulticolor2": false, + "grid": 16, + "tags": [ + "icon-status-poll-question-mark" + ], + "colorPermutations": { + "12552552551": [ + {} + ] + } + }, + { + "id": 176, + "paths": [ + "M1000.080 334.64l-336.6 336.76-20.52 6.88-450.96 153.72 160.68-471.52 332.34-332.34c-54.040-18.2-112.28-28.14-173.020-28.14-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480 0-50.68-8.4-99.5-23.92-145.36z", + "M408.42 395.24l-2.16 6.3-111.7 327.9 334.12-113.86 4.62-4.68 350.28-350.28c6.8-6.78 14.96-19.1 14.96-38.9 0-34.86-26.82-83.28-69.88-126.38-26.54-26.54-55.9-47.6-82.7-59.34-47.34-20.8-72.020-6.24-82.64 4.36l-354.9 354.88zM470.56 421.42h44v88h88v44l-4.7 12.72-139.68 47.54-47.94-47.94 47.6-139.72 12.72-4.6z" + ], + "attrs": [ + {}, + {} + ], + "isMulticolor": false, + "isMulticolor2": false, + "grid": 16, + "tags": [ + "icon-status-poll-edit" + ], + "colorPermutations": { + "12552552551": [ + {}, + {} + ] + } + }, { "id": 105, "paths": [ @@ -3326,15 +3546,21 @@ { "id": 76, "paths": [ - "M510-2l-512 320v384l512 320 512-320v-384l-512-320zM585.4 859.2c-21.2 20.8-46 30.8-76 30.8-31.2 0-56.2-9.8-76.2-29.6-20-20-29.6-44.8-29.6-76.2 0-30.4 10.2-55.2 31-76.2s45.2-31.2 74.8-31.2c29.6 0 54.2 10.4 75.6 32s31.8 46.4 31.8 76c-0.2 29-10.8 54-31.4 74.4zM638.2 546.6c-23.6 11.8-37.4 22-43.4 32.4-3.6 6.2-6 14.8-7.4 26.8v41h-161.4v-44.2c0-40.2 4.4-69.8 13-88 8-17.2 22.6-30.2 44.8-40l34.8-15.4c32-14.2 48.2-35.2 48.2-62.8 0-16-6-30.4-17.2-41.8-11.2-11.2-25.6-17.2-41.6-17.2-24 0-54.4 10-62.8 57.4l-2.2 12.2h-147l1.4-16.2c4-44.6 17-82.4 38.8-112.2 19.6-27 45.6-48.6 77-64.6s64.6-24 98.2-24c60.6 0 110.2 19.4 151.4 59.6 41.2 40 61.2 88 61.2 147.2 0 70.8-28.8 121.4-85.8 149.8z" + "M511.98 0l-511.98 320v384l512 320 512-320v-384l-512.020-320zM586.22 896h-141.36v-136.64h141.36v136.64zM721.040 497.9c-11.94 17.020-34.9 38.78-68.84 65.24l-33.48 26c-18.24 14.18-30.34 30.74-36.32 49.64-3.78 11.98-5.82 30.58-6.14 55.8h-128.12c1.88-53.26 6.92-90.060 15.080-110.4 8.18-20.34 29.22-43.74 63.16-70.22l34.42-26.94c11.3-8.52 20.42-17.8 27.34-27.9 12.56-17.34 18.86-36.4 18.86-57.2 0-23.94-7-45.78-20.98-65.48-14-19.7-39.54-29.54-76.64-29.54s-62.34 12.14-77.6 36.4c-15.24 24.28-22.88 49.48-22.88 75.64h-136.64c3.78-89.84 35.14-153.5 94.080-191.020 37.18-23.94 82.9-35.94 137.12-35.94 71.22 0 130.42 17.020 177.54 51.060s70.68 84.48 70.68 151.3c0 40.98-10.22 75.5-30.66 103.54z" + ], + "attrs": [ + {} ], - "attrs": [], "grid": 16, "tags": [ "icon-object-unknown" ], + "isMulticolor": false, + "isMulticolor2": false, "colorPermutations": { - "12552552551": [] + "12552552551": [ + {} + ] } }, { @@ -4009,6 +4235,29 @@ ] } }, + { + "id": 184, + "paths": [ + "M896 110.72c0-79.9-55.38-127.32-123.080-105.36l-772.92 250.64h896v-145.28z", + "M896 320h-896v576c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-448c0-70.4-57.6-128-128-128zM256 832h-128v-128h128v128zM256 640h-128v-128h128v128zM896 832h-512v-128h512v128zM896 640h-512v-128h512v128z" + ], + "attrs": [ + {}, + {} + ], + "isMulticolor": false, + "isMulticolor2": false, + "grid": 16, + "tags": [ + "icon-notebook-restricted" + ], + "colorPermutations": { + "12552552551": [ + {}, + {} + ] + } + }, { "id": 176, "paths": [ diff --git a/src/styles/fonts/Open-MCT-Symbols-16px.svg b/src/styles/fonts/Open-MCT-Symbols-16px.svg index c6455e381c..38ce5985a3 100644 --- a/src/styles/fonts/Open-MCT-Symbols-16px.svg +++ b/src/styles/fonts/Open-MCT-Symbols-16px.svg @@ -3,165 +3,173 @@ Generated by IcoMoon - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/styles/fonts/Open-MCT-Symbols-16px.ttf b/src/styles/fonts/Open-MCT-Symbols-16px.ttf index 073e2c6ec556deac1f1e5c31eb9885033409d428..94ab53538b6ee4ee72e83b1e7a1afc6b78958239 100644 GIT binary patch delta 6255 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 diff --git a/src/styles/fonts/Open-MCT-Symbols-16px.woff b/src/styles/fonts/Open-MCT-Symbols-16px.woff index ca1cc26bede033633737bcd580ab3615e72ae99a..510d6e9a9b85dae9d62daa494a38a0bdad0131be 100644 GIT binary patch delta 6470 zcmaJl3vg4{mG{19NtSHu|CcOFdivPHmTk%MN5=N!W8+_JBfuD34CWV-f(<5tgb?%! zJ0t-@o8E4QFeRH1y0gi&DJeTNp_IOQXw$MS!-P&Z({5O|O_`mfunfD&?1KEf=Soku znI$~=KHqcCJ@=k_{`ZML$9Mh(+xPZ&bRdl2*>VRJW&fX{@GJIU+E_TaX~*bJgzz>% zY^Dusf7bH&=x#dp8-NStrhk08cL{VH)u``61&>134oi1|c=}F-Srblz;(dMw*#qC3X^UiYyi;V2ME)7@%)FNhi~( zQ~@($mW}*6bOI}8C=LUTr7UJ~1{O%LNb!_zaT<`5bI8ezfMNTB+@c5;%`q3G%uyns zgy$=m%P~aXc#=-e&V7J6MgEXH-2#~79^?)K%^U+}VD4|MNIqk0@pj@aa43KwfRhD_ zFi-Xu_+~tPH>we*4=QGy{jB+GQkA zk{?wP(HJDvngwwFVNFQEgMxT+SmU$tA*bLBWnXXrkZ#T*$R*9!fu~4OwL)a!M@EX; zVgEBldD&elS_*6Fiv2Qw^~Em${!($X3`@mp06$bxp}5Z8Vt+smm+S?+RFcQHYMTMx zr*$c^)=Exlt1Xj~CumI0aiSBLmR*ht0sN=GQ#Oo^7{Mqi>Q$Gz7~* zxc`~KDRXnfkObIabjxtkI0&bI#TWqTA&}k$<2K-yGeu?1sSz^ySd-7Mv>4)W08z{c zi5?O2^w*Qf2xyLtgQhp|dcv8zl!yWQI!GqW?f4*h-yD=T`?GmDY+h%{!y^_d+S?JE zDR)!|9zeGOnIITek;|4K`OH$Qk-p7wYA?^?r6gnx*rnIGtt{_V&uxrcr~=iK0ky}4 zTX8&Qwc#jE)YUg&E#~~8F!o@JSxF4Fj#BAVOjs5hiAXM|D-pwYa}fS3KyY|4+O+h) zv4qPhxgsO6WkO8SQV9Pw3jvckQ~dzcScjTGG`a1v>#MX_2OGja71xRzT5%gN1OF0r zp>P!VPy)t6F{ZeMOT1LRxw-Efb@8~>S`rJ^nw_eG0(^lfN~_h;V651z(rR^jU1NDg zgTbKxu%MlvxO$a6=5z*w!LhEYo-$K)X-F5alvKEq12${CG^{oknIbl4wbf#Uvo*Lk z9<9jS|L{X9*eugDt{^vf7{Y$aqtarw+Ss56kf4bs1DF`9DFrlHk`f+2~gb6^r|KI^Ek zc?GIQ@kcX|4uP$O8gPBghOJ5t+u{ugj!Nti9KAJt({I;ft)a$%wR*c>x@d2;k7-k> zNAWn#M@sR>9*WfsJv7M~RXp zXB(~2tgP`1t`=Was?6&*8l)@F$~{~UqYuiN1gg0-tZVdp%TiUo7MFAdpUU@fy}^jl zSBpI}<#4P{HZNY>oLudIXRbdlj({4xXw{>XkOnhgxkTJFYtUJ<{c3}r?*xxfQ~2eV zUr=)npVp{gSpgR+m0kx|s8y&nMVg|J+Z`$@E_#Wru1vez%j<^%9H-MgN`V>iC9Ej% znw>=st=erWZ`A2HvrDNhEOG0NKAl!)G@63uB^%sEqtoH+iIvq^=7kI|7-9>}N-Bkk zYI2?;)AOAoq)@TJ2JqyZ8;3(m71*RDZqeaz8)pAQ`k`5Bc@y_m3`QT+WDY8xKEsUT zz5={np$U1-3(SFVp>(RPa!7Hkrly41C7o5B#2@2ct#(eWi8*%)bvaei2fGw_L}Ai7 zm-@^ewZ0e@a1K?L)YKeP%=2wBVzn;LRyH)j(r3=?^97bg4UV0wTNnrj5-&(R7U^85 z;E(|636b+wzxA50y4r`Q=o=x;YzDe*{SJid&lg8^EYd^Hy8{uW7F=X65p8wk3)^01 z`WIx}z6c*6@7XJu=>y~wd$W3$4dQpiJdmTHUPH8&!3Z`N>GpF|CLBGm#U%$PSDiK# zCdZp9iPf24fVn%(Wn{avL{29MoXa6sU2~So@LlI}P7uZZT_PihyZS{806-a$0`oT6 z>M9%Ja}|+*d7f5DJa7SwAuK?>fx!8Z$sd9g68FWdkUKcu6%4vqfxfX2bkYB=Af=gQ z9hE}3KtXb#w3OU)r68$3=3Xc>cHDgm;L6f=8QxQR5HBMAZU-@XI9lScJV|=|MqUu% zH-A+!=AkL_9Z#7|dezeju&pd#&9s*7!c*jISxXlG9=Th_d{x^plH`I7EKmVu zJJ^+!Q!IV+)X4Hy_`qCKcCJJ%0K;zvff2$?I-fomEtSjsZU77_(l_8DoBez6e~~}> zJ7fujxXqyTqg+IWFK{p79b`#hu?!yxoCOIr2M1;N)u7)BBF>;G5pR&*$YrES5XL0A z8a#v-lT;|4Ck-VZyxR!VG-S-zGQ9?Q7g|XsWP)AvVT)XKM#9f9tc#opCt%08jmgjsTAK~lT+I@s^`J~Ek%2@~vleL>hcgFgD6Bna0{38N`nT0s)d zmR15WR#uv!bj?06rpjN!NUCxfSS7VTp63~;ZKp*6i#$?M(rjUUzH-1zkvyM!V_pRR zou(d~6%vS4AfYPGB4h*>B*zH6fMDmDoUli#8oewdWNw^#PGJ3~a5J?u;8K_;UUI&w zf%^G>R^{teZL|io-4+E{@ev$vB-#w{$*4;f-G%5k@r$Ipx*M(-Sq4s4e-kD|YL>tR zFAz-BMDYvc%^LTt9y)V+sM#S)_sLqgI#A~zuh;%k14S*p*(Q1fGODzb!?A|tStL>; zBSeKbwqZ-$;IrT`PH+Zaf~&eI-5SP52&L@xVd=Jj#hbWFx;>0dH$j|C_x3dXq=ADqXY0#u0<81BdhT!Mr7U+rNTK-r^nPI-iusjEg?O|4i;I8kOOEDG`%l z{}NhS{CG)z{+CNm1N_?3rTI0NN&x?bQYB$t%n9JltEh3h)D+ zKD9_=7Yd^!+1FWt!{n{b+OGEP(*G=|b-IRITH6&G1zWX(eppyxu88bxZ|_~VJhhL5 zXJ2ahy59EooskN&)ONjU?~?Y`mSMOI<}yewVmQ05;lGg2yYk7VqdN_D`^)ZpM7h`9 z4Scls1hRbogpBpndzLS3)!*l*MH*%>yy0^dKWAcOvZn`@(DvqIL9DkMeEa!cubcv^ zP#e@-YsuMFb~4%fU3`bkKHf=75Sx_b1!62Kn z{UbD+Z|sk#XRVvO+P@xVf7u_-N%fq6K#B1eq+FOfW(slsQ3et+uHlR?6W}JG6m{ZRb$_!^Fv<=V4p>8ktSfx8@%JzP+X` zohSMUQb0bi0y7>+$q3hlR0^NP2RuN@SfAw#qC)~2_9e1ER_Kgp?z2Kh$bC2MneL%k z7*Z`b9Bu6XHw?(2@TG|+53oAg8n69g$^G@ZgOpbi*AQ6TONb$NS(yf{k zn!80cMTd(u#m9>OyF^psEm>G{TDwxaUwdB1=oaes=)R+SSFh0@&`;^_86t*_hKq(f z#-y>+IAVO?c-8o+Y0&hBxzK#v{Ens2QfC>m{Ky)xZnb%ATW!Cw{mt&N585Z}uiLNK z?>fAWZ#go~fb-j~fUDcJ)Adhj*A2JZ{iOT*?rWv?($>-o9<%4~JRg^BEc;g3)iTK& z@pgLO^V)e`cKP!1$^1F$bBV@_sD$>*vS#_aa^E=5iMYHeFplE%Y7F5EpopA`uk}=4Fn1> zGp~$L0wN{%HSi7U%oCf&cZ`pZp(oHLG>&#a8%JY|9lb{Fkm6F)phWx_MOpHze zkYk8A%m&H(UgCk#6Kw5mg!9#5dtm(ZQCzzYaNqzNJ%5Z!$Bv95&wVvW)67QL^s}EH z!$n+UvZ8DlKWp$En>aa(Yfk`hUu0u8KYwBT_-97@0S=i_=vFqafBrX36Qi@Z@i4AK zP#NEbUrvrr9KkhQ=Mrp$e*O0!P8~mS674y^PRHYn*8w$?bU{7;jqEu%%6|in(gD(` zulu0=xApWNh>ve_PSMxMkWthDFM<^ubje;!lUz6~_N3Mz)zg#W@x3aF1SE-wvIsnQ zvl73|JLoB{h1Ah+bKW^*=S1KDj3mT?3=EV}0WK?NGrY`a5T{qLNI+x`R&jy7Nrn+K zQWlVGP7O^CBjCkVh9ijMnTiU|&LayE8J_WNoF0DI6<*YZT|(dVbRQ?O_Z|AtcXfM7h9>mY4GOE`O3rR1I#|Pqpd&Qq%wrx$q%eK%aN4flVp zK=a*|f~_daY7VRVHJiVL_)F$?6_(BSBK|;OrJnM)_zC)W;UkEb3pIR)r5)ij7N1_p zR(j4-=gi2GD2lSg2_h82dlhQ(CA7g1a%y{}L=y1ziaS${^PyL?Q^RaR|6GDuaQ>ZYNPg$nO$j zK3|LthFbR~Z3~^k9J&zNs0#ai=pPY&D!fgF-wkKzOCc{k6^XEV=7ByF2^WDV;m=)y zu0~isSfT-yF&XVb_|d3Vx{J|ia-J5)HYxaNIuH|xmwr8_;V;F85cb9!L!z1ztnIK$ z(poMDXYtTE%V+3h-0R6`Wwve$gkUQ`j4s3 z|E@z@Tw`^6XApY_UG!j$+jke)s)$q^2-x5xY!34^vmEyx&96;iw|E^FHrFyU-mBfN zUJ`szPWz31+LcJ6x}iZ2{aV7Vs{BIYBWwl>btm(3!ST^vV~}pFXCY^&#Z&dp!W^a; z&w&=$L0_tGi{dp$s>tC0mfaG% zK%l1@YKjFh_n?t^+D(JL(y#+{kQ-bEyrwXVyvcs_`$I_&E-0)|CL>M(%|-`tq97_l zbAG*?Y~i@ebTz3ReBQIjb;gcmNd}3aj#zVd`gNwUJ}4-~xW16hU68INXx`=OI{fwj%6Z=T+5JrYZ+T7DMFjpD~Zmu z)S&!lTfFKSeyN4^E>o*k+=*6pE}v-CiuYXWUUDy8Y1Mk0tL-S#Kh{>S>ibgL0J^}~ zUatA1xjo?(WT|zB3_Dt7i6}lL5K~L+Z2JiE|FT^xDa*z>ggZ96*K>7D@y+Kpeje9Z zuDUlhuj5+jh|^DBN&D$nH#KtDV>^YE98AB~!?bL|h?QEM9 zr~6n=;Z5%n$@ERH!gOEe4i)tnQqU8;3))E`iD^v>@1IBIbMiTnr6NHG;=K@-mX=sT zYJ~!N@B@BVAS>gxkVrUQo*=2F<_#UBG3j#qiL-dlkmsyEA7e3`OE>PI>sH%So@XDichSB$+4oKxXTr@l9qg1zP`{{Sre!! zEG*8mnSxHUGv8=*Ma{Nwi^~}_o6Thbe^+g)$8~4wwr!~>MaeQKS8fn<-ns2sdUi`w z5WP=?Tm;xmBw`l7hVaWFiwQ`uz^-n22SfI+TMw)8F532UEb6lD9?EZTQ`a_c{{=Zo zgF8l5ICqKgbtP4JPZzsX{JKk{kMCSW_^q8=HJRNq!uB35r)GNCiTT|gt*CDH+#vb% z`}b%%RPEZ2cyU+AD6;+sacHG4?5d>KcQx29$RAfXczr{gI=1OedhQl=^#zMMecFL^rf&|D`IA1ayCnL%aii1yQAN>5>0*Dg-@dWK z_D5k&VqcB~-}s#-fYJn&hV)T?PYgZSlj25hdSdvADYB2Hm}hYW-HH#MuWD%@-QA6? z;KJ^p>Vibb*v6VkuWq^q3Y2)m_Yck!BUm+ znZZRuCTQoqFBatX^+6i;D6FpgPAjbJ+|$t)@2gc=Zro=l9QgzN-=Qg0=+T2akn&px zBmFrUCj1ol;awunjM& z#k6NC#fho7XY~U0A99<(u=MQQ3^DBoQ!>zl98&p-!4nK>q|NitbycdGr zbFP2>{OS9x8~*?Wc=+OTe{@*tn`{|3-6AwH|Wig%*BpM{(x zb&+lw`QgRjXqeD5BWBt=mZ8_j9-I?Q1Ez7)6Q-4d#eyqlow?uqr1=f=P4jBufx@>f z2}_q{*76;z$=YFk!uogC7i|VxsV!-H&bDG7u)lBrxM-j#Q}jH3X3RKVE*>v_+39t* zImeyPIVIWU=SrlKk39*`xaT#`syFC8=A9q&nS6(R*Gnr( zUn*TH^OyCOohf_T@A9|!#{yj7TySG>F=P&9!pZPwBbLZLk!PY!(fR0)qQ8$-#l~VQ z@kD$+{(gD5{J$#v727MPDwnHXsJdBQQaxP#YE6yekOi7JTsh}NC*<72K_1*t=R7!J zM4jt!{(tIRk6)E(b#B1*XVtk0yd;U)O>{r&)HXCv%|ahc!4a5*TG$0!U?2Y7 z3#VZM#^5-N!wG~N@c&;^FgtK$=EQ@?CxfX(0}LR`3?d&yqDcrs3KB5a@F_R=l { + this.openmct.indicators.getIndicatorObjectsByPriority().forEach(this.addIndicator); + + this.openmct.indicators.on('addIndicator', this.addIndicator); + }, + methods: { + addIndicator(indicator) { this.$el.appendChild(indicator.element); - }); + } } + }; diff --git a/src/utils/raf.js b/src/utils/raf.js new file mode 100644 index 0000000000..d5c0c48fe5 --- /dev/null +++ b/src/utils/raf.js @@ -0,0 +1,14 @@ +export default function raf(callback) { + let rendering = false; + + return () => { + if (!rendering) { + rendering = true; + + requestAnimationFrame(() => { + callback(); + rendering = false; + }); + } + }; +} diff --git a/src/utils/rafSpec.js b/src/utils/rafSpec.js new file mode 100644 index 0000000000..0bf5ae9d9c --- /dev/null +++ b/src/utils/rafSpec.js @@ -0,0 +1,61 @@ +import raf from "./raf"; + +describe('The raf utility function', () => { + it('Throttles function calls that arrive in quick succession using Request Animation Frame', () => { + const unthrottledFunction = jasmine.createSpy('unthrottledFunction'); + const throttledCallback = jasmine.createSpy('throttledCallback'); + const throttledFunction = raf(throttledCallback); + + for (let i = 0; i < 10; i++) { + unthrottledFunction(); + throttledFunction(); + } + + return new Promise((resolve) => { + requestAnimationFrame(resolve); + }).then(() => { + expect(unthrottledFunction).toHaveBeenCalledTimes(10); + expect(throttledCallback).toHaveBeenCalledTimes(1); + }); + }); + it('Only invokes callback once per animation frame', () => { + const throttledCallback = jasmine.createSpy('throttledCallback'); + const throttledFunction = raf(throttledCallback); + + for (let i = 0; i < 10; i++) { + throttledFunction(); + } + + return new Promise(resolve => { + requestAnimationFrame(resolve); + }).then(() => { + return new Promise(resolve => { + requestAnimationFrame(resolve); + }); + }).then(() => { + expect(throttledCallback).toHaveBeenCalledTimes(1); + }); + }); + it('Invokes callback again if called in subsequent animation frame', () => { + const throttledCallback = jasmine.createSpy('throttledCallback'); + const throttledFunction = raf(throttledCallback); + + for (let i = 0; i < 10; i++) { + throttledFunction(); + } + + return new Promise(resolve => { + requestAnimationFrame(resolve); + }).then(() => { + for (let i = 0; i < 10; i++) { + throttledFunction(); + } + + return new Promise(resolve => { + requestAnimationFrame(resolve); + }); + }).then(() => { + expect(throttledCallback).toHaveBeenCalledTimes(2); + }); + }); +}); From 04ee6f49d623c88aab6169d6615c402574d12c57 Mon Sep 17 00:00:00 2001 From: Alize Nguyen Date: Thu, 2 Jun 2022 17:47:14 -0500 Subject: [PATCH 212/283] Remove all non legacy usage of zepto (#5159) * Removed Zepto * Added utility functions for compiling HTML templates and toggling classes on and off Co-authored-by: John Hill Co-authored-by: Andrew Henry --- package.json | 4 +- src/api/indicators/SimpleIndicator.js | 10 +- .../URLIndicatorPlugin/URLIndicator.js | 23 +-- .../URLIndicatorPlugin/URLIndicatorSpec.js | 41 ++--- .../autoflow/AutoflowTabularPluginSpec.js | 27 ++- .../licenses/third-party-licenses.json | 7 - src/plugins/summaryWidget/src/Condition.js | 41 +++-- .../summaryWidget/src/ConditionManager.js | 12 +- src/plugins/summaryWidget/src/Rule.js | 154 +++++++++++------- .../summaryWidget/src/SummaryWidget.js | 71 ++++---- src/plugins/summaryWidget/src/TestDataItem.js | 28 ++-- .../summaryWidget/src/TestDataManager.js | 18 +- src/plugins/summaryWidget/src/WidgetDnD.js | 27 +-- .../summaryWidget/src/input/ColorPalette.js | 21 +-- .../summaryWidget/src/input/IconPalette.js | 30 ++-- .../summaryWidget/src/input/Palette.js | 71 +++++--- src/plugins/summaryWidget/src/input/Select.js | 48 +++--- .../summaryWidget/test/ConditionSpec.js | 60 +++++-- src/plugins/summaryWidget/test/RuleSpec.js | 29 ++-- .../summaryWidget/test/SummaryWidgetSpec.js | 12 +- .../summaryWidget/test/TestDataItemSpec.js | 53 ++++-- .../summaryWidget/test/TestDataManagerSpec.js | 8 +- src/utils/template/templateHelpers.js | 14 ++ webpack.common.js | 7 - 24 files changed, 477 insertions(+), 339 deletions(-) create mode 100644 src/utils/template/templateHelpers.js diff --git a/package.json b/package.json index c1aac194e6..a62dd41924 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "eslint-plugin-vue": "8.5.0", "eslint-plugin-you-dont-need-lodash-underscore": "6.12.0", "eventemitter3": "1.2.0", - "exports-loader": "0.7.0", "express": "4.13.1", "file-saver": "2.0.5", "git-rev-sync": "3.0.2", @@ -74,8 +73,7 @@ "webpack-cli": "4.9.2", "webpack-dev-middleware": "5.3.3", "webpack-hot-middleware": "2.25.1", - "webpack-merge": "5.8.0", - "zepto": "1.2.0" + "webpack-merge": "5.8.0" }, "scripts": { "clean": "rm -rf ./dist ./node_modules ./package-lock.json", diff --git a/src/api/indicators/SimpleIndicator.js b/src/api/indicators/SimpleIndicator.js index 1ef99e6888..31ce745a52 100644 --- a/src/api/indicators/SimpleIndicator.js +++ b/src/api/indicators/SimpleIndicator.js @@ -22,6 +22,7 @@ import EventEmitter from 'EventEmitter'; import indicatorTemplate from './res/indicator-template.html'; +import { convertTemplateToHTML } from '@/utils/template/templateHelpers'; const DEFAULT_ICON_CLASS = 'icon-info'; @@ -30,7 +31,7 @@ class SimpleIndicator extends EventEmitter { super(); this.openmct = openmct; - this.element = compileTemplate(indicatorTemplate)[0]; + this.element = convertTemplateToHTML(indicatorTemplate)[0]; this.priority = openmct.priority.DEFAULT; this.textElement = this.element.querySelector('.js-indicator-text'); @@ -116,11 +117,4 @@ class SimpleIndicator extends EventEmitter { } } -function compileTemplate(htmlTemplate) { - const templateNode = document.createElement('template'); - templateNode.innerHTML = htmlTemplate; - - return templateNode.content.cloneNode(true).children; -} - export default SimpleIndicator; diff --git a/src/plugins/URLIndicatorPlugin/URLIndicator.js b/src/plugins/URLIndicatorPlugin/URLIndicator.js index 1bc83450e9..5a6785e54b 100644 --- a/src/plugins/URLIndicatorPlugin/URLIndicator.js +++ b/src/plugins/URLIndicatorPlugin/URLIndicator.js @@ -20,10 +20,8 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define( - ['zepto'], - function ($) { - +define([], + function () { // Set of connection states; changing among these states will be // reflected in the indicator's appearance. // CONNECTED: Everything nominal, expect to be able to read/write. @@ -75,12 +73,17 @@ define( }; URLIndicator.prototype.fetchUrl = function () { - $.ajax({ - type: 'GET', - url: this.URLpath, - success: this.handleSuccess, - error: this.handleError - }); + fetch(this.URLpath) + .then(response => { + if (response.ok) { + this.handleSuccess(); + } else { + this.handleError(); + } + }) + .catch(error => { + this.handleError(); + }); }; URLIndicator.prototype.handleError = function (e) { diff --git a/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js b/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js index 408a98cd09..cf6cd39ff6 100644 --- a/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js +++ b/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js @@ -25,37 +25,35 @@ define( "utils/testing", "./URLIndicator", "./URLIndicatorPlugin", - "../../MCT", - "zepto" + "../../MCT" ], function ( testingUtils, URLIndicator, URLIndicatorPlugin, - MCT, - $ + MCT ) { - const defaultAjaxFunction = $.ajax; - describe("The URLIndicator", function () { let openmct; let indicatorElement; let pluginOptions; - let ajaxOptions; let urlIndicator; // eslint-disable-line + let fetchSpy; beforeEach(function () { jasmine.clock().install(); openmct = new testingUtils.createOpenMct(); spyOn(openmct.indicators, 'add'); - spyOn($, 'ajax'); - $.ajax.and.callFake(function (options) { - ajaxOptions = options; - }); + fetchSpy = spyOn(window, 'fetch').and.callFake(() => Promise.resolve({ + ok: true + })); }); afterEach(function () { - $.ajax = defaultAjaxFunction; + if (window.fetch.restore) { + window.fetch.restore(); + } + jasmine.clock().uninstall(); return testingUtils.resetApplicationState(openmct); @@ -96,11 +94,11 @@ define( expect(indicatorElement.classList.contains('iconClass-checked')).toBe(true); }); it("uses custom interval", function () { - expect($.ajax.calls.count()).toEqual(1); + expect(window.fetch).toHaveBeenCalledTimes(1); jasmine.clock().tick(1); - expect($.ajax.calls.count()).toEqual(1); + expect(window.fetch).toHaveBeenCalledTimes(1); jasmine.clock().tick(pluginOptions.interval + 1); - expect($.ajax.calls.count()).toEqual(2); + expect(window.fetch).toHaveBeenCalledTimes(2); }); it("uses custom label if supplied in initialization", function () { expect(indicatorElement.textContent.indexOf(pluginOptions.label) >= 0).toBe(true); @@ -120,18 +118,21 @@ define( it("requests the provided URL", function () { jasmine.clock().tick(pluginOptions.interval + 1); - expect(ajaxOptions.url).toEqual(pluginOptions.url); + expect(window.fetch).toHaveBeenCalledWith(pluginOptions.url); }); - it("indicates success if connection is nominal", function () { + it("indicates success if connection is nominal", async function () { jasmine.clock().tick(pluginOptions.interval + 1); - ajaxOptions.success(); + await urlIndicator.fetchUrl(); expect(indicatorElement.classList.contains('s-status-on')).toBe(true); }); - it("indicates an error when the server cannot be reached", function () { + it("indicates an error when the server cannot be reached", async function () { + fetchSpy.and.callFake(() => Promise.resolve({ + ok: false + })); jasmine.clock().tick(pluginOptions.interval + 1); - ajaxOptions.error(); + await urlIndicator.fetchUrl(); expect(indicatorElement.classList.contains('s-status-warning-hi')).toBe(true); }); }); diff --git a/src/plugins/autoflow/AutoflowTabularPluginSpec.js b/src/plugins/autoflow/AutoflowTabularPluginSpec.js index ce20cab1d6..5e5d49489d 100644 --- a/src/plugins/autoflow/AutoflowTabularPluginSpec.js +++ b/src/plugins/autoflow/AutoflowTabularPluginSpec.js @@ -21,7 +21,6 @@ *****************************************************************************/ import AutoflowTabularPlugin from './AutoflowTabularPlugin'; import AutoflowTabularConstants from './AutoflowTabularConstants'; -import $ from 'zepto'; import DOMObserver from './dom-observer'; import { createOpenMct, @@ -122,7 +121,7 @@ xdescribe("AutoflowTabularPlugin", () => { name: "Object " + key }; }); - testContainer = $('
')[0]; + testContainer = document.createElement('div'); domObserver = new DOMObserver(testContainer); testHistories = testKeys.reduce((histories, key, index) => { @@ -195,7 +194,7 @@ xdescribe("AutoflowTabularPlugin", () => { describe("when rows have been populated", () => { function rowsMatch() { - const rows = $(testContainer).find(".l-autoflow-row").length; + const rows = testContainer.querySelectorAll(".l-autoflow-row").length; return rows === testChildren.length; } @@ -241,20 +240,20 @@ xdescribe("AutoflowTabularPlugin", () => { const nextWidth = initialWidth + AutoflowTabularConstants.COLUMN_WIDTH_STEP; - expect($(testContainer).find('.l-autoflow-col').css('width')) + expect(testContainer.querySelector('.l-autoflow-col').css('width')) .toEqual(initialWidth + 'px'); - $(testContainer).find('.change-column-width').click(); + testContainer.querySelector('.change-column-width').click(); function widthHasChanged() { - const width = $(testContainer).find('.l-autoflow-col').css('width'); + const width = testContainer.querySelector('.l-autoflow-col').css('width'); return width !== initialWidth + 'px'; } return domObserver.when(widthHasChanged) .then(() => { - expect($(testContainer).find('.l-autoflow-col').css('width')) + expect(testContainer.querySelector('.l-autoflow-col').css('width')) .toEqual(nextWidth + 'px'); }); }); @@ -267,13 +266,13 @@ xdescribe("AutoflowTabularPlugin", () => { it("displays historical telemetry", () => { function rowTextDefined() { - return $(testContainer).find(".l-autoflow-item").filter(".r").text() !== ""; + return testContainer.querySelector(".l-autoflow-item").filter(".r").text() !== ""; } return domObserver.when(rowTextDefined).then(() => { testKeys.forEach((key, index) => { const datum = testHistories[key]; - const $cell = $(testContainer).find(".l-autoflow-row").eq(index).find(".r"); + const $cell = testContainer.querySelector(".l-autoflow-row").eq(index).find(".r"); expect($cell.text()).toEqual(String(datum.range)); }); }); @@ -294,7 +293,7 @@ xdescribe("AutoflowTabularPlugin", () => { return waitsForChange().then(() => { testData.forEach((datum, index) => { - const $cell = $(testContainer).find(".l-autoflow-row").eq(index).find(".r"); + const $cell = testContainer.querySelector(".l-autoflow-row").eq(index).find(".r"); expect($cell.text()).toEqual(String(datum.range)); }); }); @@ -312,7 +311,7 @@ xdescribe("AutoflowTabularPlugin", () => { return waitsForChange().then(() => { testKeys.forEach((datum, index) => { - const $cell = $(testContainer).find(".l-autoflow-row").eq(index).find(".r"); + const $cell = testContainer.querySelector(".l-autoflow-row").eq(index).find(".r"); expect($cell.hasClass(testClass)).toBe(true); }); }); @@ -322,16 +321,16 @@ xdescribe("AutoflowTabularPlugin", () => { const rowHeight = AutoflowTabularConstants.ROW_HEIGHT; const sliderHeight = AutoflowTabularConstants.SLIDER_HEIGHT; const count = testKeys.length; - const $container = $(testContainer); + const $container = testContainer; let promiseChain = Promise.resolve(); function columnsHaveAutoflowed() { - const itemsHeight = $container.find('.l-autoflow-items').height(); + const itemsHeight = $container.querySelector('.l-autoflow-items').height(); const availableHeight = itemsHeight - sliderHeight; const availableRows = Math.max(Math.floor(availableHeight / rowHeight), 1); const columns = Math.ceil(count / availableRows); - return $container.find('.l-autoflow-col').length === columns; + return $container.querySelector('.l-autoflow-col').length === columns; } $container.find('.abs').css({ diff --git a/src/plugins/licenses/third-party-licenses.json b/src/plugins/licenses/third-party-licenses.json index 184024eb41..c139298ce1 100644 --- a/src/plugins/licenses/third-party-licenses.json +++ b/src/plugins/licenses/third-party-licenses.json @@ -256,13 +256,6 @@ "licenseFile": "/Users/akhenry/Code/licenses/node_modules/vue/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2013-present, Yuxi (Evan) You\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.", "copyright": "Copyright (c) 2013-present, Yuxi (Evan) You" - }, - "zepto@1.2.0": { - "licenses": "MIT", - "repository": "https://github.com/madrobby/zepto", - "path": "/Users/akhenry/Code/licenses/node_modules/zepto", - "licenseFile": "/Users/akhenry/Code/licenses/node_modules/zepto/README.md", - "licenseText": "Copyright (c) 2010-2018 Thomas Fuchs\nhttp://zeptojs.com/\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." } } diff --git a/src/plugins/summaryWidget/src/Condition.js b/src/plugins/summaryWidget/src/Condition.js index 997ad67b4f..66f9ecbeb2 100644 --- a/src/plugins/summaryWidget/src/Condition.js +++ b/src/plugins/summaryWidget/src/Condition.js @@ -4,16 +4,16 @@ define([ './input/KeySelect', './input/OperationSelect', './eventHelpers', - 'EventEmitter', - 'zepto' + '../../../utils/template/templateHelpers', + 'EventEmitter' ], function ( conditionTemplate, ObjectSelect, KeySelect, OperationSelect, eventHelpers, - EventEmitter, - $ + templateHelpers, + EventEmitter ) { /** * Represents an individual condition for a summary widget rule. Manages the @@ -31,12 +31,13 @@ define([ this.index = index; this.conditionManager = conditionManager; - this.domElement = $(conditionTemplate); + this.domElement = templateHelpers.convertTemplateToHTML(conditionTemplate)[0]; + this.eventEmitter = new EventEmitter(); this.supportedCallbacks = ['remove', 'duplicate', 'change']; - this.deleteButton = $('.t-delete', this.domElement); - this.duplicateButton = $('.t-duplicate', this.domElement); + this.deleteButton = this.domElement.querySelector('.t-delete'); + this.duplicateButton = this.domElement.querySelector('.t-duplicate'); this.selects = {}; this.valueInputs = []; @@ -105,9 +106,10 @@ define([ }); Object.values(this.selects).forEach(function (select) { - $('.t-configuration', self.domElement).append(select.getDOM()); + self.domElement.querySelector('.t-configuration').append(select.getDOM()); }); - this.listenTo($('.t-value-inputs', this.domElement), 'input', onValueInput); + + this.listenTo(this.domElement.querySelector('.t-value-inputs'), 'input', onValueInput); } Condition.prototype.getDOM = function (container) { @@ -132,7 +134,7 @@ define([ * Hide the appropriate inputs when this is the only condition */ Condition.prototype.hideButtons = function () { - this.deleteButton.hide(); + this.deleteButton.style.display = 'none'; }; /** @@ -172,14 +174,14 @@ define([ */ Condition.prototype.generateValueInputs = function (operation) { const evaluator = this.conditionManager.getEvaluator(); - const inputArea = $('.t-value-inputs', this.domElement); + const inputArea = this.domElement.querySelector('.t-value-inputs'); let inputCount; let inputType; let newInput; let index = 0; let emitChange = false; - inputArea.html(''); + inputArea.innerHTML = ''; this.valueInputs = []; this.config.values = this.config.values || []; @@ -189,17 +191,24 @@ define([ while (index < inputCount) { if (inputType === 'select') { - newInput = $(''); + const options = this.generateSelectOptions(); + + newInput = document.createElement("select"); + newInput.innerHTML = options; + emitChange = true; } else { const defaultValue = inputType === 'number' ? 0 : ''; const value = this.config.values[index] || defaultValue; this.config.values[index] = value; - newInput = $(''); + + newInput = document.createElement("input"); + newInput.type = `${inputType}`; + newInput.value = `${value}`; } - this.valueInputs.push(newInput.get(0)); - inputArea.append(newInput); + this.valueInputs.push(newInput); + inputArea.appendChild(newInput); index += 1; } diff --git a/src/plugins/summaryWidget/src/ConditionManager.js b/src/plugins/summaryWidget/src/ConditionManager.js index ff90bc7bc7..e502649030 100644 --- a/src/plugins/summaryWidget/src/ConditionManager.js +++ b/src/plugins/summaryWidget/src/ConditionManager.js @@ -2,13 +2,11 @@ define ([ './ConditionEvaluator', 'objectUtils', 'EventEmitter', - 'zepto', 'lodash' ], function ( ConditionEvaluator, objectUtils, EventEmitter, - $, _ ) { @@ -232,7 +230,10 @@ define ([ self.eventEmitter.emit('add', obj); - $('.w-summary-widget').removeClass('s-status-no-data'); + const summaryWidget = document.querySelector('.w-summary-widget'); + if (summaryWidget) { + summaryWidget.classList.remove('s-status-no-data'); + } } }; @@ -256,7 +257,10 @@ define ([ this.eventEmitter.emit('remove', identifier); if (_.isEmpty(this.compositionObjs)) { - $('.w-summary-widget').addClass('s-status-no-data'); + const summaryWidget = document.querySelector('.w-summary-widget'); + if (summaryWidget) { + summaryWidget.classList.add('s-status-no-data'); + } } }; diff --git a/src/plugins/summaryWidget/src/Rule.js b/src/plugins/summaryWidget/src/Rule.js index d9217f0e0c..0b8f28804f 100644 --- a/src/plugins/summaryWidget/src/Rule.js +++ b/src/plugins/summaryWidget/src/Rule.js @@ -4,18 +4,18 @@ define([ './input/ColorPalette', './input/IconPalette', './eventHelpers', + '../../../utils/template/templateHelpers', 'EventEmitter', - 'lodash', - 'zepto' + 'lodash' ], function ( ruleTemplate, Condition, ColorPalette, IconPalette, eventHelpers, + templateHelpers, EventEmitter, - _, - $ + _ ) { /** * An object representing a summary widget rule. Maintains a set of text @@ -41,7 +41,7 @@ define([ this.widgetDnD = widgetDnD; this.container = container; - this.domElement = $(ruleTemplate); + this.domElement = templateHelpers.convertTemplateToHTML(ruleTemplate)[0]; this.eventEmitter = new EventEmitter(); this.supportedCallbacks = ['remove', 'duplicate', 'change', 'conditionChange']; this.conditions = []; @@ -50,31 +50,32 @@ define([ this.remove = this.remove.bind(this); this.duplicate = this.duplicate.bind(this); - this.thumbnail = $('.t-widget-thumb', this.domElement); - this.thumbnailIcon = $('.js-sw__icon', this.domElement); - this.thumbnailLabel = $('.c-sw__label', this.domElement); - this.title = $('.rule-title', this.domElement); - this.description = $('.rule-description', this.domElement); - this.trigger = $('.t-trigger', this.domElement); - this.toggleConfigButton = $('.js-disclosure', this.domElement); - this.configArea = $('.widget-rule-content', this.domElement); - this.grippy = $('.t-grippy', this.domElement); - this.conditionArea = $('.t-widget-rule-config', this.domElement); - this.jsConditionArea = $('.t-rule-js-condition-input-holder', this.domElement); - this.deleteButton = $('.t-delete', this.domElement); - this.duplicateButton = $('.t-duplicate', this.domElement); - this.addConditionButton = $('.add-condition', this.domElement); + this.thumbnail = this.domElement.querySelector('.t-widget-thumb'); + this.thumbnailIcon = this.domElement.querySelector('.js-sw__icon'); + this.thumbnailLabel = this.domElement.querySelector('.c-sw__label'); + this.title = this.domElement.querySelector('.rule-title'); + this.description = this.domElement.querySelector('.rule-description'); + this.trigger = this.domElement.querySelector('.t-trigger'); + this.toggleConfigButton = this.domElement.querySelector('.js-disclosure'); + this.configArea = this.domElement.querySelector('.widget-rule-content'); + this.grippy = this.domElement.querySelector('.t-grippy'); + this.conditionArea = this.domElement.querySelector('.t-widget-rule-config'); + this.jsConditionArea = this.domElement.querySelector('.t-rule-js-condition-input-holder'); + this.deleteButton = this.domElement.querySelector('.t-delete'); + this.duplicateButton = this.domElement.querySelector('.t-duplicate'); + this.addConditionButton = this.domElement.querySelector('.add-condition'); /** * The text inputs for this rule: any input included in this object will * have the appropriate event handlers registered to it, and it's corresponding * field in the domain object will be updated with its value */ + this.textInputs = { - name: $('.t-rule-name-input', this.domElement), - label: $('.t-rule-label-input', this.domElement), - message: $('.t-rule-message-input', this.domElement), - jsCondition: $('.t-rule-js-condition-input', this.domElement) + name: this.domElement.querySelector('.t-rule-name-input'), + label: this.domElement.querySelector('.t-rule-label-input'), + message: this.domElement.querySelector('.t-rule-message-input'), + jsCondition: this.domElement.querySelector('.t-rule-js-condition-input') }; this.iconInput = new IconPalette('', container); @@ -94,7 +95,7 @@ define([ function onIconInput(icon) { self.config.icon = icon; self.updateDomainObject('icon', icon); - self.thumbnailIcon.removeClass().addClass(THUMB_ICON_CLASS + ' ' + icon); + self.thumbnailIcon.className = `${THUMB_ICON_CLASS + ' ' + icon}`; self.eventEmitter.emit('change'); } @@ -106,7 +107,7 @@ define([ */ function onColorInput(color, property) { self.config.style[property] = color; - self.thumbnail.css(property, color); + self.thumbnail.style[property] = color; self.eventEmitter.emit('change'); } @@ -116,7 +117,10 @@ define([ * @private */ function encodeMsg(msg) { - return $('
').text(msg).html(); + const div = document.createElement('div'); + div.innerText = msg; + + return div.innerText; } /** @@ -144,9 +148,9 @@ define([ self.config[inputKey] = text; self.updateDomainObject(); if (inputKey === 'name') { - self.title.html(text); + self.title.innerText = text; } else if (inputKey === 'label') { - self.thumbnailLabel.html(text); + self.thumbnailLabel.innerText = text; } self.eventEmitter.emit('change'); @@ -158,13 +162,14 @@ define([ * @private */ function onDragStart(event) { - $('.t-drag-indicator').each(function () { + document.querySelectorAll('.t-drag-indicator').forEach(indicator => { // eslint-disable-next-line no-invalid-this - $(this).html($('.widget-rule-header', self.domElement).clone().get(0)); + const ruleHeader = self.domElement.querySelectorAll('.widget-rule-header')[0].cloneNode(true); + indicator.innerHTML = ruleHeader; }); - self.widgetDnD.setDragImage($('.widget-rule-header', self.domElement).clone().get(0)); + self.widgetDnD.setDragImage(self.domElement.querySelectorAll('.widget-rule-header')[0].cloneNode(true)); self.widgetDnD.dragStart(self.config.id); - self.domElement.hide(); + self.domElement.style.display = 'none'; } /** @@ -172,20 +177,31 @@ define([ * @private */ function toggleConfig() { - self.configArea.toggleClass('expanded'); - self.toggleConfigButton.toggleClass('c-disclosure-triangle--expanded'); + if (self.configArea.classList.contains('expanded')) { + self.configArea.classList.remove('expanded'); + } else { + self.configArea.classList.add('expanded'); + } + + if (self.toggleConfigButton.classList.contains('c-disclosure-triangle--expanded')) { + self.toggleConfigButton.classList.remove('c-disclosure-triangle--expanded'); + } else { + self.toggleConfigButton.classList.add('c-disclosure-triangle--expanded'); + } + self.config.expanded = !self.config.expanded; } - $('.t-rule-label-input', this.domElement).before(this.iconInput.getDOM()); + const labelInput = this.domElement.querySelector('.t-rule-label-input'); + labelInput.parentNode.insertBefore(this.iconInput.getDOM(), labelInput); this.iconInput.set(self.config.icon); this.iconInput.on('change', function (value) { onIconInput(value); }); // Initialize thumbs when first loading - this.thumbnailIcon.removeClass().addClass(THUMB_ICON_CLASS + ' ' + self.config.icon); - this.thumbnailLabel.html(self.config.label); + this.thumbnailIcon.className = `${THUMB_ICON_CLASS + ' ' + self.config.icon}`; + this.thumbnailLabel.innerText = self.config.label; Object.keys(this.colorInputs).forEach(function (inputKey) { const input = self.colorInputs[inputKey]; @@ -198,15 +214,17 @@ define([ self.updateDomainObject(); }); - $('.t-style-input', self.domElement).append(input.getDOM()); + self.domElement.querySelector('.t-style-input').append(input.getDOM()); }); Object.keys(this.textInputs).forEach(function (inputKey) { - self.textInputs[inputKey].prop('value', self.config[inputKey] || ''); - self.listenTo(self.textInputs[inputKey], 'input', function () { - // eslint-disable-next-line no-invalid-this - onTextInput(this, inputKey); - }); + if (self.textInputs[inputKey]) { + self.textInputs[inputKey].value = self.config[inputKey] || ''; + self.listenTo(self.textInputs[inputKey], 'input', function () { + // eslint-disable-next-line no-invalid-this + onTextInput(this, inputKey); + }); + } }); this.listenTo(this.deleteButton, 'click', this.remove); @@ -217,15 +235,15 @@ define([ this.listenTo(this.toggleConfigButton, 'click', toggleConfig); this.listenTo(this.trigger, 'change', onTriggerInput); - this.title.html(self.config.name); - this.description.html(self.config.description); - this.trigger.prop('value', self.config.trigger); + this.title.innerHTML = self.config.name; + this.description.innerHTML = self.config.description; + this.trigger.value = self.config.trigger; this.listenTo(this.grippy, 'mousedown', onDragStart); this.widgetDnD.on('drop', function () { // eslint-disable-next-line no-invalid-this this.domElement.show(); - $('.t-drag-indicator').hide(); + document.querySelector('.t-drag-indicator').style.display = 'none'; }, this); if (!this.conditionManager.loadCompleted()) { @@ -233,21 +251,21 @@ define([ } if (!this.config.expanded) { - this.configArea.removeClass('expanded'); - this.toggleConfigButton.removeClass('c-disclosure-triangle--expanded'); + this.configArea.classList.remove('expanded'); + this.toggleConfigButton.classList.remove('c-disclosure-triangle--expanded'); } if (this.domainObject.configuration.ruleOrder.length === 2) { - $('.t-grippy', this.domElement).hide(); + this.domElement.querySelector('.t-grippy').style.display = 'none'; } this.refreshConditions(); //if this is the default rule, hide elements that don't apply if (this.config.id === 'default') { - $('.t-delete', this.domElement).hide(); - $('.t-widget-rule-config', this.domElement).hide(); - $('.t-grippy', this.domElement).hide(); + this.domElement.querySelector('.t-delete').style.display = 'none'; + this.domElement.querySelector('.t-widget-rule-config').style.display = 'none'; + this.domElement.querySelector('.t-grippy').style.display = 'none'; } } @@ -304,8 +322,8 @@ define([ * During a rule drag event, show the placeholder element after this rule */ Rule.prototype.showDragIndicator = function () { - $('.t-drag-indicator').hide(); - $('.t-drag-indicator', this.domElement).show(); + document.querySelector('.t-drag-indicator').style.display = 'none'; + this.domElement.querySelector('.t-drag-indicator').style.display = ''; }; /** @@ -397,7 +415,10 @@ define([ const triggerContextStr = self.config.trigger === 'any' ? ' or ' : ' and '; self.conditions = []; - $('.t-condition', this.domElement).remove(); + + this.domElement.querySelectorAll('.t-condition').forEach(condition => { + condition.remove(); + }); this.config.conditions.forEach(function (condition, index) { const newCondition = new Condition(condition, index, self.conditionManager); @@ -408,16 +429,23 @@ define([ }); if (this.config.trigger === 'js') { - this.jsConditionArea.show(); - this.addConditionButton.hide(); + if (this.jsConditionArea) { + this.jsConditionArea.style.display = ''; + } + + this.addConditionButton.style.display = 'none'; } else { - this.jsConditionArea.hide(); - this.addConditionButton.show(); + if (this.jsConditionArea) { + this.jsConditionArea.style.display = 'none'; + } + + this.addConditionButton.style.display = ''; self.conditions.forEach(function (condition) { $condition = condition.getDOM(); - $('li:last-of-type', self.conditionArea).before($condition); + const lastOfType = self.conditionArea.querySelector('li:last-of-type'); + lastOfType.parentNode.insertBefore($condition, lastOfType); if (loopCnt > 0) { - $('.t-condition-context', $condition).html(triggerContextStr + ' when'); + $condition.querySelector('.t-condition-context').innerHTML = triggerContextStr + ' when'; } loopCnt++; @@ -489,7 +517,7 @@ define([ } description = (description === '' ? this.config.description : description); - this.description.html(description); + this.description.innerHTML = self.config.description; this.config.description = description; }; diff --git a/src/plugins/summaryWidget/src/SummaryWidget.js b/src/plugins/summaryWidget/src/SummaryWidget.js index 1a5c1ceba0..e9c1442bf2 100644 --- a/src/plugins/summaryWidget/src/SummaryWidget.js +++ b/src/plugins/summaryWidget/src/SummaryWidget.js @@ -5,9 +5,9 @@ define([ './TestDataManager', './WidgetDnD', './eventHelpers', + '../../../utils/template/templateHelpers', 'objectUtils', 'lodash', - 'zepto', '@braintree/sanitize-url' ], function ( widgetTemplate, @@ -16,9 +16,9 @@ define([ TestDataManager, WidgetDnD, eventHelpers, + templateHelpers, objectUtils, _, - $, urlSanitizeLib ) { @@ -54,20 +54,22 @@ define([ this.activeId = 'default'; this.rulesById = {}; - this.domElement = $(widgetTemplate); - this.toggleRulesControl = $('.t-view-control-rules', this.domElement); - this.toggleTestDataControl = $('.t-view-control-test-data', this.domElement); - this.widgetButton = this.domElement.children('#widget'); + this.domElement = templateHelpers.convertTemplateToHTML(widgetTemplate)[0]; + this.toggleRulesControl = this.domElement.querySelector('.t-view-control-rules'); + this.toggleTestDataControl = this.domElement.querySelector('.t-view-control-test-data'); + + this.widgetButton = this.domElement.querySelector(':scope > #widget'); + this.editing = false; this.container = ''; - this.editListenerUnsubscribe = $.noop; + this.editListenerUnsubscribe = () => {}; - this.outerWrapper = $('.widget-edit-holder', this.domElement); - this.ruleArea = $('#ruleArea', this.domElement); - this.configAreaRules = $('.widget-rules-wrapper', this.domElement); + this.outerWrapper = this.domElement.querySelector('.widget-edit-holder'); + this.ruleArea = this.domElement.querySelector('#ruleArea'); + this.configAreaRules = this.domElement.querySelector('.widget-rules-wrapper'); - this.testDataArea = $('.widget-test-data', this.domElement); - this.addRuleButton = $('#addRule', this.domElement); + this.testDataArea = this.domElement.querySelector('.widget-test-data'); + this.addRuleButton = this.domElement.querySelector('#addRule'); this.conditionManager = new ConditionManager(this.domainObject, this.openmct); this.testDataManager = new TestDataManager(this.domainObject, this.conditionManager, this.openmct); @@ -87,8 +89,17 @@ define([ * @private */ function toggleTestData() { - self.outerWrapper.toggleClass('expanded-widget-test-data'); - self.toggleTestDataControl.toggleClass('c-disclosure-triangle--expanded'); + if (self.outerWrapper.classList.contains('expanded-widget-test-data')) { + self.outerWrapper.classList.remove('expanded-widget-test-data'); + } else { + self.outerWrapper.classList.add('expanded-widget-test-data'); + } + + if (self.toggleTestDataControl.classList.contains('c-disclosure-triangle--expanded')) { + self.toggleTestDataControl.classList.remove('c-disclosure-triangle--expanded'); + } else { + self.toggleTestDataControl.classList.add('c-disclosure-triangle--expanded'); + } } this.listenTo(this.toggleTestDataControl, 'click', toggleTestData); @@ -98,8 +109,8 @@ define([ * @private */ function toggleRules() { - self.outerWrapper.toggleClass('expanded-widget-rules'); - self.toggleRulesControl.toggleClass('c-disclosure-triangle--expanded'); + templateHelpers.toggleClass(self.outerWrapper, 'expanded-widget-rules'); + templateHelpers.toggleClass(self.toggleRulesControl, 'c-disclosure-triangle--expanded'); } this.listenTo(this.toggleRulesControl, 'click', toggleRules); @@ -113,15 +124,15 @@ define([ */ SummaryWidget.prototype.addHyperlink = function (url, openNewTab) { if (url) { - this.widgetButton.attr('href', urlSanitizeLib.sanitizeUrl(url)); + this.widgetButton.href = urlSanitizeLib.sanitizeUrl(url); } else { - this.widgetButton.removeAttr('href'); + this.widgetButton.removeAttribute('href'); } if (openNewTab === 'newTab') { - this.widgetButton.attr('target', '_blank'); + this.widgetButton.target = '_blank'; } else { - this.widgetButton.removeAttr('target'); + this.widgetButton.removeAttribute('target'); } }; @@ -149,8 +160,8 @@ define([ SummaryWidget.prototype.show = function (container) { const self = this; this.container = container; - $(container).append(this.domElement); - $('.widget-test-data', this.domElement).append(this.testDataManager.getDOM()); + this.container.append(this.domElement); + this.domElement.querySelector('.widget-test-data').append(this.testDataManager.getDOM()); this.widgetDnD = new WidgetDnD(this.domElement, this.domainObject.configuration.ruleOrder, this.rulesById); this.initRule('default', 'Default'); this.domainObject.configuration.ruleOrder.forEach(function (ruleId) { @@ -190,7 +201,7 @@ define([ const self = this; const ruleOrder = self.domainObject.configuration.ruleOrder; const rules = self.rulesById; - self.ruleArea.html(''); + self.ruleArea.innerHTML = ''; Object.values(ruleOrder).forEach(function (ruleId) { self.ruleArea.append(rules[ruleId].getDOM()); }); @@ -205,9 +216,9 @@ define([ rules.forEach(function (ruleKey, index, array) { if (array.length > 2 && index > 0) { - $('.t-grippy', rulesById[ruleKey].domElement).show(); + rulesById[ruleKey].domElement.querySelector('.t-grippy').style.display = ''; } else { - $('.t-grippy', rulesById[ruleKey].domElement).hide(); + rulesById[ruleKey].domElement.querySelector('.t-grippy').style.display = 'none'; } }); }; @@ -218,10 +229,10 @@ define([ SummaryWidget.prototype.updateWidget = function () { const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon'; const activeRule = this.rulesById[this.activeId]; - this.applyStyle($('#widget', this.domElement), activeRule.getProperty('style')); - $('#widget', this.domElement).prop('title', activeRule.getProperty('message')); - $('#widgetLabel', this.domElement).html(activeRule.getProperty('label')); - $('#widgetIcon', this.domElement).removeClass().addClass(WIDGET_ICON_CLASS + ' ' + activeRule.getProperty('icon')); + this.applyStyle(this.domElement.querySelector('#widget'), activeRule.getProperty('style')); + this.domElement.querySelector('#widget').title = activeRule.getProperty('message'); + this.domElement.querySelector('#widgetLabel').innerHTML = activeRule.getProperty('label'); + this.domElement.querySelector('#widgetIcon').classList = WIDGET_ICON_CLASS + ' ' + activeRule.getProperty('icon'); }; /** @@ -356,7 +367,7 @@ define([ */ SummaryWidget.prototype.applyStyle = function (elem, style) { Object.keys(style).forEach(function (propId) { - elem.css(propId, style[propId]); + elem.style[propId] = style[propId]; }); }; diff --git a/src/plugins/summaryWidget/src/TestDataItem.js b/src/plugins/summaryWidget/src/TestDataItem.js index 32b737a90e..ae005c46d0 100644 --- a/src/plugins/summaryWidget/src/TestDataItem.js +++ b/src/plugins/summaryWidget/src/TestDataItem.js @@ -3,15 +3,15 @@ define([ './input/ObjectSelect', './input/KeySelect', './eventHelpers', - 'EventEmitter', - 'zepto' + '../../../utils/template/templateHelpers', + 'EventEmitter' ], function ( itemTemplate, ObjectSelect, KeySelect, eventHelpers, - EventEmitter, - $ + templateHelpers, + EventEmitter ) { /** @@ -31,12 +31,12 @@ define([ this.index = index; this.conditionManager = conditionManager; - this.domElement = $(itemTemplate); + this.domElement = templateHelpers.convertTemplateToHTML(itemTemplate)[0]; this.eventEmitter = new EventEmitter(); this.supportedCallbacks = ['remove', 'duplicate', 'change']; - this.deleteButton = $('.t-delete', this.domElement); - this.duplicateButton = $('.t-duplicate', this.domElement); + this.deleteButton = this.domElement.querySelector('.t-delete'); + this.duplicateButton = this.domElement.querySelector('.t-duplicate'); this.selects = {}; this.valueInputs = []; @@ -101,7 +101,7 @@ define([ }); Object.values(this.selects).forEach(function (select) { - $('.t-configuration', self.domElement).append(select.getDOM()); + self.domElement.querySelector('.t-configuration').append(select.getDOM()); }); this.listenTo(this.domElement, 'input', onValueInput); } @@ -139,7 +139,7 @@ define([ * Hide the appropriate inputs when this is the only item */ TestDataItem.prototype.hideButtons = function () { - this.deleteButton.hide(); + this.deleteButton.style.display = 'none'; }; /** @@ -177,17 +177,21 @@ define([ */ TestDataItem.prototype.generateValueInput = function (key) { const evaluator = this.conditionManager.getEvaluator(); - const inputArea = $('.t-value-inputs', this.domElement); + const inputArea = this.domElement.querySelector('.t-value-inputs'); const dataType = this.conditionManager.getTelemetryPropertyType(this.config.object, key); const inputType = evaluator.getInputTypeById(dataType); - inputArea.html(''); + inputArea.innerHTML = ''; if (inputType) { if (!this.config.value) { this.config.value = (inputType === 'number' ? 0 : ''); } - this.valueInput = $(' ').get(0); + const newInput = document.createElement("input"); + newInput.type = `${inputType}`; + newInput.value = `${this.config.value}`; + + this.valueInput = newInput; inputArea.append(this.valueInput); } }; diff --git a/src/plugins/summaryWidget/src/TestDataManager.js b/src/plugins/summaryWidget/src/TestDataManager.js index 819cc5ee3f..70240453d6 100644 --- a/src/plugins/summaryWidget/src/TestDataManager.js +++ b/src/plugins/summaryWidget/src/TestDataManager.js @@ -2,13 +2,13 @@ define([ './eventHelpers', '../res/testDataTemplate.html', './TestDataItem', - 'zepto', + '../../../utils/template/templateHelpers', 'lodash' ], function ( eventHelpers, testDataTemplate, TestDataItem, - $, + templateHelpers, _ ) { @@ -28,13 +28,13 @@ define([ this.openmct = openmct; this.evaluator = this.manager.getEvaluator(); - this.domElement = $(testDataTemplate); + this.domElement = templateHelpers.convertTemplateToHTML(testDataTemplate)[0]; this.config = this.domainObject.configuration.testDataConfig; this.testCache = {}; - this.itemArea = $('.t-test-data-config', this.domElement); - this.addItemButton = $('.add-test-condition', this.domElement); - this.testDataInput = $('.t-test-data-checkbox', this.domElement); + this.itemArea = this.domElement.querySelector('.t-test-data-config'); + this.addItemButton = this.domElement.querySelector('.add-test-condition'); + this.testDataInput = this.domElement.querySelector('.t-test-data-checkbox'); /** * Toggles whether the associated {ConditionEvaluator} uses the actual @@ -139,7 +139,10 @@ define([ } self.items = []; - $('.t-test-data-item', this.domElement).remove(); + + this.domElement.querySelectorAll('.t-test-data-item').forEach(item => { + item.remove(); + }); this.config.forEach(function (item, index) { const newItem = new TestDataItem(item, index, self.manager); @@ -150,7 +153,6 @@ define([ }); self.items.forEach(function (item) { - // $('li:last-of-type', self.itemArea).before(item.getDOM()); self.itemArea.prepend(item.getDOM()); }); diff --git a/src/plugins/summaryWidget/src/WidgetDnD.js b/src/plugins/summaryWidget/src/WidgetDnD.js index e9ee2f0400..90cd3b6971 100644 --- a/src/plugins/summaryWidget/src/WidgetDnD.js +++ b/src/plugins/summaryWidget/src/WidgetDnD.js @@ -1,11 +1,11 @@ define([ '../res/ruleImageTemplate.html', 'EventEmitter', - 'zepto' + '../../../utils/template/templateHelpers' ], function ( ruleImageTemplate, EventEmitter, - $ + templateHelpers ) { /** @@ -19,8 +19,8 @@ define([ this.ruleOrder = ruleOrder; this.rulesById = rulesById; - this.imageContainer = $(ruleImageTemplate); - this.image = $('.t-drag-rule-image', this.imageContainer); + this.imageContainer = templateHelpers.convertTemplateToHTML(ruleImageTemplate)[0]; + this.image = this.imageContainer.querySelector('.t-drag-rule-image'); this.draggingId = ''; this.draggingRulePrevious = ''; this.eventEmitter = new EventEmitter(); @@ -29,18 +29,18 @@ define([ this.drag = this.drag.bind(this); this.drop = this.drop.bind(this); - $(this.container).on('mousemove', this.drag); - $(document).on('mouseup', this.drop); - $(this.container).before(this.imageContainer); - $(this.imageContainer).hide(); + this.container.addEventListener('mousemove', this.drag); + document.addEventListener('mouseup', this.drop); + this.container.parentNode.insertBefore(this.imageContainer, this.container); + this.imageContainer.style.display = 'none'; } /** * Remove event listeners registered to elements external to the widget */ WidgetDnD.prototype.destroy = function () { - $(this.container).off('mousemove', this.drag); - $(document).off('mouseup', this.drop); + this.container.removeEventListener('mousemove', this.drag); + document.removeEventListener('mouseup', this.drop); }; /** @@ -81,7 +81,8 @@ define([ let target = ''; ruleOrder.forEach(function (ruleId, index) { - offset = rulesById[ruleId].getDOM().offset(); + const ruleDOM = rulesById[ruleId].getDOM(); + offset = window.innerWidth - (ruleDOM.offsetLeft + ruleDOM.offsetWidth); y = offset.top; height = offset.height; if (index === 0) { @@ -114,7 +115,7 @@ define([ this.imageContainer.show(); this.imageContainer.offset({ top: event.pageY - this.image.height() / 2, - left: event.pageX - $('.t-grippy', this.image).width() + left: event.pageX - this.image.querySelector('.t-grippy').style.width }); }; @@ -129,7 +130,7 @@ define([ dragTarget = this.getDropLocation(event); this.imageContainer.offset({ top: event.pageY - this.image.height() / 2, - left: event.pageX - $('.t-grippy', this.image).width() + left: event.pageX - this.image.querySelector('.t-grippy').style.width }); if (this.rulesById[dragTarget]) { this.rulesById[dragTarget].showDragIndicator(); diff --git a/src/plugins/summaryWidget/src/input/ColorPalette.js b/src/plugins/summaryWidget/src/input/ColorPalette.js index 0bbe236419..2319f98304 100644 --- a/src/plugins/summaryWidget/src/input/ColorPalette.js +++ b/src/plugins/summaryWidget/src/input/ColorPalette.js @@ -1,10 +1,8 @@ define([ - './Palette', - 'zepto' + './Palette' ], function ( - Palette, - $ + Palette ) { //The colors that will be used to instantiate this palette if none are provided @@ -33,17 +31,16 @@ function ( this.palette.setNullOption('rgba(0,0,0,0)'); - const domElement = $(this.palette.getDOM()); + const domElement = this.palette.getDOM(); const self = this; - $('.c-button--menu', domElement).addClass('c-button--swatched'); - $('.t-swatch', domElement).addClass('color-swatch'); - $('.c-palette', domElement).addClass('c-palette--color'); + domElement.querySelector('.c-button--menu').classList.add('c-button--swatched'); + domElement.querySelector('.t-swatch').classList.add('color-swatch'); + domElement.querySelector('.c-palette').classList.add('c-palette--color'); - $('.c-palette__item', domElement).each(function () { + domElement.querySelectorAll('.c-palette__item').forEach(item => { // eslint-disable-next-line no-invalid-this - const elem = this; - $(elem).css('background-color', elem.dataset.item); + item.style.backgroundColor = item.dataset.item; }); /** @@ -53,7 +50,7 @@ function ( */ function updateSwatch() { const color = self.palette.getCurrent(); - $('.color-swatch', domElement).css('background-color', color); + domElement.querySelector('.color-swatch').style.backgroundColor = color; } this.palette.on('change', updateSwatch); diff --git a/src/plugins/summaryWidget/src/input/IconPalette.js b/src/plugins/summaryWidget/src/input/IconPalette.js index cdc011d5da..557cc4d958 100644 --- a/src/plugins/summaryWidget/src/input/IconPalette.js +++ b/src/plugins/summaryWidget/src/input/IconPalette.js @@ -1,9 +1,7 @@ define([ - './Palette', - 'zepto' + './Palette' ], function ( - Palette, - $ + Palette ) { //The icons that will be used to instantiate this palette if none are provided const DEFAULT_ICONS = [ @@ -45,20 +43,19 @@ define([ this.icons = icons || DEFAULT_ICONS; this.palette = new Palette(cssClass, container, this.icons); - this.palette.setNullOption(' '); - this.oldIcon = this.palette.current || ' '; + this.palette.setNullOption(''); + this.oldIcon = this.palette.current || ''; - const domElement = $(this.palette.getDOM()); + const domElement = this.palette.getDOM(); const self = this; - $('.c-button--menu', domElement).addClass('c-button--swatched'); - $('.t-swatch', domElement).addClass('icon-swatch'); - $('.c-palette', domElement).addClass('c-palette--icon'); + domElement.querySelector('.c-button--menu').classList.add('c-button--swatched'); + domElement.querySelector('.t-swatch').classList.add('icon-swatch'); + domElement.querySelector('.c-palette').classList.add('c-palette--icon'); - $('.c-palette-item', domElement).each(function () { + domElement.querySelectorAll('.c-palette-item').forEach(item => { // eslint-disable-next-line no-invalid-this - const elem = this; - $(elem).addClass(elem.dataset.item); + item.classList.add(item.dataset.item); }); /** @@ -67,8 +64,11 @@ define([ * @private */ function updateSwatch() { - $('.icon-swatch', domElement).removeClass(self.oldIcon) - .addClass(self.palette.getCurrent()); + if (self.oldIcon) { + domElement.querySelector('.icon-swatch').classList.remove(self.oldIcon); + } + + domElement.querySelector('.icon-swatch').classList.add(self.palette.getCurrent()); self.oldIcon = self.palette.getCurrent(); } diff --git a/src/plugins/summaryWidget/src/input/Palette.js b/src/plugins/summaryWidget/src/input/Palette.js index ff1d3b5500..96df813de2 100644 --- a/src/plugins/summaryWidget/src/input/Palette.js +++ b/src/plugins/summaryWidget/src/input/Palette.js @@ -1,13 +1,13 @@ define([ '../eventHelpers', '../../res/input/paletteTemplate.html', - 'EventEmitter', - 'zepto' + '../../../../utils/template/templateHelpers', + 'EventEmitter' ], function ( eventHelpers, paletteTemplate, - EventEmitter, - $ + templateHelpers, + EventEmitter ) { /** * Instantiates a new Open MCT Color Palette input @@ -28,36 +28,41 @@ define([ this.items = items; this.container = container; - this.domElement = $(paletteTemplate); + this.domElement = templateHelpers.convertTemplateToHTML(paletteTemplate)[0]; + this.itemElements = { - nullOption: $('.c-palette__item-none .c-palette__item', this.domElement) + nullOption: this.domElement.querySelector('.c-palette__item-none .c-palette__item') }; this.eventEmitter = new EventEmitter(); this.supportedCallbacks = ['change']; this.value = this.items[0]; this.nullOption = ' '; - this.button = $('.js-button', this.domElement); - this.menu = $('.c-menu', this.domElement); + this.button = this.domElement.querySelector('.js-button'); + this.menu = this.domElement.querySelector('.c-menu'); this.hideMenu = this.hideMenu.bind(this); - self.button.addClass(this.cssClass); + if (this.cssClass) { + self.button.classList.add(this.cssClass); + } + self.setNullOption(this.nullOption); self.items.forEach(function (item) { - const itemElement = $('
'); - $('.c-palette__items', self.domElement).append(itemElement); - self.itemElements[item] = itemElement; + const itemElement = `
`; + const temp = document.createElement('div'); + temp.innerHTML = itemElement; + self.itemElements[item] = temp.firstChild; + self.domElement.querySelector('.c-palette__items').appendChild(temp.firstChild); }); - $('.c-menu', self.domElement).hide(); + self.domElement.querySelector('.c-menu').style.display = 'none'; - this.listenTo($(document), 'click', this.hideMenu); - this.listenTo($('.js-button', self.domElement), 'click', function (event) { + this.listenTo(window.document, 'click', this.hideMenu); + this.listenTo(self.domElement.querySelector('.js-button'), 'click', function (event) { event.stopPropagation(); - $('.c-menu', self.container).hide(); - $('.c-menu', self.domElement).show(); + self.container.querySelector('.c-menu').style.display = 'none'; + self.domElement.querySelector('.c-menu').style.display = ''; }); /** @@ -70,10 +75,12 @@ define([ const elem = event.currentTarget; const item = elem.dataset.item; self.set(item); - $('.c-menu', self.domElement).hide(); + self.domElement.querySelector('.c-menu').style.display = 'none'; } - this.listenTo($('.c-palette__item', self.domElement), 'click', handleItemClick); + self.domElement.querySelectorAll('.c-palette__item').forEach(item => { + this.listenTo(item, 'click', handleItemClick); + }); } /** @@ -91,7 +98,7 @@ define([ }; Palette.prototype.hideMenu = function () { - $('.c-menu', this.domElement).hide(); + this.domElement.querySelector('.c-menu').style.display = 'none'; }; /** @@ -141,12 +148,16 @@ define([ * Update the view assoicated with the currently selected item */ Palette.prototype.updateSelected = function (item) { - $('.c-palette__item', this.domElement).removeClass('is-selected'); - this.itemElements[item].addClass('is-selected'); + this.domElement.querySelectorAll('.c-palette__item').forEach(paletteItem => { + if (paletteItem.classList.contains('is-selected')) { + paletteItem.classList.remove('is-selected'); + } + }); + this.itemElements[item].classList.add('is-selected'); if (item === 'nullOption') { - $('.t-swatch', this.domElement).addClass('no-selection'); + this.domElement.querySelector('.t-swatch').classList.add('no-selection'); } else { - $('.t-swatch', this.domElement).removeClass('no-selection'); + this.domElement.querySelector('.t-swatch').classList.remove('no-selection'); } }; @@ -157,14 +168,20 @@ define([ */ Palette.prototype.setNullOption = function (item) { this.nullOption = item; - this.itemElements.nullOption.data('item', item); + this.itemElements.nullOption.data = { item: item }; }; /** * Hides the 'no selection' option to be hidden in the view if it doesn't apply */ Palette.prototype.toggleNullOption = function () { - $('.c-palette__item-none', this.domElement).toggle(); + const elem = this.domElement.querySelector('.c-palette__item-none'); + + if (elem.style.display === 'none') { + this.domElement.querySelector('.c-palette__item-none').style.display = 'flex'; + } else { + this.domElement.querySelector('.c-palette__item-none').style.display = 'none'; + } }; return Palette; diff --git a/src/plugins/summaryWidget/src/input/Select.js b/src/plugins/summaryWidget/src/input/Select.js index 3f89034caf..676a9791b2 100644 --- a/src/plugins/summaryWidget/src/input/Select.js +++ b/src/plugins/summaryWidget/src/input/Select.js @@ -1,13 +1,13 @@ define([ '../eventHelpers', '../../res/input/selectTemplate.html', - 'EventEmitter', - 'zepto' + '../../../../utils/template/templateHelpers', + 'EventEmitter' ], function ( eventHelpers, selectTemplate, - EventEmitter, - $ + templateHelpers, + EventEmitter ) { /** @@ -20,7 +20,8 @@ define([ const self = this; - this.domElement = $(selectTemplate); + this.domElement = templateHelpers.convertTemplateToHTML(selectTemplate)[0]; + this.options = []; this.eventEmitter = new EventEmitter(); this.supportedCallbacks = ['change']; @@ -35,12 +36,12 @@ define([ */ function onChange(event) { const elem = event.target; - const value = self.options[$(elem).prop('selectedIndex')]; + const value = self.options[elem.selectedIndex]; self.eventEmitter.emit('change', value[0]); } - this.listenTo($('select', this.domElement), 'change', onChange, this); + this.listenTo(this.domElement.querySelector('select'), 'change', onChange, this); } /** @@ -74,16 +75,19 @@ define([ const self = this; let selectedIndex = 0; - selectedIndex = $('select', this.domElement).prop('selectedIndex'); - $('option', this.domElement).remove(); + selectedIndex = this.domElement.querySelector('select').selectedIndex; - self.options.forEach(function (option, index) { - $('select', self.domElement) - .append(''); + this.domElement.querySelector('select').innerHTML = ''; + + self.options.forEach(function (option) { + const optionElement = document.createElement('option'); + optionElement.value = option[0]; + optionElement.innerText = `+ ${option[1]}`; + + self.domElement.querySelector('select').appendChild(optionElement); }); - $('select', this.domElement).prop('selectedIndex', selectedIndex); + this.domElement.querySelector('select').selectedIndex = selectedIndex; }; /** @@ -120,7 +124,7 @@ define([ selectedIndex = index; } }); - $('select', this.domElement).prop('selectedIndex', selectedIndex); + this.domElement.querySelector('select').selectedIndex = selectedIndex; selectedOption = this.options[selectedIndex]; this.eventEmitter.emit('change', selectedOption[0]); @@ -131,17 +135,21 @@ define([ * @return {string} */ Select.prototype.getSelected = function () { - return $('select', this.domElement).prop('value'); + return this.domElement.querySelector('select').value; }; Select.prototype.hide = function () { - $(this.domElement).addClass('hidden'); - $('.equal-to').addClass('hidden'); + this.domElement.classList.add('hidden'); + if (this.domElement.querySelector('.equal-to')) { + this.domElement.querySelector('.equal-to').classList.add('hidden'); + } }; Select.prototype.show = function () { - $(this.domElement).removeClass('hidden'); - $('.equal-to').removeClass('hidden'); + this.domElement.classList.remove('hidden'); + if (this.domElement.querySelector('.equal-to')) { + this.domElement.querySelector('.equal-to').classList.remove('hidden'); + } }; Select.prototype.destroy = function () { diff --git a/src/plugins/summaryWidget/test/ConditionSpec.js b/src/plugins/summaryWidget/test/ConditionSpec.js index a69742065b..8b166bf872 100644 --- a/src/plugins/summaryWidget/test/ConditionSpec.js +++ b/src/plugins/summaryWidget/test/ConditionSpec.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['../src/Condition', 'zepto'], function (Condition, $) { +define(['../src/Condition'], function (Condition) { xdescribe('A summary widget condition', function () { let testCondition; let mockConfig; @@ -33,7 +33,7 @@ define(['../src/Condition', 'zepto'], function (Condition, $) { let generateValuesSpy; beforeEach(function () { - mockContainer = $(document.createElement('div')); + mockContainer = document.createElement('div'); mockConfig = { object: 'object1', @@ -78,7 +78,7 @@ define(['../src/Condition', 'zepto'], function (Condition, $) { it('exposes a DOM element to represent itself in the view', function () { mockContainer.append(testCondition.getDOM()); - expect($('.t-condition', mockContainer).get().length).toEqual(1); + expect(mockContainer.querySelectorAll('.t-condition').length).toEqual(1); }); it('responds to a change in its object select', function () { @@ -111,41 +111,59 @@ define(['../src/Condition', 'zepto'], function (Condition, $) { }); it('generates value inputs of the appropriate type and quantity', function () { + let inputs; + mockContainer.append(testCondition.getDOM()); mockEvaluator.getInputType.and.returnValue('number'); mockEvaluator.getInputCount.and.returnValue(3); testCondition.generateValueInputs(''); - expect($('input', mockContainer).filter('[type=number]').get().length).toEqual(3); - expect($('input', mockContainer).eq(0).prop('valueAsNumber')).toEqual(1); - expect($('input', mockContainer).eq(1).prop('valueAsNumber')).toEqual(2); - expect($('input', mockContainer).eq(2).prop('valueAsNumber')).toEqual(3); + + inputs = mockContainer.querySelectorAll('input'); + const numberInputs = Array.from(inputs).filter(input => input.type === 'number'); + + expect(numberInputs.length).toEqual(3); + expect(numberInputs[0].valueAsNumber).toEqual(1); + expect(numberInputs[1].valueAsNumber).toEqual(2); + expect(numberInputs[2].valueAsNumber).toEqual(3); mockEvaluator.getInputType.and.returnValue('text'); mockEvaluator.getInputCount.and.returnValue(2); testCondition.config.values = ['Text I Am', 'Text It Is']; testCondition.generateValueInputs(''); - expect($('input', mockContainer).filter('[type=text]').get().length).toEqual(2); - expect($('input', mockContainer).eq(0).prop('value')).toEqual('Text I Am'); - expect($('input', mockContainer).eq(1).prop('value')).toEqual('Text It Is'); + + inputs = mockContainer.querySelectorAll('input'); + const textInputs = Array.from(inputs).filter(input => input.type === 'text'); + + expect(textInputs.length).toEqual(2); + expect(textInputs[0].value).toEqual('Text I Am'); + expect(textInputs[1].value).toEqual('Text It Is'); }); it('ensures reasonable defaults on values if none are provided', function () { + let inputs; + mockContainer.append(testCondition.getDOM()); mockEvaluator.getInputType.and.returnValue('number'); mockEvaluator.getInputCount.and.returnValue(3); testCondition.config.values = []; testCondition.generateValueInputs(''); - expect($('input', mockContainer).eq(0).prop('valueAsNumber')).toEqual(0); - expect($('input', mockContainer).eq(1).prop('valueAsNumber')).toEqual(0); - expect($('input', mockContainer).eq(2).prop('valueAsNumber')).toEqual(0); + + inputs = Array.from(mockContainer.querySelectorAll('input')); + + expect(inputs[0].valueAsNumber).toEqual(0); + expect(inputs[1].valueAsNumber).toEqual(0); + expect(inputs[2].valueAsNumber).toEqual(0); expect(testCondition.config.values).toEqual([0, 0, 0]); mockEvaluator.getInputType.and.returnValue('text'); mockEvaluator.getInputCount.and.returnValue(2); testCondition.config.values = []; testCondition.generateValueInputs(''); - expect($('input', mockContainer).eq(0).prop('value')).toEqual(''); - expect($('input', mockContainer).eq(1).prop('value')).toEqual(''); + + inputs = Array.from(mockContainer.querySelectorAll('input')); + + expect(inputs[0].value).toEqual(''); + expect(inputs[1].value).toEqual(''); expect(testCondition.config.values).toEqual(['', '']); }); @@ -154,8 +172,16 @@ define(['../src/Condition', 'zepto'], function (Condition, $) { mockEvaluator.getInputType.and.returnValue('number'); mockEvaluator.getInputCount.and.returnValue(3); testCondition.generateValueInputs(''); - $('input', mockContainer).eq(1).prop('value', 9001); - $('input', mockContainer).eq(1).trigger('input'); + + const event = new Event('input', { + bubbles: true, + cancelable: true + }); + const inputs = mockContainer.querySelectorAll('input'); + + inputs[1].value = 9001; + inputs[1].dispatchEvent(event); + expect(changeSpy).toHaveBeenCalledWith({ value: 9001, property: 'values[1]', diff --git a/src/plugins/summaryWidget/test/RuleSpec.js b/src/plugins/summaryWidget/test/RuleSpec.js index 4d6c5b7149..df5108f7ae 100644 --- a/src/plugins/summaryWidget/test/RuleSpec.js +++ b/src/plugins/summaryWidget/test/RuleSpec.js @@ -1,4 +1,4 @@ -define(['../src/Rule', 'zepto'], function (Rule, $) { +define(['../src/Rule'], function (Rule) { describe('A Summary Widget Rule', function () { let mockRuleConfig; let mockDomainObject; @@ -78,7 +78,7 @@ define(['../src/Rule', 'zepto'], function (Rule, $) { 'dragStart' ]); - mockContainer = $(document.createElement('div')); + mockContainer = document.createElement('div'); removeSpy = jasmine.createSpy('removeCallback'); duplicateSpy = jasmine.createSpy('duplicateCallback'); @@ -99,7 +99,7 @@ define(['../src/Rule', 'zepto'], function (Rule, $) { it('gets its DOM element', function () { mockContainer.append(testRule.getDOM()); - expect($('.l-widget-rule', mockContainer).get().length).toBeGreaterThan(0); + expect(mockContainer.querySelectorAll('.l-widget-rule').length).toBeGreaterThan(0); }); it('gets its configuration properties', function () { @@ -185,7 +185,7 @@ define(['../src/Rule', 'zepto'], function (Rule, $) { it('builds condition view from condition configuration', function () { mockContainer.append(testRule.getDOM()); - expect($('.t-condition', mockContainer).get().length).toEqual(2); + expect(mockContainer.querySelectorAll('.t-condition').length).toEqual(2); }); it('responds to input of style properties, and updates the preview', function () { @@ -196,9 +196,9 @@ define(['../src/Rule', 'zepto'], function (Rule, $) { testRule.colorInputs.color.set('#999999'); expect(mockRuleConfig.style.color).toEqual('#999999'); - expect(testRule.thumbnail.css('background-color')).toEqual('rgb(67, 67, 67)'); - expect(testRule.thumbnail.css('border-color')).toEqual('rgb(102, 102, 102)'); - expect(testRule.thumbnail.css('color')).toEqual('rgb(153, 153, 153)'); + expect(testRule.thumbnail.style['background-color']).toEqual('rgb(67, 67, 67)'); + expect(testRule.thumbnail.style['border-color']).toEqual('rgb(102, 102, 102)'); + expect(testRule.thumbnail.style.color).toEqual('rgb(153, 153, 153)'); expect(changeSpy).toHaveBeenCalled(); }); @@ -228,8 +228,12 @@ define(['../src/Rule', 'zepto'], function (Rule, $) { // }); it('allows input for when the rule triggers', function () { - testRule.trigger.prop('value', 'all'); - testRule.trigger.trigger('change'); + testRule.trigger.value = 'all'; + const event = new Event('change', { + bubbles: true, + cancelable: true + }); + testRule.trigger.dispatchEvent(event); expect(testRule.config.trigger).toEqual('all'); expect(conditionChangeSpy).toHaveBeenCalled(); }); @@ -247,7 +251,12 @@ define(['../src/Rule', 'zepto'], function (Rule, $) { }); it('initiates a drag event when its grippy is clicked', function () { - testRule.grippy.trigger('mousedown'); + const event = new Event('mousedown', { + bubbles: true, + cancelable: true + }); + testRule.grippy.dispatchEvent(event); + expect(mockWidgetDnD.setDragImage).toHaveBeenCalled(); expect(mockWidgetDnD.dragStart).toHaveBeenCalledWith('mockRule'); }); diff --git a/src/plugins/summaryWidget/test/SummaryWidgetSpec.js b/src/plugins/summaryWidget/test/SummaryWidgetSpec.js index 1bee993e2d..877b1b366d 100644 --- a/src/plugins/summaryWidget/test/SummaryWidgetSpec.js +++ b/src/plugins/summaryWidget/test/SummaryWidgetSpec.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['../src/SummaryWidget', 'zepto'], function (SummaryWidget, $) { +define(['../src/SummaryWidget'], function (SummaryWidget) { xdescribe('The Summary Widget', function () { let summaryWidget; let mockDomainObject; @@ -111,7 +111,7 @@ define(['../src/SummaryWidget', 'zepto'], function (SummaryWidget, $) { }); it('builds rules and rule placeholders in view from configuration', function () { - expect($('.l-widget-rule', summaryWidget.ruleArea).get().length).toEqual(2); + expect(summaryWidget.ruleArea.querySelectorAll('.l-widget-rule').length).toEqual(2); }); it('allows initializing a new rule with a particular identifier', function () { @@ -130,7 +130,7 @@ define(['../src/SummaryWidget', 'zepto'], function (SummaryWidget, $) { mockDomainObject.configuration.ruleOrder.forEach(function (ruleId) { expect(mockDomainObject.configuration.ruleConfigById[ruleId]).toBeDefined(); }); - expect($('.l-widget-rule', summaryWidget.ruleArea).get().length).toEqual(6); + expect(summaryWidget.ruleArea.querySelectorAll('.l-widget-rule').length).toEqual(6); }); it('allows duplicating a rule from source configuration', function () { @@ -186,10 +186,10 @@ define(['../src/SummaryWidget', 'zepto'], function (SummaryWidget, $) { it('adds hyperlink to the widget button and sets newTab preference', function () { summaryWidget.addHyperlink('https://www.nasa.gov', 'newTab'); - const widgetButton = $('#widget', mockContainer); + const widgetButton = mockContainer.querySelector('#widget'); - expect(widgetButton.attr('href')).toEqual('https://www.nasa.gov'); - expect(widgetButton.attr('target')).toEqual('_blank'); + expect(widgetButton.href).toEqual('https://www.nasa.gov/'); + expect(widgetButton.target).toEqual('_blank'); }); }); }); diff --git a/src/plugins/summaryWidget/test/TestDataItemSpec.js b/src/plugins/summaryWidget/test/TestDataItemSpec.js index dffa6c6f22..171753efe4 100644 --- a/src/plugins/summaryWidget/test/TestDataItemSpec.js +++ b/src/plugins/summaryWidget/test/TestDataItemSpec.js @@ -1,4 +1,4 @@ -define(['../src/TestDataItem', 'zepto'], function (TestDataItem, $) { +define(['../src/TestDataItem'], function (TestDataItem) { describe('A summary widget test data item', function () { let testDataItem; let mockConfig; @@ -11,7 +11,7 @@ define(['../src/TestDataItem', 'zepto'], function (TestDataItem, $) { let generateValueSpy; beforeEach(function () { - mockContainer = $(document.createElement('div')); + mockContainer = document.createElement('div'); mockConfig = { object: 'object1', @@ -56,7 +56,7 @@ define(['../src/TestDataItem', 'zepto'], function (TestDataItem, $) { it('exposes a DOM element to represent itself in the view', function () { mockContainer.append(testDataItem.getDOM()); - expect($('.t-test-data-item', mockContainer).get().length).toEqual(1); + expect(mockContainer.querySelectorAll('.t-test-data-item').length).toEqual(1); }); it('responds to a change in its object select', function () { @@ -80,34 +80,54 @@ define(['../src/TestDataItem', 'zepto'], function (TestDataItem, $) { }); it('generates a value input of the appropriate type', function () { + let inputs; + mockContainer.append(testDataItem.getDOM()); mockEvaluator.getInputTypeById.and.returnValue('number'); testDataItem.generateValueInput(''); - expect($('input', mockContainer).filter('[type=number]').get().length).toEqual(1); - expect($('input', mockContainer).prop('valueAsNumber')).toEqual(1); + + inputs = mockContainer.querySelectorAll('input'); + const numberInputs = Array.from(inputs).filter(input => input.type === 'number'); + + expect(numberInputs.length).toEqual(1); + expect(inputs[0].valueAsNumber).toEqual(1); mockEvaluator.getInputTypeById.and.returnValue('text'); testDataItem.config.value = 'Text I Am'; testDataItem.generateValueInput(''); - expect($('input', mockContainer).filter('[type=text]').get().length).toEqual(1); - expect($('input', mockContainer).prop('value')).toEqual('Text I Am'); + + inputs = mockContainer.querySelectorAll('input'); + const textInputs = Array.from(inputs).filter(input => input.type === 'text'); + + expect(textInputs.length).toEqual(1); + expect(inputs[0].value).toEqual('Text I Am'); }); it('ensures reasonable defaults on values if none are provided', function () { + let inputs; + mockContainer.append(testDataItem.getDOM()); mockEvaluator.getInputTypeById.and.returnValue('number'); testDataItem.config.value = undefined; testDataItem.generateValueInput(''); - expect($('input', mockContainer).filter('[type=number]').get().length).toEqual(1); - expect($('input', mockContainer).prop('valueAsNumber')).toEqual(0); + + inputs = mockContainer.querySelectorAll('input'); + const numberInputs = Array.from(inputs).filter(input => input.type === 'number'); + + expect(numberInputs.length).toEqual(1); + expect(inputs[0].valueAsNumber).toEqual(0); expect(testDataItem.config.value).toEqual(0); mockEvaluator.getInputTypeById.and.returnValue('text'); testDataItem.config.value = undefined; testDataItem.generateValueInput(''); - expect($('input', mockContainer).filter('[type=text]').get().length).toEqual(1); - expect($('input', mockContainer).prop('value')).toEqual(''); + + inputs = mockContainer.querySelectorAll('input'); + const textInputs = Array.from(inputs).filter(input => input.type === 'text'); + + expect(textInputs.length).toEqual(1); + expect(inputs[0].value).toEqual(''); expect(testDataItem.config.value).toEqual(''); }); @@ -115,8 +135,15 @@ define(['../src/TestDataItem', 'zepto'], function (TestDataItem, $) { mockContainer.append(testDataItem.getDOM()); mockEvaluator.getInputTypeById.and.returnValue('number'); testDataItem.generateValueInput(''); - $('input', mockContainer).prop('value', 9001); - $('input', mockContainer).trigger('input'); + + const event = new Event('input', { + bubbles: true, + cancelable: true + }); + + mockContainer.querySelector('input').value = 9001; + mockContainer.querySelector('input').dispatchEvent(event); + expect(changeSpy).toHaveBeenCalledWith({ value: 9001, property: 'value', diff --git a/src/plugins/summaryWidget/test/TestDataManagerSpec.js b/src/plugins/summaryWidget/test/TestDataManagerSpec.js index 70042250d3..59ce37d92c 100644 --- a/src/plugins/summaryWidget/test/TestDataManagerSpec.js +++ b/src/plugins/summaryWidget/test/TestDataManagerSpec.js @@ -1,4 +1,4 @@ -define(['../src/TestDataManager', 'zepto'], function (TestDataManager, $) { +define(['../src/TestDataManager'], function (TestDataManager) { describe('A Summary Widget Rule', function () { let mockDomainObject; let mockOpenMCT; @@ -103,7 +103,7 @@ define(['../src/TestDataManager', 'zepto'], function (TestDataManager, $) { mockConditionManager.getObjectName.and.returnValue('Object Name'); mockConditionManager.getTelemetryPropertyName.and.returnValue('Property Name'); - mockContainer = $(document.createElement('div')); + mockContainer = document.createElement('div'); testDataManager = new TestDataManager(mockDomainObject, mockConditionManager, mockOpenMCT); }); @@ -114,7 +114,7 @@ define(['../src/TestDataManager', 'zepto'], function (TestDataManager, $) { it('exposes a DOM element to represent itself in the view', function () { mockContainer.append(testDataManager.getDOM()); - expect($('.t-widget-test-data-content', mockContainer).get().length).toBeGreaterThan(0); + expect(mockContainer.querySelectorAll('.t-widget-test-data-content').length).toBeGreaterThan(0); }); it('generates a test cache in the format expected by a condition evaluator', function () { @@ -207,7 +207,7 @@ define(['../src/TestDataManager', 'zepto'], function (TestDataManager, $) { it('builds item view from item configuration', function () { mockContainer.append(testDataManager.getDOM()); - expect($('.t-test-data-item', mockContainer).get().length).toEqual(3); + expect(mockContainer.querySelectorAll('.t-test-data-item').length).toEqual(3); }); it('can remove a item from its configuration', function () { diff --git a/src/utils/template/templateHelpers.js b/src/utils/template/templateHelpers.js new file mode 100644 index 0000000000..70d381ce7d --- /dev/null +++ b/src/utils/template/templateHelpers.js @@ -0,0 +1,14 @@ +export function convertTemplateToHTML(templateString) { + const template = document.createElement('template'); + template.innerHTML = templateString; + + return template.content.cloneNode(true).children; +} + +export function toggleClass(element, className) { + if (element.classList.contains(className)) { + element.classList.remove(className); + } else { + element.classList.add(className); + } +} diff --git a/webpack.common.js b/webpack.common.js index 5b1de5da4f..20f9705f48 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -101,13 +101,6 @@ const config = { test: /\.html$/, type: 'asset/source' }, - { - test: /zepto/, - use: [ - "imports-loader?this=>window", - "exports-loader?Zepto" - ] - }, { test: /\.(jpg|jpeg|png|svg)$/, type: 'asset/resource', From 40a74510648761fc6e82b0ed60aebb774f175041 Mon Sep 17 00:00:00 2001 From: Shefali Joshi Date: Fri, 3 Jun 2022 09:46:27 -0700 Subject: [PATCH 213/283] Fix stackplots static style (#5045) * [4864] Fixes cancelling edit properties console error * Get the style receiver when the styleRuleManager is initialized. This prevents any ambiguity about which element should receive the style * Don't subscribe if the styleRuleManager has been destroyed Co-authored-by: John Hill Co-authored-by: Jamie V Co-authored-by: Nikhil Co-authored-by: Andrew Henry --- .circleci/config.yml | 10 +++++----- src/plugins/condition/StyleRuleManager.js | 6 ++++-- src/plugins/formActions/EditPropertiesAction.js | 10 +++++++++- src/plugins/formActions/pluginSpec.js | 7 +++++++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3268c660a6..8863b9e057 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,7 +31,7 @@ commands: type: string steps: - when: - condition: + condition: equal: [false, << pipeline.parameters.BUST_CACHE >> ] steps: - restore_cache: @@ -41,7 +41,7 @@ commands: parameters: node-version: type: string - steps: + steps: - save_cache: key: deps-{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }} paths: @@ -61,7 +61,7 @@ commands: upload_code_covio: description: "Command to upload code coverage reports to codecov.io" steps: - - run: curl -Os https://uploader.codecov.io/latest/linux/codecov;chmod +x codecov;./codecov + - run: curl -Os https://uploader.codecov.io/latest/linux/codecov;chmod +x codecov;./codecov orbs: node: circleci/node@4.9.0 browser-tools: circleci/browser-tools@1.3.0 @@ -101,7 +101,7 @@ jobs: equal: [ "FirefoxESR", <> ] steps: - browser-tools/install-firefox: - version: "91.7.1esr" #https://archive.mozilla.org/pub/firefox/releases/ + version: "91.7.1esr" #https://archive.mozilla.org/pub/firefox/releases/ - when: condition: equal: [ "FirefoxHeadless", <> ] @@ -158,7 +158,7 @@ workflows: - lint: name: node16-lint node-version: lts/gallium - - unit-test: + - unit-test: name: node14-chrome node-version: lts/fermium browser: ChromeHeadless diff --git a/src/plugins/condition/StyleRuleManager.js b/src/plugins/condition/StyleRuleManager.js index e7f201ca7e..18063b337c 100644 --- a/src/plugins/condition/StyleRuleManager.js +++ b/src/plugins/condition/StyleRuleManager.js @@ -78,11 +78,13 @@ export default class StyleRuleManager extends EventEmitter { this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => { this.openmct.telemetry.request(conditionSetDomainObject) .then(output => { - if (output && output.length) { + if (output && output.length && (this.conditionSetIdentifier && this.openmct.objects.areIdsEqual(conditionSetDomainObject.identifier, this.conditionSetIdentifier))) { this.handleConditionSetResultUpdated(output[0]); } }); - this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this)); + if (this.conditionSetIdentifier && this.openmct.objects.areIdsEqual(conditionSetDomainObject.identifier, this.conditionSetIdentifier)) { + this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this)); + } }); } diff --git a/src/plugins/formActions/EditPropertiesAction.js b/src/plugins/formActions/EditPropertiesAction.js index 53afadfe90..65ceaaadd1 100644 --- a/src/plugins/formActions/EditPropertiesAction.js +++ b/src/plugins/formActions/EditPropertiesAction.js @@ -76,6 +76,13 @@ export default class EditPropertiesAction extends PropertiesAction { } } + /** + * @private + */ + _onCancel() { + //noop + } + /** * @private */ @@ -87,6 +94,7 @@ export default class EditPropertiesAction extends PropertiesAction { formStructure.title = 'Edit ' + this.domainObject.name; return this.openmct.forms.showForm(formStructure) - .then(this._onSave.bind(this)); + .then(this._onSave.bind(this)) + .catch(this._onCancel.bind(this)); } } diff --git a/src/plugins/formActions/pluginSpec.js b/src/plugins/formActions/pluginSpec.js index 9c4cbb2cc0..232ff0d303 100644 --- a/src/plugins/formActions/pluginSpec.js +++ b/src/plugins/formActions/pluginSpec.js @@ -123,6 +123,9 @@ describe('EditPropertiesAction plugin', () => { } editPropertiesAction.invoke([domainObject]) + .then(() => { + done(); + }) .catch(() => { done(); }); @@ -208,6 +211,10 @@ describe('EditPropertiesAction plugin', () => { }; editPropertiesAction.invoke([domainObject]) + .then(() => { + expect(domainObject.name).toEqual(name); + done(); + }) .catch(() => { expect(domainObject.name).toEqual(name); From 1c525f50c88cb662699811c620e59ee285b67abd Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 3 Jun 2022 09:53:21 -0700 Subject: [PATCH 214/283] Display Layout toolbar refinements for units (#5197) * Fixes #3197 - Moved position of hide/show units toggle button. - Added labels to many toolbar buttons, including hide/show units, hide/show frame, edit text, more. - Added label to toolbar-toggle-button.vue. - Added separator between stackOrder button and position inputs. * Fixes #3197 - Removed unwanted margin in alphanumerics when label is hidden. Co-authored-by: Shefali Joshi --- .../displayLayout/DisplayLayoutToolbar.js | 40 ++++++++++++------- .../components/telemetry-view.scss | 8 ++-- src/plugins/displayLayout/pluginSpec.js | 2 +- .../components/toolbar-toggle-button.vue | 12 ++++-- 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/plugins/displayLayout/DisplayLayoutToolbar.js b/src/plugins/displayLayout/DisplayLayoutToolbar.js index 24038b8555..5bcf3ee45d 100644 --- a/src/plugins/displayLayout/DisplayLayoutToolbar.js +++ b/src/plugins/displayLayout/DisplayLayoutToolbar.js @@ -211,13 +211,15 @@ define(['lodash'], function (_) { options: [ { value: false, - icon: 'icon-frame-show', - title: "Frame visible" + icon: 'icon-frame-hide', + title: "Frame visible", + label: 'Hide frame' }, { value: true, - icon: 'icon-frame-hide', - title: "Frame hidden" + icon: 'icon-frame-show', + title: "Frame hidden", + label: 'Show frame' } ] }; @@ -401,6 +403,7 @@ define(['lodash'], function (_) { }, icon: "icon-pencil", title: "Edit text properties", + label: "Edit text", dialog: DIALOG_FORM.text }; } @@ -514,12 +517,14 @@ define(['lodash'], function (_) { { value: true, icon: 'icon-eye-open', - title: "Show units" + title: "Show units", + label: "Show units" }, { value: false, icon: 'icon-eye-disabled', - title: "Hide units" + title: "Hide units", + label: "Hide units" } ] }; @@ -562,6 +567,7 @@ define(['lodash'], function (_) { domainObject: selectedParent, icon: "icon-object", title: "Switch the way this telemetry is displayed", + label: "View type", options: viewOptions, method: function (option) { displayLayoutContext.switchViewType(selectedItemContext, option.value, selection); @@ -662,9 +668,9 @@ define(['lodash'], function (_) { 'display-mode': [], 'telemetry-value': [], 'style': [], + 'unit-toggle': [], 'position': [], 'duplicate': [], - 'unit-toggle': [], 'remove': [], 'toggle-grid': [] }; @@ -689,6 +695,7 @@ define(['lodash'], function (_) { if (toolbar.position.length === 0) { toolbar.position = [ getStackOrder(selectedParent, selectionPath), + getSeparator(), getXInput(selectedParent, selectedObjects), getYInput(selectedParent, selectedObjects), getHeightInput(selectedParent, selectedObjects), @@ -712,9 +719,17 @@ define(['lodash'], function (_) { toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selectedObjects)]; } + if (toolbar['unit-toggle'].length === 0) { + let toggleUnitsButton = getToggleUnitsButton(selectedParent, selectedObjects); + if (toggleUnitsButton) { + toolbar['unit-toggle'] = [toggleUnitsButton]; + } + } + if (toolbar.position.length === 0) { toolbar.position = [ getStackOrder(selectedParent, selectionPath), + getSeparator(), getXInput(selectedParent, selectedObjects), getYInput(selectedParent, selectedObjects), getHeightInput(selectedParent, selectedObjects), @@ -729,17 +744,11 @@ define(['lodash'], function (_) { if (toolbar.viewSwitcher.length === 0) { toolbar.viewSwitcher = [getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)]; } - - if (toolbar['unit-toggle'].length === 0) { - let toggleUnitsButton = getToggleUnitsButton(selectedParent, selectedObjects); - if (toggleUnitsButton) { - toolbar['unit-toggle'] = [toggleUnitsButton]; - } - } } else if (layoutItem.type === 'text-view') { if (toolbar.position.length === 0) { toolbar.position = [ getStackOrder(selectedParent, selectionPath), + getSeparator(), getXInput(selectedParent, selectedObjects), getYInput(selectedParent, selectedObjects), getHeightInput(selectedParent, selectedObjects), @@ -758,6 +767,7 @@ define(['lodash'], function (_) { if (toolbar.position.length === 0) { toolbar.position = [ getStackOrder(selectedParent, selectionPath), + getSeparator(), getXInput(selectedParent, selectedObjects), getYInput(selectedParent, selectedObjects), getHeightInput(selectedParent, selectedObjects), @@ -772,6 +782,7 @@ define(['lodash'], function (_) { if (toolbar.position.length === 0) { toolbar.position = [ getStackOrder(selectedParent, selectionPath), + getSeparator(), getXInput(selectedParent, selectedObjects), getYInput(selectedParent, selectedObjects), getHeightInput(selectedParent, selectedObjects), @@ -786,6 +797,7 @@ define(['lodash'], function (_) { if (toolbar.position.length === 0) { toolbar.position = [ getStackOrder(selectedParent, selectionPath), + getSeparator(), getXInput(selectedParent, selectedObjects), getYInput(selectedParent, selectedObjects), getX2Input(selectedParent, selectedObjects), diff --git a/src/plugins/displayLayout/components/telemetry-view.scss b/src/plugins/displayLayout/components/telemetry-view.scss index 0dbfc75ac6..8b73d118a0 100644 --- a/src/plugins/displayLayout/components/telemetry-view.scss +++ b/src/plugins/displayLayout/components/telemetry-view.scss @@ -17,14 +17,14 @@ } } - > * + * { - margin-left: $interiorMargin; - } - &__value { @include isLimit(); } + &__label { + margin-right: $interiorMargin; + } + .c-frame & { @include abs(); border: 1px solid transparent; diff --git a/src/plugins/displayLayout/pluginSpec.js b/src/plugins/displayLayout/pluginSpec.js index 643f13be6e..a1ac3764a6 100644 --- a/src/plugins/displayLayout/pluginSpec.js +++ b/src/plugins/displayLayout/pluginSpec.js @@ -351,7 +351,7 @@ describe('the plugin', function () { it('provides controls including separators', () => { const displayLayoutToolbar = openmct.toolbars.get(selection); - expect(displayLayoutToolbar.length).toBe(7); + expect(displayLayoutToolbar.length).toBe(8); }); }); }); diff --git a/src/ui/toolbar/components/toolbar-toggle-button.vue b/src/ui/toolbar/components/toolbar-toggle-button.vue index e3d37c4c5f..27ca3d62f0 100644 --- a/src/ui/toolbar/components/toolbar-toggle-button.vue +++ b/src/ui/toolbar/components/toolbar-toggle-button.vue @@ -5,9 +5,15 @@ :title="nextValue.title" :class="[nextValue.icon, {'c-icon-button--mixed': nonSpecific}]" @click="cycle" - >
-
- + > +
+ {{ nextValue.label }} +
+
+ diff --git a/src/ui/components/tags/TagEditor.vue b/src/ui/components/tags/TagEditor.vue new file mode 100644 index 0000000000..65d9ace133 --- /dev/null +++ b/src/ui/components/tags/TagEditor.vue @@ -0,0 +1,155 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, 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. + *****************************************************************************/ + + + + diff --git a/src/ui/components/tags/TagSelection.vue b/src/ui/components/tags/TagSelection.vue new file mode 100644 index 0000000000..3163ae0456 --- /dev/null +++ b/src/ui/components/tags/TagSelection.vue @@ -0,0 +1,152 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, 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. + *****************************************************************************/ + + + + diff --git a/src/ui/components/tags/tags.scss b/src/ui/components/tags/tags.scss new file mode 100644 index 0000000000..ebd3e7a184 --- /dev/null +++ b/src/ui/components/tags/tags.scss @@ -0,0 +1,67 @@ +/******************************* TAGS */ +.c-tag { + border-radius: 10px; //TODO: convert to theme constant + display: inline-flex; + padding: 1px 10px; //TODO: convert to theme constant + + > * + * { + margin-left: $interiorMargin; + } + + &__remove-btn { + color: inherit !important; + display: none; + opacity: 0; + overflow: hidden; + padding: 1px !important; + transition: $transIn; + width: 0; + + &:hover { + opacity: 1; + } + } + + /* SEARCH RESULTS */ + &.--is-not-search-match { + opacity: 0.5; + } +} + +/******************************* TAG EDITOR */ +.c-tag-applier { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + + > * + * { + margin-left: $interiorMargin; + } + + &__add-btn { + &:before { font-size: 0.9em; } + } + + .c-tag { + flex-direction: row; + align-items: center; + padding-right: 3px !important; + + &__remove-btn { + display: block; + } + } +} + +/******************************* HOVERS */ +.has-tag-applier { + // Apply this class to all components that should trigger tag removal btn on hover + &:hover { + .c-tag__remove-btn { + width: 1.1em; + opacity: 0.7; + transition: $transOut; + } + } + } diff --git a/src/ui/layout/Layout.vue b/src/ui/layout/Layout.vue index 2ded6498d6..ee52964363 100644 --- a/src/ui/layout/Layout.vue +++ b/src/ui/layout/Layout.vue @@ -18,6 +18,9 @@ }" > + + + + + + diff --git a/src/ui/layout/search/GrandSearch.vue b/src/ui/layout/search/GrandSearch.vue new file mode 100644 index 0000000000..788ae1e589 --- /dev/null +++ b/src/ui/layout/search/GrandSearch.vue @@ -0,0 +1,145 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, 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. + *****************************************************************************/ + + + + diff --git a/src/ui/layout/search/ObjectSearchResult.vue b/src/ui/layout/search/ObjectSearchResult.vue new file mode 100644 index 0000000000..2e3416a8ee --- /dev/null +++ b/src/ui/layout/search/ObjectSearchResult.vue @@ -0,0 +1,102 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, 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. + *****************************************************************************/ + + + + diff --git a/src/ui/layout/search/SearchResultsDropDown.vue b/src/ui/layout/search/SearchResultsDropDown.vue new file mode 100644 index 0000000000..21e1487cb8 --- /dev/null +++ b/src/ui/layout/search/SearchResultsDropDown.vue @@ -0,0 +1,99 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, 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. + *****************************************************************************/ + + + + diff --git a/src/ui/layout/search/search.scss b/src/ui/layout/search/search.scss new file mode 100644 index 0000000000..fe3e3513db --- /dev/null +++ b/src/ui/layout/search/search.scss @@ -0,0 +1,137 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, 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. + *****************************************************************************/ + +/******************************* EXPANDED SEARCH 2022 */ +.c-gsearch { + .l-shell__head & { + // Search input in the shell head + width: 20%; + + .c-search { + background: rgba($colorHeadFg, 0.2); + box-shadow: none; + } + } + + &__results-wrapper { + @include menuOuter(); + display: flex; + flex-direction: column; + padding: $interiorMarginLg; + min-width: 500px; + max-height: 500px; + } + + &__results, + &__results-section { + flex: 1 1 auto; + } + + &__results { + // Holds n __results-sections + padding-right: $interiorMargin; // Fend off scrollbar + overflow-y: auto; + + > * + * { + margin-top: $interiorMarginLg; + } + } + + &__results-section { + > * + * { + margin-top: $interiorMarginSm; + } + } + + &__results-section-title { + @include propertiesHeader(); + } +} + +.c-gsearch-result { + display: flex; + padding: $interiorMargin $interiorMarginSm; + + > * + * { + margin-left: $interiorMarginLg; + } + + + .c-gsearch-result { + border-top: 1px solid $colorInteriorBorder; + } + + &__type-icon, + &__more-options-button { + flex: 0 0 auto; + } + + &__type-icon { + color: $colorItemTreeIcon; + font-size: 2.2em; + + // TEMP: uses object-label component, hide label part + .c-object-label__name { + display: none; + } + } + + &__more-options-button { + display: none; // TEMP until enabled + } + + &__body { + flex: 1 1 auto; + + > * + * { + margin-top: $interiorMarginSm; + } + + .c-location { + font-size: 0.9em; + opacity: 0.8; + } + } + + &__tags { + display: flex; + + > * + * { + margin-left: $interiorMargin; + } + } + + &__title { + border-radius: $basicCr; + color: pullForward($colorBodyFg, 30%); + cursor: pointer; + font-size: 1.15em; + padding: 3px $interiorMarginSm; + + &:hover { + background-color: $colorItemTreeHoverBg; + } + } + + .c-tag { + font-size: 0.9em; + } +} diff --git a/src/ui/mixins/context-menu-gesture.js b/src/ui/mixins/context-menu-gesture.js index 404015f383..ad7fa0de31 100644 --- a/src/ui/mixins/context-menu-gesture.js +++ b/src/ui/mixins/context-menu-gesture.js @@ -33,6 +33,10 @@ export default { }, methods: { showContextMenu(event) { + if (this.readOnly) { + return; + } + event.preventDefault(); event.stopPropagation(); diff --git a/src/ui/router/Browse.js b/src/ui/router/Browse.js index 05106815ae..1c8f622457 100644 --- a/src/ui/router/Browse.js +++ b/src/ui/router/Browse.js @@ -133,9 +133,7 @@ define([ composition.load() .then(children => { let lastChild = children[children.length - 1]; - if (!lastChild) { - console.debug('Unable to navigate to anything. No root objects found.'); - } else { + if (lastChild) { let lastChildId = openmct.objects.makeKeyString(lastChild.identifier); openmct.router.setPath(`#/browse/${lastChildId}`); } From 59c0da1b5753fc1fd1630d42a401c4de822131eb Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 3 Jun 2022 14:34:03 -0700 Subject: [PATCH 221/283] Add units to Gauges (#5196) * Fixes #3197 - Code and styling to allow units display. Co-authored-by: Shefali Joshi Co-authored-by: Nikhil Co-authored-by: Andrew Henry --- src/plugins/gauge/GaugePlugin.js | 46 ++++++++++++------- src/plugins/gauge/GaugePluginSpec.js | 38 ++++++++------- src/plugins/gauge/components/Gauge.vue | 38 +++++++++++++-- .../gauge/components/GaugeFormController.vue | 2 + src/plugins/gauge/gauge.scss | 12 ++--- 5 files changed, 94 insertions(+), 42 deletions(-) diff --git a/src/plugins/gauge/GaugePlugin.js b/src/plugins/gauge/GaugePlugin.js index c9db912df1..441e53cd57 100644 --- a/src/plugins/gauge/GaugePlugin.js +++ b/src/plugins/gauge/GaugePlugin.js @@ -49,6 +49,7 @@ export default function () { gaugeType: GAUGE_TYPES[0][1], isDisplayMinMax: true, isDisplayCurVal: true, + isDisplayUnits: true, isUseTelemetryLimits: true, limitLow: 10, limitHigh: 90, @@ -59,6 +60,23 @@ export default function () { }; }, form: [ + { + name: "Gauge type", + options: GAUGE_TYPES.map(type => { + return { + name: type[0], + value: type[1] + }; + }), + control: "select", + cssClass: "l-input-sm", + key: "gaugeController", + property: [ + "configuration", + "gaugeController", + "gaugeType" + ] + }, { name: "Display current value", control: "toggleSwitch", @@ -70,6 +88,17 @@ export default function () { "isDisplayCurVal" ] }, + { + name: "Display units", + control: "toggleSwitch", + cssClass: "l-input", + key: "isDisplayUnits", + property: [ + "configuration", + "gaugeController", + "isDisplayUnits" + ] + }, { name: "Display range values", control: "toggleSwitch", @@ -92,23 +121,6 @@ export default function () { "precision" ] }, - { - name: "Gauge type", - options: GAUGE_TYPES.map(type => { - return { - name: type[0], - value: type[1] - }; - }), - control: "select", - cssClass: "l-input-sm", - key: "gaugeController", - property: [ - "configuration", - "gaugeController", - "gaugeType" - ] - }, { name: "Value ranges and limits", control: "gauge-controller", diff --git a/src/plugins/gauge/GaugePluginSpec.js b/src/plugins/gauge/GaugePluginSpec.js index 5894498063..0c6b3d7a1f 100644 --- a/src/plugins/gauge/GaugePluginSpec.js +++ b/src/plugins/gauge/GaugePluginSpec.js @@ -63,30 +63,30 @@ describe('Gauge plugin', () => { }); it('Plugin installed by default', () => { - const gaugueType = openmct.types.get('gauge'); + const GaugeType = openmct.types.get('gauge'); - expect(gaugueType).not.toBeNull(); - expect(gaugueType.definition.name).toEqual('Gauge'); + expect(GaugeType).not.toBeNull(); + expect(GaugeType.definition.name).toEqual('Gauge'); }); - it('Gaugue plugin is creatable', () => { - const gaugueType = openmct.types.get('gauge'); + it('Gauge plugin is creatable', () => { + const GaugeType = openmct.types.get('gauge'); - expect(gaugueType.definition.creatable).toBeTrue(); + expect(GaugeType.definition.creatable).toBeTrue(); }); - it('Gaugue plugin is creatable', () => { - const gaugueType = openmct.types.get('gauge'); + it('Gauge plugin is creatable', () => { + const GaugeType = openmct.types.get('gauge'); - expect(gaugueType.definition.creatable).toBeTrue(); + expect(GaugeType.definition.creatable).toBeTrue(); }); - it('Gaugue form controller', () => { + it('Gauge form controller', () => { const gaugeController = openmct.forms.getFormControl('gauge-controller'); expect(gaugeController).toBeDefined(); }); - describe('Gaugue with Filled Dial', () => { + describe('Gauge with Filled Dial', () => { let gaugeViewProvider; let gaugeView; let gaugeViewObject; @@ -105,6 +105,7 @@ describe('Gauge plugin', () => { gaugeType: 'dial-filled', isDisplayMinMax: true, isDisplayCurVal: true, + isDisplayUnits: true, isUseTelemetryLimits: false, limitLow: -0.9, limitHigh: 0.9, @@ -222,7 +223,7 @@ describe('Gauge plugin', () => { }); }); - describe('Gaugue with Needle Dial', () => { + describe('Gauge with Needle Dial', () => { let gaugeViewProvider; let gaugeView; let gaugeViewObject; @@ -240,6 +241,7 @@ describe('Gauge plugin', () => { gaugeType: 'dial-needle', isDisplayMinMax: true, isDisplayCurVal: true, + isDisplayUnits: true, isUseTelemetryLimits: false, limitLow: -0.9, limitHigh: 0.9, @@ -357,7 +359,7 @@ describe('Gauge plugin', () => { }); }); - describe('Gaugue with Vertical Meter', () => { + describe('Gauge with Vertical Meter', () => { let gaugeViewProvider; let gaugeView; let gaugeViewObject; @@ -375,6 +377,7 @@ describe('Gauge plugin', () => { gaugeType: 'meter-vertical', isDisplayMinMax: true, isDisplayCurVal: true, + isDisplayUnits: true, isUseTelemetryLimits: false, limitLow: -0.9, limitHigh: 0.9, @@ -492,7 +495,7 @@ describe('Gauge plugin', () => { }); }); - describe('Gaugue with Vertical Meter Inverted', () => { + describe('Gauge with Vertical Meter Inverted', () => { let gaugeViewProvider; let gaugeView; let gaugeViewObject; @@ -506,6 +509,7 @@ describe('Gauge plugin', () => { gaugeType: 'meter-vertical', isDisplayMinMax: true, isDisplayCurVal: true, + isDisplayUnits: true, isUseTelemetryLimits: false, limitLow: -0.9, limitHigh: 0.9, @@ -574,7 +578,7 @@ describe('Gauge plugin', () => { }); }); - describe('Gaugue with Horizontal Meter', () => { + describe('Gauge with Horizontal Meter', () => { let gaugeViewProvider; let gaugeView; let gaugeViewObject; @@ -588,6 +592,7 @@ describe('Gauge plugin', () => { gaugeType: 'meter-vertical', isDisplayMinMax: true, isDisplayCurVal: true, + isDisplayUnits: true, isUseTelemetryLimits: false, limitLow: -0.9, limitHigh: 0.9, @@ -656,7 +661,7 @@ describe('Gauge plugin', () => { }); }); - describe('Gaugue with Filled Dial with Use Telemetry Limits', () => { + describe('Gauge with Filled Dial with Use Telemetry Limits', () => { let gaugeViewProvider; let gaugeView; let gaugeViewObject; @@ -673,6 +678,7 @@ describe('Gauge plugin', () => { gaugeType: 'dial-filled', isDisplayMinMax: true, isDisplayCurVal: true, + isDisplayUnits: true, isUseTelemetryLimits: true, limitLow: 10, limitHigh: 90, diff --git a/src/plugins/gauge/components/Gauge.vue b/src/plugins/gauge/components/Gauge.vue index 19fa4a8218..5c1ed0f503 100644 --- a/src/plugins/gauge/components/Gauge.vue +++ b/src/plugins/gauge/components/Gauge.vue @@ -64,11 +64,11 @@ @@ -79,6 +79,17 @@ style="transform: translate(50%, 70%)" >{{ curVal }} + + {{ units }} + {{ curVal }} + > + {{ curVal }} + {{ units }} + + {{ units }} @@ -288,12 +315,15 @@ export default { precision: gaugeController.precision, displayMinMax: gaugeController.isDisplayMinMax, displayCurVal: gaugeController.isDisplayCurVal, + displayUnits: gaugeController.isDisplayUnits, limitHigh: gaugeController.limitHigh, limitLow: gaugeController.limitLow, rangeHigh: gaugeController.max, rangeLow: gaugeController.min, gaugeType: gaugeController.gaugeType, - activeTimeSystem: this.openmct.time.timeSystem() + showUnits: gaugeController.showUnits, + activeTimeSystem: this.openmct.time.timeSystem(), + units: '' }; }, computed: { @@ -524,6 +554,8 @@ export default { const length = values.length; this.updateValue(values[length - 1]); }); + + this.units = this.metadata.value(this.valueKey).unit || ''; }, round(val, decimals = this.precision) { let precision = Math.pow(10, decimals); diff --git a/src/plugins/gauge/components/GaugeFormController.vue b/src/plugins/gauge/components/GaugeFormController.vue index ea80457880..648e0c12d3 100644 --- a/src/plugins/gauge/components/GaugeFormController.vue +++ b/src/plugins/gauge/components/GaugeFormController.vue @@ -111,6 +111,7 @@ export default { isUseTelemetryLimits: this.model.value.isUseTelemetryLimits, isDisplayMinMax: this.model.value.isDisplayMinMax, isDisplayCurVal: this.model.value.isDisplayCurVal, + isDisplayUnits: this.model.value.isDisplayUnits, limitHigh: this.model.value.limitHigh, limitLow: this.model.value.limitLow, max: this.model.value.max, @@ -125,6 +126,7 @@ export default { gaugeType: this.model.value.gaugeType, isDisplayMinMax: this.isDisplayMinMax, isDisplayCurVal: this.isDisplayCurVal, + isDisplayUnits: this.isDisplayUnits, isUseTelemetryLimits: this.isUseTelemetryLimits, limitLow: this.limitLow, limitHigh: this.limitHigh, diff --git a/src/plugins/gauge/gauge.scss b/src/plugins/gauge/gauge.scss index a56f566a6e..3ce533a033 100644 --- a/src/plugins/gauge/gauge.scss +++ b/src/plugins/gauge/gauge.scss @@ -16,13 +16,12 @@ // Both dial and meter types overflow: hidden; - &__range { + &__range, + &__units, + &__units text { $c: $colorGaugeRange; color: $c; - - text { - fill: $c; - } + fill: $c; } &__wrapper { @@ -66,7 +65,8 @@ svg[class*='c-dial'] { transition: transform $transitionTimeGauge; } - &__current-value-text { + &__current-value-text, + &__units-text { fill: $colorGaugeTextValue; font-family: $heroFont; } From 111b0d0d68d3f93cdd37b8b5ba81eaa1d989d38c Mon Sep 17 00:00:00 2001 From: Nikhil Date: Fri, 3 Jun 2022 18:24:43 -0700 Subject: [PATCH 222/283] Imagery layers (#4968) * Moved imagery controls to a separate component * Zoom pan controls moved to component * Implement adjustments to encapsulate state into ImageryControls * Track modifier key pressed for layouts * image control popup open/close fix * Styling for imagery local controls Co-authored-by: Michael Rogers Co-authored-by: Shefali Joshi Co-authored-by: Andrew Henry Co-authored-by: Scott Bell Co-authored-by: Charles Hacskaylo Co-authored-by: unlikelyzero Co-authored-by: Jamie Vigliotta Co-authored-by: John Hill --- e2e/test-data/PerformanceDisplayLayout.json | 2 +- e2e/tests/performance/imagery.perf.spec.js | 2 +- .../imagery/exampleImagery.e2e.spec.js | 57 +++-- example/imagery/plugin.js | 19 +- .../overlays/components/OverlayComponent.vue | 1 + .../displayLayout/components/LayoutFrame.vue | 3 +- .../components/layout-frame.scss | 4 - .../imagery/components/FilterSettings.vue | 74 ++++++ .../imagery/components/ImageControls.vue | 131 ++++++----- .../imagery/components/ImageryView.vue | 125 +++++++--- .../components/ImageryViewMenuSwitcher.vue | 65 ++++++ .../imagery/components/LayerSettings.vue | 59 +++++ .../imagery/components/ZoomSettings.vue | 89 +++++++ .../imagery/components/imagery-view.scss | 220 +++++++++++------- .../layers/example-imagery-layer-16x9.png | Bin 0 -> 8554 bytes .../layers/example-imagery-layer-safe.png | Bin 0 -> 9245 bytes .../layers/example-imagery-layer-scale.png | Bin 0 -> 11616 bytes src/plugins/imagery/pluginSpec.js | 24 ++ .../telemetryTable/components/table.scss | 5 +- src/styles/_controls.scss | 25 ++ src/styles/_global.scss | 19 ++ src/styles/_legacy-plots.scss | 4 +- src/styles/_table.scss | 2 +- src/ui/components/ObjectFrame.vue | 25 ++ src/ui/components/object-frame.scss | 12 +- webpack.common.js | 4 + 26 files changed, 748 insertions(+), 223 deletions(-) create mode 100644 src/plugins/imagery/components/FilterSettings.vue create mode 100644 src/plugins/imagery/components/ImageryViewMenuSwitcher.vue create mode 100644 src/plugins/imagery/components/LayerSettings.vue create mode 100644 src/plugins/imagery/components/ZoomSettings.vue create mode 100644 src/plugins/imagery/layers/example-imagery-layer-16x9.png create mode 100644 src/plugins/imagery/layers/example-imagery-layer-safe.png create mode 100644 src/plugins/imagery/layers/example-imagery-layer-scale.png diff --git a/e2e/test-data/PerformanceDisplayLayout.json b/e2e/test-data/PerformanceDisplayLayout.json index eebc7635ad..de81d7b4ca 100644 --- a/e2e/test-data/PerformanceDisplayLayout.json +++ b/e2e/test-data/PerformanceDisplayLayout.json @@ -1 +1 @@ -{"openmct":{"21338566-d472-4377-aed1-21b79272c8de":{"identifier":{"key":"21338566-d472-4377-aed1-21b79272c8de","namespace":""},"name":"Performance Display Layout","type":"layout","composition":[{"key":"644c2e47-2903-475f-8a4a-6be1588ee02f","namespace":""}],"configuration":{"items":[{"width":32,"height":18,"x":1,"y":1,"identifier":{"key":"644c2e47-2903-475f-8a4a-6be1588ee02f","namespace":""},"hasFrame":true,"fontSize":"default","font":"default","type":"subobject-view","id":"5aeb5a71-3149-41ed-9d8a-d34b0a18b053"}],"layoutGrid":[10,10]},"modified":1652228997384,"location":"mine","persisted":1652228997384},"644c2e47-2903-475f-8a4a-6be1588ee02f":{"identifier":{"key":"644c2e47-2903-475f-8a4a-6be1588ee02f","namespace":""},"name":"Performance Example Imagery","type":"example.imagery","configuration":{"imageLocation":"","imageLoadDelayInMilliSeconds":20000,"imageSamples":[]},"telemetry":{"values":[{"name":"Name","key":"name"},{"name":"Time","key":"utc","format":"utc","hints":{"domain":2}},{"name":"Local Time","key":"local","format":"local-format","hints":{"domain":1}},{"name":"Image","key":"url","format":"image","hints":{"image":1}},{"name":"Image Download Name","key":"imageDownloadName","format":"imageDownloadName","hints":{"imageDownloadName":1}}]},"modified":1652228997375,"location":"21338566-d472-4377-aed1-21b79272c8de","persisted":1652228997375}},"rootId":"21338566-d472-4377-aed1-21b79272c8de"} \ No newline at end of file +{"openmct":{"b3cee102-86dd-4c0a-8eec-4d5d276f8691":{"identifier":{"key":"b3cee102-86dd-4c0a-8eec-4d5d276f8691","namespace":""},"name":"Performance Display Layout","type":"layout","composition":[{"key":"9666e7b4-be0c-47a5-94b8-99accad7155e","namespace":""}],"configuration":{"items":[{"width":32,"height":18,"x":12,"y":9,"identifier":{"key":"9666e7b4-be0c-47a5-94b8-99accad7155e","namespace":""},"hasFrame":true,"fontSize":"default","font":"default","type":"subobject-view","id":"23ca351d-a67d-46aa-a762-290eb742d2f1"}],"layoutGrid":[10,10]},"modified":1654299875432,"location":"mine","persisted":1654299878751},"9666e7b4-be0c-47a5-94b8-99accad7155e":{"identifier":{"key":"9666e7b4-be0c-47a5-94b8-99accad7155e","namespace":""},"name":"Performance Example Imagery","type":"example.imagery","configuration":{"imageLocation":"","imageLoadDelayInMilliSeconds":20000,"imageSamples":[],"layers":[{"source":"dist/imagery/example-imagery-layer-16x9.png","name":"16:9","visible":false},{"source":"dist/imagery/example-imagery-layer-safe.png","name":"Safe","visible":false},{"source":"dist/imagery/example-imagery-layer-scale.png","name":"Scale","visible":false}]},"telemetry":{"values":[{"name":"Name","key":"name"},{"name":"Time","key":"utc","format":"utc","hints":{"domain":2}},{"name":"Local Time","key":"local","format":"local-format","hints":{"domain":1}},{"name":"Image","key":"url","format":"image","hints":{"image":1},"layers":[{"source":"dist/imagery/example-imagery-layer-16x9.png","name":"16:9"},{"source":"dist/imagery/example-imagery-layer-safe.png","name":"Safe"},{"source":"dist/imagery/example-imagery-layer-scale.png","name":"Scale"}]},{"name":"Image Download Name","key":"imageDownloadName","format":"imageDownloadName","hints":{"imageDownloadName":1}}]},"modified":1654299840077,"location":"b3cee102-86dd-4c0a-8eec-4d5d276f8691","persisted":1654299840078}},"rootId":"b3cee102-86dd-4c0a-8eec-4d5d276f8691"} \ No newline at end of file diff --git a/e2e/tests/performance/imagery.perf.spec.js b/e2e/tests/performance/imagery.perf.spec.js index e7033ef10d..433bc1699d 100644 --- a/e2e/tests/performance/imagery.perf.spec.js +++ b/e2e/tests/performance/imagery.perf.spec.js @@ -164,7 +164,7 @@ test.describe('Performance tests', () => { console.log('jpgResourceTiming ' + JSON.stringify(jpgResourceTiming)); // Click Close Icon - await page.locator('.c-click-icon').click(); + await page.locator('[aria-label="Close"]').click(); await page.evaluate(() => window.performance.mark("view-large-close-button")); //await client.send('HeapProfiler.enable'); diff --git a/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js b/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js index d5c204cdb2..0a570e5c2a 100644 --- a/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js +++ b/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js @@ -32,7 +32,6 @@ const { expect } = require('@playwright/test'); test.describe('Example Imagery', () => { test.beforeEach(async ({ page }) => { - page.on('console', msg => console.log(msg.text())); //Go to baseURL await page.goto('/', { waitUntil: 'networkidle' }); @@ -61,19 +60,19 @@ test.describe('Example Imagery', () => { test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => { const bgImageLocator = page.locator(backgroundImageSelector); const deltaYStep = 100; //equivalent to 1x zoom - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox(); // zoom in - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); await page.mouse.wheel(0, deltaYStep * 2); // wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox(); // zoom out - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); await page.mouse.wheel(0, -deltaYStep); // wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); const imageMouseZoomedOut = await page.locator(backgroundImageSelector).boundingBox(); expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height); @@ -88,11 +87,11 @@ test.describe('Example Imagery', () => { const panHotkey = process.platform === 'linux' ? ['Control', 'Alt'] : ['Alt']; const bgImageLocator = page.locator(backgroundImageSelector); - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); // zoom in await page.mouse.wheel(0, deltaYStep * 2); - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); const zoomedBoundingBox = await bgImageLocator.boundingBox(); const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2; const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2; @@ -151,22 +150,22 @@ test.describe('Example Imagery', () => { test('Can use + - buttons to zoom on the image', async ({ page }) => { const bgImageLocator = page.locator(backgroundImageSelector); - await bgImageLocator.hover(); - const zoomInBtn = page.locator('.t-btn-zoom-in'); - const zoomOutBtn = page.locator('.t-btn-zoom-out'); + await bgImageLocator.hover({trial: true}); + const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0); + const zoomOutBtn = page.locator('.t-btn-zoom-out').nth(0); const initialBoundingBox = await bgImageLocator.boundingBox(); await zoomInBtn.click(); await zoomInBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); const zoomedInBoundingBox = await bgImageLocator.boundingBox(); expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height); expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width); await zoomOutBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); const zoomedOutBoundingBox = await bgImageLocator.boundingBox(); expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height); expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width); @@ -176,18 +175,18 @@ test.describe('Example Imagery', () => { test('Can use the reset button to reset the image', async ({ page }) => { const bgImageLocator = page.locator(backgroundImageSelector); // wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); - const zoomInBtn = page.locator('.t-btn-zoom-in'); - const zoomResetBtn = page.locator('.t-btn-zoom-reset'); + const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0); + const zoomResetBtn = page.locator('.t-btn-zoom-reset').nth(0); const initialBoundingBox = await bgImageLocator.boundingBox(); await zoomInBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); await zoomInBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); const zoomedInBoundingBox = await bgImageLocator.boundingBox(); expect.soft(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height); @@ -195,7 +194,7 @@ test.describe('Example Imagery', () => { await zoomResetBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); const resetBoundingBox = await bgImageLocator.boundingBox(); expect.soft(resetBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height); @@ -209,18 +208,18 @@ test.describe('Example Imagery', () => { const bgImageLocator = page.locator(backgroundImageSelector); const pausePlayButton = page.locator('.c-button.pause-play'); // wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); // open the time conductor drop down - await page.locator('.c-conductor__controls button.c-mode-button').click(); + await page.locator('button:has-text("Fixed Timespan")').click(); // Click local clock - await page.locator('.icon-clock >> text=Local Clock').click(); + await page.locator('[data-testid="conductor-modeOption-realtime"]').click(); await expect.soft(pausePlayButton).not.toHaveClass(/is-paused/); - const zoomInBtn = page.locator('.t-btn-zoom-in'); + const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0); await zoomInBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); return expect(pausePlayButton).not.toHaveClass(/is-paused/); }); @@ -267,7 +266,7 @@ test('Example Imagery in Display layout', async ({ page }) => { await page.waitForSelector('.c-message-banner__message', { state: 'detached'}); await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery'); const bgImageLocator = page.locator(backgroundImageSelector); - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); // Click previous image button const previousImageButton = page.locator('.c-nav--prev'); @@ -279,7 +278,7 @@ test('Example Imagery in Display layout', async ({ page }) => { // Zoom in const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox(); - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); const deltaYStep = 100; // equivalent to 1x zoom await page.mouse.wheel(0, deltaYStep * 2); const zoomedBoundingBox = await bgImageLocator.boundingBox(); @@ -287,7 +286,7 @@ test('Example Imagery in Display layout', async ({ page }) => { const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2; // Wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox(); expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height); expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width); @@ -311,11 +310,11 @@ test('Example Imagery in Display layout', async ({ page }) => { await page.locator('[data-testid=conductor-modeOption-realtime]').click(); // Zoom in on next image - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); await page.mouse.wheel(0, deltaYStep * 2); // Wait for zoom animation to finish - await bgImageLocator.hover(); + await bgImageLocator.hover({trial: true}); const imageNextMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox(); expect(imageNextMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height); expect(imageNextMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width); diff --git a/example/imagery/plugin.js b/example/imagery/plugin.js index 6823ede509..47d6f4ef70 100644 --- a/example/imagery/plugin.js +++ b/example/imagery/plugin.js @@ -59,7 +59,8 @@ export default function () { object.configuration = { imageLocation: '', imageLoadDelayInMilliSeconds: DEFAULT_IMAGE_LOAD_DELAY_IN_MILISECONDS, - imageSamples: [] + imageSamples: [], + layers: [] }; object.telemetry = { @@ -90,7 +91,21 @@ export default function () { format: 'image', hints: { image: 1 - } + }, + layers: [ + { + source: 'dist/imagery/example-imagery-layer-16x9.png', + name: '16:9' + }, + { + source: 'dist/imagery/example-imagery-layer-safe.png', + name: 'Safe' + }, + { + source: 'dist/imagery/example-imagery-layer-scale.png', + name: 'Scale' + } + ] }, { name: 'Image Download Name', diff --git a/src/api/overlays/components/OverlayComponent.vue b/src/api/overlays/components/OverlayComponent.vue index f8a66f7597..9742fd7367 100644 --- a/src/api/overlays/components/OverlayComponent.vue +++ b/src/api/overlays/components/OverlayComponent.vue @@ -7,6 +7,7 @@
diff --git a/src/plugins/displayLayout/components/LayoutFrame.vue b/src/plugins/displayLayout/components/LayoutFrame.vue index 052fa63a3c..c81fb80f71 100644 --- a/src/plugins/displayLayout/components/LayoutFrame.vue +++ b/src/plugins/displayLayout/components/LayoutFrame.vue @@ -25,8 +25,7 @@ class="l-layout__frame c-frame" :class="{ 'no-frame': !item.hasFrame, - 'u-inspectable': inspectable, - 'is-in-small-container': size.width < 600 || size.height < 600 + 'u-inspectable': inspectable }" :style="style" > diff --git a/src/plugins/displayLayout/components/layout-frame.scss b/src/plugins/displayLayout/components/layout-frame.scss index 63f2299ecb..d036814021 100644 --- a/src/plugins/displayLayout/components/layout-frame.scss +++ b/src/plugins/displayLayout/components/layout-frame.scss @@ -9,10 +9,6 @@ > *:first-child { flex: 1 1 auto; } - - &.is-in-small-container { - //background: rgba(blue, 0.1); - } } .c-frame__move-bar { diff --git a/src/plugins/imagery/components/FilterSettings.vue b/src/plugins/imagery/components/FilterSettings.vue new file mode 100644 index 0000000000..c88d215d55 --- /dev/null +++ b/src/plugins/imagery/components/FilterSettings.vue @@ -0,0 +1,74 @@ + + + diff --git a/src/plugins/imagery/components/ImageControls.vue b/src/plugins/imagery/components/ImageControls.vue index a14a402df5..b14c13f8b2 100644 --- a/src/plugins/imagery/components/ImageControls.vue +++ b/src/plugins/imagery/components/ImageControls.vue @@ -21,75 +21,62 @@ *****************************************************************************/ diff --git a/src/plugins/imagery/components/LayerSettings.vue b/src/plugins/imagery/components/LayerSettings.vue new file mode 100644 index 0000000000..1e99a0aee6 --- /dev/null +++ b/src/plugins/imagery/components/LayerSettings.vue @@ -0,0 +1,59 @@ + + + diff --git a/src/plugins/imagery/components/ZoomSettings.vue b/src/plugins/imagery/components/ZoomSettings.vue new file mode 100644 index 0000000000..e53d6289ed --- /dev/null +++ b/src/plugins/imagery/components/ZoomSettings.vue @@ -0,0 +1,89 @@ + + + diff --git a/src/plugins/imagery/components/imagery-view.scss b/src/plugins/imagery/components/imagery-view.scss index 8cdae861db..e781fdce9a 100644 --- a/src/plugins/imagery/components/imagery-view.scss +++ b/src/plugins/imagery/components/imagery-view.scss @@ -28,6 +28,27 @@ display: flex; flex-direction: column; flex: 1 1 auto; + + &.unnsynced{ + @include sUnsynced(); + } + + &.cursor-zoom-in { + cursor: zoom-in; + } + + &.cursor-zoom-out { + cursor: zoom-out; + } + + &.pannable { + @include cursorGrab(); + } + } + + .image-wrapper { + overflow: visible clip; + background-image: repeating-linear-gradient(45deg, transparent, transparent 4px, rgba(125, 125, 125, 0.2) 4px, rgba(125, 125, 125, 0.2) 8px); } .image-wrapper { @@ -45,19 +66,6 @@ flex: 1 1 auto; height: 0; overflow: hidden; - &.unnsynced{ - @include sUnsynced(); - } - &.cursor-zoom-in { - cursor: zoom-in; - } - &.cursor-zoom-out { - cursor: zoom-out; - } - &.pannable { - @include cursorGrab(); - - } } &__background-image { background-position: center; @@ -77,6 +85,7 @@ background: rgba(black, 0.2); border-radius: $smallCr; padding: 2px $interiorMargin; + pointer-events: none; position: absolute; right: $m; top: $m; @@ -146,6 +155,11 @@ } + &__layer-image { + pointer-events: none; + z-index: 1; + } + &__thumbs-wrapper { display: flex; // Uses row layout justify-content: flex-end; @@ -179,6 +193,50 @@ font-size: 0.8em; margin: $interiorMarginSm; } + + .c-control-menu { + // Controls on left of flex column layout, close btn on right + @include menuOuter(); + + border-radius: $controlCr; + display: flex; + align-items: flex-start; + justify-content: space-between; + padding: $interiorMargin; + width: min-content; + + > * + * { + margin-left: $interiorMargin; + } + } + + .c-switcher-menu { + display: contents; + + &__content { + // Menu panel + top: 28px; + position: absolute; + + .c-so-view & { + top: 25px; + } + } + } +} + +.--width-less-than-220 .--show-if-less-than-220.c-switcher-menu { + display: contents !important; +} + +.s-image-layer { + position: absolute; + height: 100%; + width: 100%; + opacity: 0.5; + background-size: contain; + background-repeat: no-repeat; + background-position: center; } /*************************************** THUMBS */ @@ -229,70 +287,36 @@ /*************************************** IMAGERY LOCAL CONTROLS*/ .c-imagery { .h-local-controls--overlay-content { + display: flex; + flex-direction: row; position: absolute; left: $interiorMargin; top: $interiorMargin; z-index: 70; background: $colorLocalControlOvrBg; border-radius: $basicCr; - max-width: 250px; - min-width: 170px; - width: 35%; align-items: center; - padding: $interiorMargin $interiorMarginLg; - - input[type="range"] { - display: block; - width: 100%; - &:not(:first-child) { - margin-top: $interiorMarginLg; - } - - &:before { - margin-right: $interiorMarginSm; - } - } + padding: $interiorMargin $interiorMargin; .s-status-taking-snapshot & { display: none; } } - - &__lc { - &__reset-btn { - // Span that holds bracket graphics and button - $bc: $scrollbarTrackColorBg; - - &:before, - &:after { - border-right: 1px solid $bc; - content:''; - display: block; - width: 5px; - height: 4px; - } - - &:before { - border-top: 1px solid $bc; - margin-bottom: 2px; - } - - &:after { - border-bottom: 1px solid $bc; - margin-top: 2px; - } - - .c-icon-link { - color: $colorBtnFg; - } + [class*='--menus-aligned'] { + > * + * { + button { margin-left: $interiorMarginSm; } } } } .c-image-controls { + &__controls-wrapper { + // Wraps __controls and __close-btn + display: flex; + } + &__controls { display: flex; align-items: stretch; - flex-direction: column; > * + * { margin-top: $interiorMargin; @@ -314,31 +338,67 @@ } - &__input { - // A wrapper is needed to add the type icon to left of each control - - input[type='range'] { - //width: 100%; // Do we need this? - } - } - &__zoom { - > * + * { margin-left: $interiorMargin; } + > * + * { margin-left: $interiorMargin; } // Is this used? } - &__sliders { - display: flex; - flex: 1 1 auto; - flex-direction: column; + &--filters { + // Styles specific to the brightness and contrast controls - > * + * { - margin-top: 11px; + .c-image-controls { + &__sliders { + display: flex; + flex: 1 1 auto; + flex-direction: column; + min-width: 80px; + + > * + * { + margin-top: 11px; + } + + input[type="range"] { + display: block; + width: 100%; + } + } + + &__slider-wrapper { + display: flex; + align-items: center; + + &:before { margin-right: $interiorMargin; } + } + + &__reset-btn { + // Span that holds bracket graphics and button + $bc: $scrollbarTrackColorBg; + flex: 0 0 auto; + + &:before, + &:after { + border-right: 1px solid $bc; + content:''; + display: block; + width: 5px; + height: 4px; + } + + &:before { + border-top: 1px solid $bc; + margin-bottom: 2px; + } + + &:after { + border-bottom: 1px solid $bc; + margin-top: 2px; + } + + .c-icon-link { + color: $colorBtnFg; + } + } } } - - &__btn-reset { - flex: 0 0 auto; - } } /*************************************** BUTTONS */ @@ -383,7 +443,7 @@ @include cArrowButtonSizing($dimOuter: 48px); border-radius: $controlCr; - .is-in-small-container & { + .--width-less-than-600 & { @include cArrowButtonSizing($dimOuter: 32px); } } @@ -409,10 +469,6 @@ background-color: $colorBodyFg; } - //[class*='__image-placeholder'] { - // display: none; - //} - img { display: block !important; } diff --git a/src/plugins/imagery/layers/example-imagery-layer-16x9.png b/src/plugins/imagery/layers/example-imagery-layer-16x9.png new file mode 100644 index 0000000000000000000000000000000000000000..877a0e6038b220964e2d232ab10c7a37f6eaf055 GIT binary patch literal 8554 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 { location: "parentId", modified: 0, persisted: 0, + configuration: { + layers: [{ + name: '16:9', + visible: true + }] + }, telemetry: { values: [ { "name": "Image", "key": "url", "format": "image", + "layers": [ + { + source: location.host + '/images/bg-splash.jpg', + name: '16:9' + } + ], "hints": { "image": 1, "priority": 3 @@ -366,6 +378,18 @@ describe("The Imagery View Layouts", () => { }); }); + it("on mount should show the any image layers", (done) => { + //Looks like we need Vue.nextTick here so that computed properties settle down + Vue.nextTick().then(() => { + Vue.nextTick(() => { + const layerEls = parent.querySelectorAll('.js-layer-image'); + console.log(layerEls); + expect(layerEls.length).toEqual(1); + done(); + }); + }); + }); + it("should show the clicked thumbnail as the main image", (done) => { //Looks like we need Vue.nextTick here so that computed properties settle down Vue.nextTick(() => { diff --git a/src/plugins/telemetryTable/components/table.scss b/src/plugins/telemetryTable/components/table.scss index 512af8c3a1..03d54c0f72 100644 --- a/src/plugins/telemetryTable/components/table.scss +++ b/src/plugins/telemetryTable/components/table.scss @@ -63,8 +63,9 @@ padding-top: 0; padding-bottom: 0; } - .is-in-small-container & { - display: none; + + .--width-less-than-600 & { + display: none !important; } } } diff --git a/src/styles/_controls.scss b/src/styles/_controls.scss index ac11686977..3e356036d6 100644 --- a/src/styles/_controls.scss +++ b/src/styles/_controls.scss @@ -42,6 +42,17 @@ } } +@mixin menuPositioning() { + display: flex; + flex-direction: column; + position: absolute; + z-index: 100; + + > * { + flex: 0 0 auto; + } +} + @mixin menuInner() { li { @include cControl(); @@ -479,6 +490,10 @@ select { &__row { > * + * { margin-left: $interiorMargin; } } + + li { + white-space: nowrap; + } } /******************************************************** TABS */ @@ -567,6 +582,7 @@ select { /******************************************************** MENUS */ .c-menu { @include menuOuter(); + @include menuPositioning(); @include menuInner(); &__section-hint { @@ -590,6 +606,7 @@ select { .c-super-menu { // Two column layout, menu items on left with detail of hover element on right @include menuOuter(); + @include menuPositioning(); display: flex; padding: $interiorMarginLg; flex-direction: row; @@ -1035,6 +1052,14 @@ input[type="range"] { display: inline-flex; align-items: center; } + + [class*='--menus-aligned'] { + // Contains top level elements that hold dropdown menus + // Top level elements use display: contents to allow their menus to compactly align + // 03-18-22: used in ImageControls.vue + display: flex; + flex-direction: row; + } } .c-local-controls { diff --git a/src/styles/_global.scss b/src/styles/_global.scss index 5b2efb8efd..46ab0ad66b 100644 --- a/src/styles/_global.scss +++ b/src/styles/_global.scss @@ -349,3 +349,22 @@ body.desktop .has-local-controls { pointer-events: none !important; cursor: default !important; } + +/******************************************************** RESPONSIVE CONTAINERS */ +@mixin responsiveContainerWidths($dimension) { + // 3/21/22: `--width-less-than*` classes set in ObjectView.vue + .--show-if-less-than-#{$dimension} { + // Hide anything that displays within a given width by default. + // `display` property must be set within a more specific class + // for the particular item to be displayed. + display: none !important + } + + .--width-less-than-#{$dimension} { + .--hide-if-less-than-#{$dimension} { display: none; } + } +} + +//.--hide-by-default { display: none !important; } +@include responsiveContainerWidths('220'); +@include responsiveContainerWidths('600'); diff --git a/src/styles/_legacy-plots.scss b/src/styles/_legacy-plots.scss index 4ec0529e0f..b9ae1b6d9b 100644 --- a/src/styles/_legacy-plots.scss +++ b/src/styles/_legacy-plots.scss @@ -118,7 +118,7 @@ mct-plot { } } - .is-in-small-container & { + .--width-less-than-600 & { .c-control-bar { display: none; } @@ -498,7 +498,7 @@ mct-plot { margin-bottom: $interiorMarginSm; } - .is-in-small-container & { + .--width-less-than-600 & { &.is-legend-hidden { display: none; } diff --git a/src/styles/_table.scss b/src/styles/_table.scss index 12540ae933..d6c85206d0 100644 --- a/src/styles/_table.scss +++ b/src/styles/_table.scss @@ -90,7 +90,7 @@ div.c-table { flex: 1 1 auto; } - .is-in-small-container & { + .--width-less-than-600 & { &:not(.is-paused) { .c-table-control-bar { display: none; diff --git a/src/ui/components/ObjectFrame.vue b/src/ui/components/ObjectFrame.vue index cef8931873..1254cc3cbb 100644 --- a/src/ui/components/ObjectFrame.vue +++ b/src/ui/components/ObjectFrame.vue @@ -21,9 +21,11 @@ *****************************************************************************/