From 29d83e9c6d3019f603f6c71b750e8f9c74984f4a Mon Sep 17 00:00:00 2001 From: Jesse Mazzella Date: Wed, 21 Feb 2024 16:07:48 -0800 Subject: [PATCH] fix(#7456): allow children of Overlay Plots to be removed (#7516) * fix(#7456): check if object is an identifier * refactor: use v-show, remove unnecessary deep copy, set yAxes array to empty in a way that preserves reactivity * refactor: use ESM exports * refactor: use ESM imports * test(e2e): add test for element item removal from overlay plot * a11y: add accessible name for object labels * refactor: move overlayPlot creation to beforeAll, use getByLabel --- .../plugins/plot/overlayPlot.e2e.spec.js | 45 +++++++------ src/api/composition/CompositionProvider.js | 12 ++-- .../composition/DefaultCompositionProvider.js | 8 +-- src/api/objects/MutableDomainObject.js | 6 +- src/api/objects/ObjectAPI.js | 67 +++++++++++-------- src/api/objects/RootRegistry.js | 6 +- src/api/objects/TransactionSpec.js | 6 +- src/api/objects/object-utils.js | 29 +++----- src/api/objects/test/object-utilsSpec.js | 32 ++++----- src/api/telemetry/TelemetryAPI.js | 8 +-- .../ImportFromJSONAction.js | 4 +- .../inspectorViews/elements/ElementItem.vue | 17 +++-- .../elements/PlotElementsPool.vue | 11 +-- .../notebook/utils/notebook-storage.js | 5 +- .../staticRootPlugin/StaticModelProvider.js | 18 ++--- .../summaryWidget/src/ConditionManager.js | 10 +-- .../summaryWidget/src/input/ObjectSelect.js | 6 +- .../src/telemetry/EvaluatorPool.js | 4 +- .../src/telemetry/SummaryWidgetEvaluator.js | 6 +- .../src/MeanTelemetryProvider.js | 8 +-- src/ui/components/ObjectLabel.vue | 14 ++++ src/ui/components/ObjectView.vue | 4 +- src/ui/composables/edit.js | 43 ++++++++++++ src/ui/mixins/staleness-mixin.js | 6 +- 24 files changed, 223 insertions(+), 152 deletions(-) create mode 100644 src/ui/composables/edit.js diff --git a/e2e/tests/functional/plugins/plot/overlayPlot.e2e.spec.js b/e2e/tests/functional/plugins/plot/overlayPlot.e2e.spec.js index 54c76aae97..1c9c8d968a 100644 --- a/e2e/tests/functional/plugins/plot/overlayPlot.e2e.spec.js +++ b/e2e/tests/functional/plugins/plot/overlayPlot.e2e.spec.js @@ -33,15 +33,15 @@ import { import { expect, test } from '../../../../pluginFixtures.js'; test.describe('Overlay Plot', () => { + let overlayPlot; test.beforeEach(async ({ page }) => { await page.goto('./', { waitUntil: 'domcontentloaded' }); + overlayPlot = await createDomainObjectWithDefaults(page, { + type: 'Overlay Plot' + }); }); test('Plot legend color is in sync with plot series color', async ({ page }) => { - const overlayPlot = await createDomainObjectWithDefaults(page, { - type: 'Overlay Plot' - }); - await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator', parent: overlayPlot.uuid @@ -68,9 +68,6 @@ test.describe('Overlay Plot', () => { type: 'issue', description: 'https://github.com/nasa/openmct/issues/7403' }); - const overlayPlot = await createDomainObjectWithDefaults(page, { - type: 'Overlay Plot' - }); await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator', @@ -130,10 +127,6 @@ test.describe('Overlay Plot', () => { type: 'issue', description: 'https://github.com/nasa/openmct/issues/6338' }); - // Create an Overlay Plot with a default SWG - const overlayPlot = await createDomainObjectWithDefaults(page, { - type: 'Overlay Plot' - }); const swgA = await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator', @@ -199,10 +192,6 @@ test.describe('Overlay Plot', () => { test('The elements pool supports dragging series into multiple y-axis buckets', async ({ page }) => { - const overlayPlot = await createDomainObjectWithDefaults(page, { - type: 'Overlay Plot' - }); - const swgA = await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator', parent: overlayPlot.uuid @@ -292,10 +281,6 @@ test.describe('Overlay Plot', () => { description: 'https://github.com/nasa/openmct/issues/7421' }); - const overlayPlot = await createDomainObjectWithDefaults(page, { - type: 'Overlay Plot' - }); - const swgA = await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator', parent: overlayPlot.uuid @@ -309,12 +294,32 @@ test.describe('Overlay Plot', () => { await page.getByRole('tab', { name: 'Elements' }).click(); await page.locator(`#inspector-elements-tree >> text=${swgA.name}`).click(); - const plotPixels = await getCanvasPixels(page, '.js-overlay canvas'); const plotPixelSize = plotPixels.length; expect(plotPixelSize).toBeGreaterThan(0); } ); + + test('Can remove an item via the elements pool action menu', async ({ page }) => { + const swgA = await createDomainObjectWithDefaults(page, { + type: 'Sine Wave Generator', + parent: overlayPlot.uuid + }); + + await page.goto(overlayPlot.url); + // Wait for plot series data to load and be drawn + await waitForPlotsToRender(page); + await page.getByLabel('Edit Object').click(); + + await page.getByRole('tab', { name: 'Elements' }).click(); + + const swgAElementsPoolItem = page.getByLabel(`Preview ${swgA.name}`); + await expect(swgAElementsPoolItem).toBeVisible(); + await swgAElementsPoolItem.click({ button: 'right' }); + await page.getByRole('menuitem', { name: 'Remove' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect(swgAElementsPoolItem).toBeHidden(); + }); }); /** diff --git a/src/api/composition/CompositionProvider.js b/src/api/composition/CompositionProvider.js index 0b669f86e0..ec68648a68 100644 --- a/src/api/composition/CompositionProvider.js +++ b/src/api/composition/CompositionProvider.js @@ -21,7 +21,7 @@ *****************************************************************************/ import _ from 'lodash'; -import objectUtils from '../objects/object-utils.js'; +import { makeKeyString, parseKeyString } from '../objects/object-utils.js'; /** * @typedef {import('../objects/ObjectAPI').DomainObject} DomainObject @@ -223,18 +223,18 @@ export default class CompositionProvider { * @param {DomainObject} oldDomainObject */ #onMutation(newDomainObject, oldDomainObject) { - const id = objectUtils.makeKeyString(oldDomainObject.identifier); + const id = makeKeyString(oldDomainObject.identifier); const listeners = this.#listeningTo[id]; if (!listeners) { return; } - const oldComposition = oldDomainObject.composition.map(objectUtils.makeKeyString); - const newComposition = newDomainObject.composition.map(objectUtils.makeKeyString); + const oldComposition = oldDomainObject.composition.map(makeKeyString); + const newComposition = newDomainObject.composition.map(makeKeyString); - const added = _.difference(newComposition, oldComposition).map(objectUtils.parseKeyString); - const removed = _.difference(oldComposition, newComposition).map(objectUtils.parseKeyString); + const added = _.difference(newComposition, oldComposition).map(parseKeyString); + const removed = _.difference(oldComposition, newComposition).map(parseKeyString); function notify(value) { return function (listener) { diff --git a/src/api/composition/DefaultCompositionProvider.js b/src/api/composition/DefaultCompositionProvider.js index c5c595dd63..05a6603505 100644 --- a/src/api/composition/DefaultCompositionProvider.js +++ b/src/api/composition/DefaultCompositionProvider.js @@ -21,7 +21,7 @@ *****************************************************************************/ import { toRaw } from 'vue'; -import objectUtils from '../objects/object-utils.js'; +import { makeKeyString } from '../objects/object-utils.js'; import CompositionProvider from './CompositionProvider.js'; /** @@ -91,7 +91,7 @@ export default class DefaultCompositionProvider extends CompositionProvider { this.establishTopicListener(); /** @type {string} */ - const keyString = objectUtils.makeKeyString(domainObject.identifier); + const keyString = makeKeyString(domainObject.identifier); let objectListeners = this.listeningTo[keyString]; if (!objectListeners) { @@ -120,7 +120,7 @@ export default class DefaultCompositionProvider extends CompositionProvider { */ off(domainObject, event, callback, context) { /** @type {string} */ - const keyString = objectUtils.makeKeyString(domainObject.identifier); + const keyString = makeKeyString(domainObject.identifier); const objectListeners = this.listeningTo[keyString]; const index = objectListeners[event].findIndex((l) => { @@ -228,7 +228,7 @@ export default class DefaultCompositionProvider extends CompositionProvider { this.publicAPI.objects.mutate(domainObject, 'composition', newComposition); /** @type {string} */ - let id = objectUtils.makeKeyString(domainObject.identifier); + let id = makeKeyString(domainObject.identifier); const listeners = this.listeningTo[id]; if (!listeners) { diff --git a/src/api/objects/MutableDomainObject.js b/src/api/objects/MutableDomainObject.js index f83a26401a..80c46598ff 100644 --- a/src/api/objects/MutableDomainObject.js +++ b/src/api/objects/MutableDomainObject.js @@ -22,7 +22,7 @@ import EventEmitter from 'EventEmitter'; import _ from 'lodash'; -import utils from './object-utils.js'; +import { makeKeyString, refresh } from './object-utils.js'; const ANY_OBJECT_EVENT = 'mutation'; @@ -152,7 +152,7 @@ class MutableDomainObject { mutable.$observe('$_synchronize_model', (updatedObject) => { let clone = JSON.parse(JSON.stringify(updatedObject)); - utils.refresh(mutable, clone); + refresh(mutable, clone); }); return mutable; @@ -168,7 +168,7 @@ class MutableDomainObject { } function qualifiedEventName(object, eventName) { - let keystring = utils.makeKeyString(object.identifier); + let keystring = makeKeyString(object.identifier); return [keystring, eventName].join(':'); } diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index d28d193605..3615f3c94c 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -21,7 +21,7 @@ *****************************************************************************/ import EventEmitter from 'EventEmitter'; -import utils from 'objectUtils'; +import { identifierEquals, makeKeyString, parseKeyString, refresh } from 'objectUtils'; import ConflictError from './ConflictError.js'; import InMemorySearchProvider from './InMemorySearchProvider.js'; @@ -82,8 +82,19 @@ import Transaction from './Transaction.js'; * @memberof module:openmct */ export default class ObjectAPI { + #makeKeyString; + #parseKeyString; + #identifierEquals; + #refresh; + #openmct; + constructor(typeRegistry, openmct) { - this.openmct = openmct; + this.#makeKeyString = makeKeyString; + this.#parseKeyString = parseKeyString; + this.#identifierEquals = identifierEquals; + this.#refresh = refresh; + this.#openmct = openmct; + this.typeRegistry = typeRegistry; this.SEARCH_TYPES = Object.freeze({ OBJECTS: 'OBJECTS', @@ -206,14 +217,14 @@ export default class ObjectAPI { * has been saved, or be rejected if it cannot be saved */ get(identifier, abortSignal, forceRemote = false) { - let keystring = this.makeKeyString(identifier); + let keystring = this.#makeKeyString(identifier); if (!forceRemote) { if (this.cache[keystring] !== undefined) { return this.cache[keystring]; } - identifier = utils.parseKeyString(identifier); + identifier = parseKeyString(identifier); if (this.isTransactionActive()) { let dirtyObject = this.transaction.getDirtyObject(identifier); @@ -227,7 +238,7 @@ export default class ObjectAPI { const provider = this.getProvider(identifier); if (!provider) { - throw new Error(`No Provider Matched for keyString "${this.makeKeyString(identifier)}"`); + throw new Error(`No Provider Matched for keyString "${this.#makeKeyString(identifier)}"`); } if (!provider.get) { @@ -325,7 +336,7 @@ export default class ObjectAPI { */ getMutable(identifier) { if (!this.supportsMutation(identifier)) { - throw new Error(`Object "${this.makeKeyString(identifier)}" does not support mutation.`); + throw new Error(`Object "${this.#makeKeyString(identifier)}" does not support mutation.`); } return this.get(identifier).then((object) => { @@ -352,7 +363,7 @@ export default class ObjectAPI { } isPersistable(idOrKeyString) { - let identifier = utils.parseKeyString(idOrKeyString); + let identifier = parseKeyString(idOrKeyString); let provider = this.getProvider(identifier); if (provider?.isReadOnly) { return !provider.isReadOnly(); @@ -362,7 +373,7 @@ export default class ObjectAPI { } isMissing(domainObject) { - let identifier = utils.makeKeyString(domainObject.identifier); + let identifier = makeKeyString(domainObject.identifier); let missingName = 'Missing: ' + identifier; return domainObject.name === missingName; @@ -442,21 +453,21 @@ export default class ObjectAPI { if (error instanceof this.errors.Conflict) { // Synchronized objects will resolve their own conflicts if (this.SYNCHRONIZED_OBJECT_TYPES.includes(domainObject.type)) { - this.openmct.notifications.info( - `Conflict detected while saving "${this.makeKeyString( + this.#openmct.notifications.info( + `Conflict detected while saving "${this.#makeKeyString( domainObject.name )}", attempting to resolve` ); } else { - this.openmct.notifications.error( - `Conflict detected while saving ${this.makeKeyString(domainObject.identifier)}` + this.#openmct.notifications.error( + `Conflict detected while saving ${this.#makeKeyString(domainObject.identifier)}` ); if (this.isTransactionActive()) { this.endTransaction(); } - await this.refresh(domainObject); + await this.#refresh(domainObject); } } @@ -465,7 +476,7 @@ export default class ObjectAPI { } async #getCurrentUsername() { - const user = await this.openmct.user.getCurrentUser(); + const user = await this.#openmct.user.getCurrentUser(); let username; if (user !== undefined) { @@ -554,7 +565,7 @@ export default class ObjectAPI { */ getRelativePath(objectPath) { return objectPath - .map((p) => this.makeKeyString(p.identifier)) + .map((p) => this.#makeKeyString(p.identifier)) .reverse() .join('/'); } @@ -574,13 +585,13 @@ export default class ObjectAPI { } let sourceTelemetry = null; - if (telemetryIdentifier && utils.identifierEquals(identifier, telemetryIdentifier)) { + if (telemetryIdentifier && this.#identifierEquals(identifier, telemetryIdentifier)) { sourceTelemetry = identifier; } else if (objectDetails.composition) { sourceTelemetry = objectDetails.composition[0]; if (telemetryIdentifier) { sourceTelemetry = objectDetails.composition.find((telemetrySource) => - utils.identifierEquals(telemetrySource, telemetryIdentifier) + this.#identifierEquals(telemetrySource, telemetryIdentifier) ); } } @@ -666,7 +677,7 @@ export default class ObjectAPI { mutableObject = MutableDomainObject.createMutable(domainObject, this.eventEmitter); // Check if provider supports realtime updates - let identifier = utils.parseKeyString(mutableObject.identifier); + let identifier = parseKeyString(mutableObject.identifier); let provider = this.getProvider(identifier); if ( @@ -706,7 +717,7 @@ export default class ObjectAPI { if (domainObject.isMutable) { domainObject.$refresh(refreshedObject); } else { - utils.refresh(domainObject, refreshedObject); + refresh(domainObject, refreshedObject); } return domainObject; @@ -745,7 +756,7 @@ export default class ObjectAPI { * @returns {string} A string representation of the given identifier, including namespace and key */ makeKeyString(identifier) { - return utils.makeKeyString(identifier); + return makeKeyString(identifier); } /** @@ -753,7 +764,7 @@ export default class ObjectAPI { * @returns {module:openmct.ObjectAPI~Identifier} An identifier object */ parseKeyString(keyString) { - return utils.parseKeyString(keyString); + return parseKeyString(keyString); } /** @@ -761,9 +772,9 @@ export default class ObjectAPI { * @param {module:openmct.ObjectAPI~Identifier[]} identifiers */ areIdsEqual(...identifiers) { - const firstIdentifier = utils.parseKeyString(identifiers[0]); + const firstIdentifier = this.#parseKeyString(identifiers[0]); - return identifiers.map(utils.parseKeyString).every((identifier) => { + return identifiers.map(this.#parseKeyString).every((identifier) => { return ( identifier === firstIdentifier || (identifier.namespace === firstIdentifier.namespace && @@ -791,7 +802,7 @@ export default class ObjectAPI { } return path.some((pathElement) => { - const identifierToCheck = utils.parseKeyString(keyStringToCheck); + const identifierToCheck = this.#parseKeyString(keyStringToCheck); return this.areIdsEqual(identifierToCheck, pathElement.identifier); }); @@ -814,7 +825,7 @@ export default class ObjectAPI { if (location && !this.#pathContainsDomainObject(location, path)) { // if we have a location, and we don't already have this in our constructed path, // then keep walking up the path - return this.getOriginalPath(utils.parseKeyString(location), path, abortSignal); + return this.getOriginalPath(this.#parseKeyString(location), path, abortSignal); } else { return path; } @@ -853,8 +864,8 @@ export default class ObjectAPI { await Promise.all( keyStrings.map((keyString) => this.supportsMutation(keyString) - ? this.getMutable(utils.parseKeyString(keyString)) - : this.get(utils.parseKeyString(keyString)) + ? this.getMutable(this.#parseKeyString(keyString)) + : this.get(this.#parseKeyString(keyString)) ) ) ).reverse(); @@ -866,7 +877,7 @@ export default class ObjectAPI { return ( objectPath !== undefined && objectPath.length > 1 && - domainObject.location !== this.makeKeyString(objectPath[1].identifier) + domainObject.location !== this.#makeKeyString(objectPath[1].identifier) ); } diff --git a/src/api/objects/RootRegistry.js b/src/api/objects/RootRegistry.js index 51c3f211bc..1f1b652bc4 100644 --- a/src/api/objects/RootRegistry.js +++ b/src/api/objects/RootRegistry.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -import utils from './object-utils.js'; +import { isIdentifier } from './object-utils.js'; export default class RootRegistry { constructor(openmct) { @@ -47,12 +47,12 @@ export default class RootRegistry { } _isValid(rootItem) { - if (utils.isIdentifier(rootItem) || typeof rootItem === 'function') { + if (isIdentifier(rootItem) || typeof rootItem === 'function') { return true; } if (Array.isArray(rootItem)) { - return rootItem.every(utils.isIdentifier); + return rootItem.every(isIdentifier); } return false; diff --git a/src/api/objects/TransactionSpec.js b/src/api/objects/TransactionSpec.js index 67ba64fc7e..254641b9b3 100644 --- a/src/api/objects/TransactionSpec.js +++ b/src/api/objects/TransactionSpec.js @@ -1,4 +1,4 @@ -import utils from 'objectUtils'; +import { makeKeyString, parseKeyString } from 'objectUtils'; import Transaction from './Transaction.js'; @@ -9,7 +9,7 @@ let transaction; describe('Transaction Class', () => { beforeEach(() => { objectAPI = { - makeKeyString: (identifier) => utils.makeKeyString(identifier), + makeKeyString: (identifier) => makeKeyString(identifier), save: () => Promise.resolve(true), mutate: (object, prop, value) => { object[prop] = value; @@ -18,7 +18,7 @@ describe('Transaction Class', () => { }, refresh: (object) => Promise.resolve(object), areIdsEqual: (...identifiers) => { - return identifiers.map(utils.parseKeyString).every((identifier) => { + return identifiers.map(parseKeyString).every((identifier) => { return ( identifier === identifiers[0] || (identifier.namespace === identifiers[0].namespace && diff --git a/src/api/objects/object-utils.js b/src/api/objects/object-utils.js index c6435d7996..86ba2e8845 100644 --- a/src/api/objects/object-utils.js +++ b/src/api/objects/object-utils.js @@ -24,7 +24,7 @@ * Utility for checking if a thing is an Open MCT Identifier. * @private */ -function isIdentifier(thing) { +export function isIdentifier(thing) { return ( typeof thing === 'object' && Object.prototype.hasOwnProperty.call(thing, 'key') && @@ -36,7 +36,7 @@ function isIdentifier(thing) { * Utility for checking if a thing is a key string. Not perfect. * @private */ -function isKeyString(thing) { +export function isKeyString(thing) { return typeof thing === 'string'; } @@ -49,7 +49,7 @@ function isKeyString(thing) { * @param keyString * @returns identifier */ -function parseKeyString(keyString) { +export function parseKeyString(keyString) { if (isIdentifier(keyString)) { return keyString; } @@ -86,7 +86,7 @@ function parseKeyString(keyString) { * @param identifier * @returns keyString */ -function makeKeyString(identifier) { +export function makeKeyString(identifier) { if (!identifier) { throw new Error('Cannot make key string from null identifier'); } @@ -112,7 +112,7 @@ function makeKeyString(identifier) { * @param domainObject * @returns oldFormatModel */ -function toOldFormat(model) { +export function toOldFormat(model) { model = JSON.parse(JSON.stringify(model)); delete model.identifier; if (model.composition) { @@ -131,7 +131,7 @@ function toOldFormat(model) { * @param keyString * @returns domainObject */ -function toNewFormat(model, keyString) { +export function toNewFormat(model, keyString) { model = JSON.parse(JSON.stringify(model)); model.identifier = parseKeyString(keyString); if (model.composition) { @@ -148,7 +148,7 @@ function toNewFormat(model, keyString) { * @param otherIdentifier * @returns Boolean true if identifiers are equal. */ -function identifierEquals(a, b) { +export function identifierEquals(a, b) { return a.key === b.key && a.namespace === b.namespace; } @@ -160,23 +160,12 @@ function identifierEquals(a, b) { * @param otherDomainOBject * @returns Boolean true if objects are equal. */ -function objectEquals(a, b) { +export function objectEquals(a, b) { return identifierEquals(a.identifier, b.identifier); } -function refresh(oldObject, newObject) { +export function refresh(oldObject, newObject) { let deleted = _.difference(Object.keys(oldObject), Object.keys(newObject)); deleted.forEach((propertyName) => delete oldObject[propertyName]); Object.assign(oldObject, newObject); } - -export default { - isIdentifier: isIdentifier, - toOldFormat: toOldFormat, - toNewFormat: toNewFormat, - makeKeyString: makeKeyString, - parseKeyString: parseKeyString, - equals: objectEquals, - identifierEquals: identifierEquals, - refresh: refresh -}; diff --git a/src/api/objects/test/object-utilsSpec.js b/src/api/objects/test/object-utilsSpec.js index 8c13e995bd..d17c03f5d8 100644 --- a/src/api/objects/test/object-utilsSpec.js +++ b/src/api/objects/test/object-utilsSpec.js @@ -1,4 +1,4 @@ -import objectUtils from 'objectUtils'; +import { makeKeyString, parseKeyString, toNewFormat, toOldFormat } from 'objectUtils'; describe('objectUtils', function () { describe('keyString util', function () { @@ -31,27 +31,27 @@ describe('objectUtils', function () { Object.keys(EXPECTATIONS).forEach(function (keyString) { it('parses "' + keyString + '".', function () { - expect(objectUtils.parseKeyString(keyString)).toEqual(EXPECTATIONS[keyString]); + expect(parseKeyString(keyString)).toEqual(EXPECTATIONS[keyString]); }); it('parses and re-encodes "' + keyString + '"', function () { - const identifier = objectUtils.parseKeyString(keyString); - expect(objectUtils.makeKeyString(identifier)).toEqual(keyString); + const identifier = parseKeyString(keyString); + expect(makeKeyString(identifier)).toEqual(keyString); }); it('is idempotent for "' + keyString + '".', function () { - const identifier = objectUtils.parseKeyString(keyString); - let again = objectUtils.parseKeyString(identifier); + const identifier = parseKeyString(keyString); + let again = parseKeyString(identifier); expect(identifier).toEqual(again); - again = objectUtils.parseKeyString(again); - again = objectUtils.parseKeyString(again); + again = parseKeyString(again); + again = parseKeyString(again); expect(identifier).toEqual(again); - let againKeyString = objectUtils.makeKeyString(again); + let againKeyString = makeKeyString(again); expect(againKeyString).toEqual(keyString); - againKeyString = objectUtils.makeKeyString(againKeyString); - againKeyString = objectUtils.makeKeyString(againKeyString); - againKeyString = objectUtils.makeKeyString(againKeyString); + againKeyString = makeKeyString(againKeyString); + againKeyString = makeKeyString(againKeyString); + againKeyString = makeKeyString(againKeyString); expect(againKeyString).toEqual(keyString); }); }); @@ -60,7 +60,7 @@ describe('objectUtils', function () { describe('old object conversions', function () { it('translate ids', function () { expect( - objectUtils.toNewFormat( + toNewFormat( { prop: 'someValue' }, @@ -77,7 +77,7 @@ describe('objectUtils', function () { it('translates composition', function () { expect( - objectUtils.toNewFormat( + toNewFormat( { prop: 'someValue', composition: ['anotherObjectId', 'scratch:anotherObjectId'] @@ -107,7 +107,7 @@ describe('objectUtils', function () { describe('new object conversions', function () { it('removes ids', function () { expect( - objectUtils.toOldFormat({ + toOldFormat({ prop: 'someValue', identifier: { namespace: '', @@ -121,7 +121,7 @@ describe('objectUtils', function () { it('translates composition', function () { expect( - objectUtils.toOldFormat({ + toOldFormat({ prop: 'someValue', composition: [ { diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js index e6aa50bda6..143f3fc32e 100644 --- a/src/api/telemetry/TelemetryAPI.js +++ b/src/api/telemetry/TelemetryAPI.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -import objectUtils from 'objectUtils'; +import { makeKeyString } from 'objectUtils'; import CustomStringFormatter from '../../plugins/displayLayout/CustomStringFormatter.js'; import BatchingWebSocket from './BatchingWebSocket.js'; @@ -429,7 +429,7 @@ export default class TelemetryAPI { this.#subscribeCache = {}; } - const keyString = objectUtils.makeKeyString(domainObject.identifier); + const keyString = makeKeyString(domainObject.identifier); const supportedStrategy = supportsBatching ? requestedStrategy : SUBSCRIBE_STRATEGY.LATEST; // Override the requested strategy with the strategy supported by the provider const optionsWithSupportedStrategy = { @@ -541,7 +541,7 @@ export default class TelemetryAPI { this.stalenessSubscriberCache = {}; } - const keyString = objectUtils.makeKeyString(domainObject.identifier); + const keyString = makeKeyString(domainObject.identifier); let stalenessSubscriber = this.stalenessSubscriberCache[keyString]; if (!stalenessSubscriber) { @@ -600,7 +600,7 @@ export default class TelemetryAPI { this.limitsSubscribeCache = {}; } - const keyString = objectUtils.makeKeyString(domainObject.identifier); + const keyString = makeKeyString(domainObject.identifier); let subscriber = this.limitsSubscribeCache[keyString]; if (!subscriber) { diff --git a/src/plugins/importFromJSONAction/ImportFromJSONAction.js b/src/plugins/importFromJSONAction/ImportFromJSONAction.js index 465ce1a9d1..9003fbe5eb 100644 --- a/src/plugins/importFromJSONAction/ImportFromJSONAction.js +++ b/src/plugins/importFromJSONAction/ImportFromJSONAction.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -import objectUtils from 'objectUtils'; +import { parseKeyString } from 'objectUtils'; import { filter__proto__ } from 'utils/sanitization'; import { v4 as uuid } from 'uuid'; @@ -158,7 +158,7 @@ export default class ImportAsJSONAction { key: uuid() }; - const oldId = objectUtils.parseKeyString(domainObjectId); + const oldId = parseKeyString(domainObjectId); tree = this._rewriteId(oldId, newId, tree); }, this); diff --git a/src/plugins/inspectorViews/elements/ElementItem.vue b/src/plugins/inspectorViews/elements/ElementItem.vue index f286686990..6c7e7fe44a 100644 --- a/src/plugins/inspectorViews/elements/ElementItem.vue +++ b/src/plugins/inspectorViews/elements/ElementItem.vue @@ -23,6 +23,8 @@ @@ -169,7 +169,8 @@ export default { setYAxisIds() { const configId = this.openmct.objects.makeKeyString(this.parentObject.identifier); this.config = configStore.get(configId); - this.yAxes = []; + // Clear the yAxes array and repopulate it with the current YAxis elements + this.yAxes.splice(0); this.yAxes.push({ id: this.config.yAxis.id, elements: this.parentObject.configuration.series.filter( @@ -207,7 +208,7 @@ export default { } // Store the element in the cache and set its yAxisId - this.elementsCache[keyString] = JSON.parse(JSON.stringify(element)); + this.elementsCache[keyString] = element; if (this.elementsCache[keyString].yAxisId !== yAxisId) { // Mutate the YAxisId on the domainObject itself this.updateCacheAndMutate(element, yAxisId); @@ -276,7 +277,7 @@ export default { yAxisId }); this.composition.add(domainObject); - this.elementsCache[keyString] = JSON.parse(JSON.stringify(domainObject)); + this.elementsCache[keyString] = domainObject; } this.elementsCache[keyString].yAxisId = yAxisId; diff --git a/src/plugins/notebook/utils/notebook-storage.js b/src/plugins/notebook/utils/notebook-storage.js index aaca877611..866cd0faee 100644 --- a/src/plugins/notebook/utils/notebook-storage.js +++ b/src/plugins/notebook/utils/notebook-storage.js @@ -1,4 +1,4 @@ -import objectUtils from 'objectUtils'; +import { makeKeyString } from 'objectUtils'; const NOTEBOOK_LOCAL_STORAGE = 'notebook-storage'; let currentNotebookObjectIdentifier = null; @@ -22,8 +22,7 @@ function defaultNotebookObjectChanged(newDomainObject) { function observeDefaultNotebookObject(openmct, notebookStorage, domainObject) { if ( currentNotebookObjectIdentifier && - objectUtils.makeKeyString(currentNotebookObjectIdentifier) === - objectUtils.makeKeyString(notebookStorage.identifier) + makeKeyString(currentNotebookObjectIdentifier) === makeKeyString(notebookStorage.identifier) ) { return; } diff --git a/src/plugins/staticRootPlugin/StaticModelProvider.js b/src/plugins/staticRootPlugin/StaticModelProvider.js index d955159b92..d8006fc75e 100644 --- a/src/plugins/staticRootPlugin/StaticModelProvider.js +++ b/src/plugins/staticRootPlugin/StaticModelProvider.js @@ -26,7 +26,7 @@ * rootIdentifier, and rewrites all child object identifiers so that they * exist in the same namespace as the rootIdentifier. */ -import objectUtils from 'objectUtils'; +import { makeKeyString, parseKeyString, toNewFormat } from 'objectUtils'; class StaticModelProvider { constructor(importData, rootIdentifier) { @@ -38,7 +38,7 @@ class StaticModelProvider { * Standard "Get". */ get(identifier) { - const keyString = objectUtils.makeKeyString(identifier); + const keyString = makeKeyString(identifier); if (this.objectMap[keyString]) { return this.objectMap[keyString]; } @@ -49,7 +49,7 @@ class StaticModelProvider { parseObjectLeaf(objectLeaf, idMap, newRootNamespace, oldRootNamespace) { Object.keys(objectLeaf).forEach((nodeKey) => { if (idMap.get(nodeKey)) { - const newIdentifier = objectUtils.makeKeyString({ + const newIdentifier = makeKeyString({ namespace: newRootNamespace, key: idMap.get(nodeKey) }); @@ -104,7 +104,7 @@ class StaticModelProvider { let mappedLeafValue; if (oldRootNamespace) { mappedLeafValue = idMap.get( - objectUtils.makeKeyString({ + makeKeyString({ namespace: oldRootNamespace, key: leafValue }) @@ -125,7 +125,7 @@ class StaticModelProvider { return null; } - const newLocationIdentifier = objectUtils.makeKeyString({ + const newLocationIdentifier = makeKeyString({ namespace: newRootNamespace, key: mappedLeafValue }); @@ -134,7 +134,7 @@ class StaticModelProvider { } else { const mappedLeafValue = idMap.get(leafValue); if (mappedLeafValue) { - const newIdentifier = objectUtils.makeKeyString({ + const newIdentifier = makeKeyString({ namespace: newRootNamespace, key: mappedLeafValue }); @@ -147,7 +147,7 @@ class StaticModelProvider { } rewriteObjectIdentifiers(importData, rootIdentifier) { - const { namespace: oldRootNamespace } = objectUtils.parseKeyString(importData.rootId); + const { namespace: oldRootNamespace } = parseKeyString(importData.rootId); const { namespace: newRootNamespace } = rootIdentifier; const idMap = new Map(); const objectTree = importData.openmct; @@ -172,7 +172,7 @@ class StaticModelProvider { */ convertToNewObjects(oldObjectMap) { return Object.keys(oldObjectMap).reduce(function (newObjectMap, key) { - newObjectMap[key] = objectUtils.toNewFormat(oldObjectMap[key], key); + newObjectMap[key] = toNewFormat(oldObjectMap[key], key); return newObjectMap; }, {}); @@ -180,7 +180,7 @@ class StaticModelProvider { /* Set the root location correctly for a top-level object */ setRootLocation(objectMap, rootIdentifier) { - objectMap[objectUtils.makeKeyString(rootIdentifier)].location = 'ROOT'; + objectMap[makeKeyString(rootIdentifier)].location = 'ROOT'; return objectMap; } diff --git a/src/plugins/summaryWidget/src/ConditionManager.js b/src/plugins/summaryWidget/src/ConditionManager.js index 917f386e8a..dc453dafdd 100644 --- a/src/plugins/summaryWidget/src/ConditionManager.js +++ b/src/plugins/summaryWidget/src/ConditionManager.js @@ -1,6 +1,6 @@ import EventEmitter from 'EventEmitter'; import _ from 'lodash'; -import objectUtils from 'objectUtils'; +import { makeKeyString } from 'objectUtils'; import ConditionEvaluator from './ConditionEvaluator.js'; @@ -119,7 +119,7 @@ ConditionManager.prototype.addGlobalPropertyType = function (key, type) { * has completed and types have been parsed */ ConditionManager.prototype.parsePropertyTypes = function (object) { - const objectId = objectUtils.makeKeyString(object.identifier); + const objectId = makeKeyString(object.identifier); this.telemetryTypesById[objectId] = {}; Object.values(this.telemetryMetadataById[objectId]).forEach(function (valueMetadata) { @@ -182,7 +182,7 @@ ConditionManager.prototype.createNormalizedDatum = function (objId, telemetryDat ConditionManager.prototype.onCompositionAdd = function (obj) { let compositionKeys; const telemetryAPI = this.openmct.telemetry; - const objId = objectUtils.makeKeyString(obj.identifier); + const objId = makeKeyString(obj.identifier); let telemetryMetadata; const self = this; @@ -191,7 +191,7 @@ ConditionManager.prototype.onCompositionAdd = function (obj) { self.telemetryMetadataById[objId] = {}; // FIXME: this should just update based on listener. - compositionKeys = self.domainObject.composition.map(objectUtils.makeKeyString); + compositionKeys = self.domainObject.composition.map(makeKeyString); if (!compositionKeys.includes(objId)) { self.domainObject.composition.push(obj.identifier); } @@ -245,7 +245,7 @@ ConditionManager.prototype.onCompositionAdd = function (obj) { * @private */ ConditionManager.prototype.onCompositionRemove = function (identifier) { - const objectId = objectUtils.makeKeyString(identifier); + const objectId = makeKeyString(identifier); // FIXME: this should just update by listener. _.remove(this.domainObject.composition, function (id) { return id.key === identifier.key && id.namespace === identifier.namespace; diff --git a/src/plugins/summaryWidget/src/input/ObjectSelect.js b/src/plugins/summaryWidget/src/input/ObjectSelect.js index 5547854c4e..40f0a506d8 100644 --- a/src/plugins/summaryWidget/src/input/ObjectSelect.js +++ b/src/plugins/summaryWidget/src/input/ObjectSelect.js @@ -1,4 +1,4 @@ -import objectUtils from 'objectUtils'; +import { makeKeyString } from 'objectUtils'; import Select from './Select.js'; @@ -39,7 +39,7 @@ export default function ObjectSelect(config, manager, baseOptions) { * @private */ function onCompositionAdd(obj) { - self.select.addOption(objectUtils.makeKeyString(obj.identifier), obj.name); + self.select.addOption(makeKeyString(obj.identifier), obj.name); } /** @@ -77,7 +77,7 @@ export default function ObjectSelect(config, manager, baseOptions) { */ ObjectSelect.prototype.generateOptions = function () { const items = Object.values(this.compositionObjs).map(function (obj) { - return [objectUtils.makeKeyString(obj.identifier), obj.name]; + return [makeKeyString(obj.identifier), obj.name]; }); this.baseOptions.forEach(function (option, index) { items.splice(index, 0, option); diff --git a/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js b/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js index 75ddff560d..88d3acded3 100644 --- a/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js +++ b/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -import objectUtils from 'objectUtils'; +import { makeKeyString } from 'objectUtils'; import SummaryWidgetEvaluator from './SummaryWidgetEvaluator.js'; @@ -31,7 +31,7 @@ export default function EvaluatorPool(openmct) { } EvaluatorPool.prototype.get = function (domainObject) { - const objectId = objectUtils.makeKeyString(domainObject.identifier); + const objectId = makeKeyString(domainObject.identifier); let poolEntry = this.byObjectId[objectId]; if (!poolEntry) { poolEntry = { diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js index b05f51cd21..0e61202f67 100644 --- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js +++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js @@ -21,7 +21,7 @@ *****************************************************************************/ import _ from 'lodash'; -import objectUtils from 'objectUtils'; +import { makeKeyString } from 'objectUtils'; import eventHelpers from '../eventHelpers.js'; import SummaryWidgetRule from './SummaryWidgetRule.js'; @@ -113,7 +113,7 @@ SummaryWidgetEvaluator.prototype.updateRules = function (domainObject) { }; SummaryWidgetEvaluator.prototype.addChild = function (childObject) { - const childId = objectUtils.makeKeyString(childObject.identifier); + const childId = makeKeyString(childObject.identifier); const metadata = this.openmct.telemetry.getMetadata(childObject); const formats = this.openmct.telemetry.getFormatMap(metadata); @@ -126,7 +126,7 @@ SummaryWidgetEvaluator.prototype.addChild = function (childObject) { }; SummaryWidgetEvaluator.prototype.removeChild = function (childObject) { - const childId = objectUtils.makeKeyString(childObject.identifier); + const childId = makeKeyString(childObject.identifier); delete this.baseState[childId]; }; diff --git a/src/plugins/telemetryMean/src/MeanTelemetryProvider.js b/src/plugins/telemetryMean/src/MeanTelemetryProvider.js index 35557fbeb2..03ed1c2fe4 100644 --- a/src/plugins/telemetryMean/src/MeanTelemetryProvider.js +++ b/src/plugins/telemetryMean/src/MeanTelemetryProvider.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -import objectUtils from 'objectUtils'; +import { parseKeyString } from 'objectUtils'; import TelemetryAverager from './TelemetryAverager.js'; @@ -43,7 +43,7 @@ MeanTelemetryProvider.prototype.supportsRequest = MeanTelemetryProvider.prototype.subscribe = function (domainObject, callback) { let wrappedUnsubscribe; let unsubscribeCalled = false; - const objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); + const objectId = parseKeyString(domainObject.telemetryPoint); const samples = domainObject.samples; this.objectAPI @@ -79,7 +79,7 @@ MeanTelemetryProvider.prototype.subscribeToAverage = function (domainObject, sam }; MeanTelemetryProvider.prototype.request = function (domainObject, request) { - const objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); + const objectId = parseKeyString(domainObject.telemetryPoint); const samples = domainObject.samples; return this.objectAPI.get(objectId).then( @@ -119,7 +119,7 @@ MeanTelemetryProvider.prototype.requestAverageTelemetry = function ( * @private */ MeanTelemetryProvider.prototype.getLinkedObject = function (domainObject) { - const objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); + const objectId = parseKeyString(domainObject.telemetryPoint); return this.objectAPI.get(objectId); }; diff --git a/src/ui/components/ObjectLabel.vue b/src/ui/components/ObjectLabel.vue index c9caf835c2..bac8e10cfe 100644 --- a/src/ui/components/ObjectLabel.vue +++ b/src/ui/components/ObjectLabel.vue @@ -25,6 +25,7 @@ class="c-tree__item__label c-object-label" :class="[statusClass]" draggable="true" + :aria-label="ariaLabel" @dragstart="dragStart" @click="navigateOrPreview" > @@ -47,7 +48,10 @@