diff --git a/.circleci/config.yml b/.circleci/config.yml index e9163c13d9..45749b0f26 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 executors: pw-focal-development: docker: - - image: mcr.microsoft.com/playwright:v1.23.0-focal + - image: mcr.microsoft.com/playwright:v1.25.0-focal environment: NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed PERCY_POSTINSTALL_BROWSER: 'true' # Needed to store the percy browser in cache deps diff --git a/.github/workflows/e2e-couchdb.yml b/.github/workflows/e2e-couchdb.yml index 0a7ebe4cc2..69c33967a1 100644 --- a/.github/workflows/e2e-couchdb.yml +++ b/.github/workflows/e2e-couchdb.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: '16' - - run: npx playwright@1.23.0 install + - run: npx playwright@1.25.0 install - run: npm install - run: sh src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh - run: npm run test:e2e:couchdb diff --git a/.github/workflows/e2e-pr.yml b/.github/workflows/e2e-pr.yml index b21bf8ce79..385255797a 100644 --- a/.github/workflows/e2e-pr.yml +++ b/.github/workflows/e2e-pr.yml @@ -30,7 +30,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: '16' - - run: npx playwright@1.23.0 install + - run: npx playwright@1.25.0 install - run: npx playwright install chrome-beta - run: npm install - run: npm run test:e2e:full diff --git a/e2e/appActions.js b/e2e/appActions.js index c7a4428d5d..2cfecd1b19 100644 --- a/e2e/appActions.js +++ b/e2e/appActions.js @@ -45,6 +45,8 @@ * @property {string} url the relative url to the object (for use with `page.goto()`) */ +const Buffer = require('buffer').Buffer; + /** * This common function creates a domain object with the default options. It is the preferred way of creating objects * in the e2e suite when uninterested in properties of the objects themselves. @@ -100,6 +102,59 @@ async function createDomainObjectWithDefaults(page, { type, name, parent = 'mine }; } +/** + * Create a Plan object from JSON with the provided options. + * @param {import('@playwright/test').Page} page + * @param {*} options + * @returns {Promise} An object containing information about the newly created domain object. + */ +async function createPlanFromJSON(page, { name, json, parent = 'mine' }) { + const parentUrl = await getHashUrlToDomainObject(page, parent); + + // Navigate to the parent object. This is necessary to create the object + // in the correct location, such as a folder, layout, or plot. + await page.goto(`${parentUrl}?hideTree=true`); + + //Click the Create button + await page.click('button:has-text("Create")'); + + // Click 'Plan' menu option + await page.click(`li:text("Plan")`); + + // Modify the name input field of the domain object to accept 'name' + if (name) { + const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]'); + await nameInput.fill(""); + await nameInput.fill(name); + } + + // Upload buffer from memory + await page.locator('input#fileElem').setInputFiles({ + name: 'plan.txt', + mimeType: 'text/plain', + buffer: Buffer.from(JSON.stringify(json)) + }); + + // Click OK button and wait for Navigate event + await Promise.all([ + page.waitForLoadState(), + page.click('[aria-label="Save"]'), + // Wait for Save Banner to appear + page.waitForSelector('.c-message-banner__message') + ]); + + // Wait until the URL is updated + await page.waitForURL(`**/mine/*`); + const uuid = await getFocusedObjectUuid(page); + const objectUrl = await getHashUrlToDomainObject(page, uuid); + + return { + uuid, + name, + url: objectUrl + }; +} + /** * Open the given `domainObject`'s context menu from the object tree. * Expands the path to the object and scrolls to it if necessary. @@ -258,6 +313,7 @@ async function setEndOffset(page, offset) { // eslint-disable-next-line no-undef module.exports = { createDomainObjectWithDefaults, + createPlanFromJSON, openObjectTreeContextMenu, getHashUrlToDomainObject, getFocusedObjectUuid, diff --git a/e2e/tests/functional/menu.e2e.spec.js b/e2e/tests/functional/menu.e2e.spec.js index 9dca46cba7..9a1e600ddc 100644 --- a/e2e/tests/functional/menu.e2e.spec.js +++ b/e2e/tests/functional/menu.e2e.spec.js @@ -42,7 +42,7 @@ test.describe('Persistence operations @addInit', () => { button: 'right' }); - const menuOptions = page.locator('.c-menu ul'); + const menuOptions = page.locator('.c-menu li'); await expect.soft(menuOptions).toContainText(['Open In New Tab', 'View', 'Create Link']); await expect(menuOptions).not.toContainText(['Move', 'Duplicate', 'Remove', 'Add New Folder', 'Edit Properties...', 'Export as JSON', 'Import from JSON']); diff --git a/e2e/tests/functional/planning/plan.e2e.spec.js b/e2e/tests/functional/planning/plan.e2e.spec.js new file mode 100644 index 0000000000..c8b7ece8d6 --- /dev/null +++ b/e2e/tests/functional/planning/plan.e2e.spec.js @@ -0,0 +1,87 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ +const { test, expect } = require('../../../pluginFixtures'); +const { createPlanFromJSON } = require('../../../appActions'); + +const testPlan = { + "TEST_GROUP": [ + { + "name": "Past event 1", + "start": 1660320408000, + "end": 1660343797000, + "type": "TEST-GROUP", + "color": "orange", + "textColor": "white" + }, + { + "name": "Past event 2", + "start": 1660406808000, + "end": 1660429160000, + "type": "TEST-GROUP", + "color": "orange", + "textColor": "white" + }, + { + "name": "Past event 3", + "start": 1660493208000, + "end": 1660503981000, + "type": "TEST-GROUP", + "color": "orange", + "textColor": "white" + }, + { + "name": "Past event 4", + "start": 1660579608000, + "end": 1660624108000, + "type": "TEST-GROUP", + "color": "orange", + "textColor": "white" + }, + { + "name": "Past event 5", + "start": 1660666008000, + "end": 1660681529000, + "type": "TEST-GROUP", + "color": "orange", + "textColor": "white" + } + ] +}; + +test.describe("Plan", () => { + test("Create a Plan and display all plan events @unstable", async ({ page }) => { + await page.goto('./', { waitUntil: 'networkidle' }); + + const plan = await createPlanFromJSON(page, { + name: 'Test Plan', + json: testPlan + }); + const startBound = testPlan.TEST_GROUP[0].start; + const endBound = testPlan.TEST_GROUP[testPlan.TEST_GROUP.length - 1].end; + + // Switch to fixed time mode with all plan events within the bounds + await page.goto(`${plan.url}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=plan.view`); + const eventCount = await page.locator('.activity-bounds').count(); + expect(eventCount).toEqual(testPlan.TEST_GROUP.length); + }); +}); + diff --git a/e2e/tests/functional/planning/timestrip.e2e.spec.js b/e2e/tests/functional/planning/timestrip.e2e.spec.js new file mode 100644 index 0000000000..e1f7511445 --- /dev/null +++ b/e2e/tests/functional/planning/timestrip.e2e.spec.js @@ -0,0 +1,181 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +const { test, expect } = require('../../../pluginFixtures'); +const { createDomainObjectWithDefaults, createPlanFromJSON } = require('../../../appActions'); + +const testPlan = { + "TEST_GROUP": [ + { + "name": "Past event 1", + "start": 1660320408000, + "end": 1660343797000, + "type": "TEST-GROUP", + "color": "orange", + "textColor": "white" + }, + { + "name": "Past event 2", + "start": 1660406808000, + "end": 1660429160000, + "type": "TEST-GROUP", + "color": "orange", + "textColor": "white" + }, + { + "name": "Past event 3", + "start": 1660493208000, + "end": 1660503981000, + "type": "TEST-GROUP", + "color": "orange", + "textColor": "white" + }, + { + "name": "Past event 4", + "start": 1660579608000, + "end": 1660624108000, + "type": "TEST-GROUP", + "color": "orange", + "textColor": "white" + }, + { + "name": "Past event 5", + "start": 1660666008000, + "end": 1660681529000, + "type": "TEST-GROUP", + "color": "orange", + "textColor": "white" + } + ] +}; + +test.describe("Time Strip", () => { + test("Create two Time Strips, add a single Plan to both, and verify they can have separate Indepdenent Time Contexts @unstable", async ({ page }) => { + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/nasa/openmct/issues/5627' + }); + + // Constant locators + const independentTimeConductorInputs = page.locator('.l-shell__main-independent-time-conductor .c-input--datetime'); + const activityBounds = page.locator('.activity-bounds'); + + // Goto baseURL + await page.goto('./', { waitUntil: 'networkidle' }); + + const timestrip = await test.step("Create a Time Strip", async () => { + const createdTimeStrip = await createDomainObjectWithDefaults(page, { type: 'Time Strip' }); + const objectName = await page.locator('.l-browse-bar__object-name').innerText(); + expect(objectName).toBe(createdTimeStrip.name); + + return createdTimeStrip; + }); + + const plan = await test.step("Create a Plan and add it to the timestrip", async () => { + const createdPlan = await createPlanFromJSON(page, { + name: 'Test Plan', + json: testPlan + }); + + await page.goto(timestrip.url); + // Expand the tree to show the plan + await page.click("button[title='Show selected item in tree']"); + await page.dragAndDrop(`role=treeitem[name=/${createdPlan.name}/]`, '.c-object-view'); + await page.click("button[title='Save']"); + await page.click("li[title='Save and Finish Editing']"); + const startBound = testPlan.TEST_GROUP[0].start; + const endBound = testPlan.TEST_GROUP[testPlan.TEST_GROUP.length - 1].end; + + // Switch to fixed time mode with all plan events within the bounds + await page.goto(`${timestrip.url}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=time-strip.view`); + + // Verify all events are displayed + const eventCount = await page.locator('.activity-bounds').count(); + expect(eventCount).toEqual(testPlan.TEST_GROUP.length); + + return createdPlan; + }); + + await test.step("TimeStrip can use the Independent Time Conductor", async () => { + // Activate Independent Time Conductor in Fixed Time Mode + await page.click('.c-toggle-switch__slider'); + expect(await activityBounds.count()).toEqual(0); + + // Set the independent time bounds so that only one event is shown + const startBound = testPlan.TEST_GROUP[0].start; + const endBound = testPlan.TEST_GROUP[0].end; + const startBoundString = new Date(startBound).toISOString().replace('T', ' '); + const endBoundString = new Date(endBound).toISOString().replace('T', ' '); + + await independentTimeConductorInputs.nth(0).fill(''); + await independentTimeConductorInputs.nth(0).fill(startBoundString); + await page.keyboard.press('Enter'); + await independentTimeConductorInputs.nth(1).fill(''); + await independentTimeConductorInputs.nth(1).fill(endBoundString); + await page.keyboard.press('Enter'); + expect(await activityBounds.count()).toEqual(1); + }); + + await test.step("Can have multiple TimeStrips with the same plan linked and different Independent Time Contexts", async () => { + // Create another Time Strip and verify that it has been created + const createdTimeStrip = await createDomainObjectWithDefaults(page, { + type: 'Time Strip', + name: "Another Time Strip" + }); + + const objectName = await page.locator('.l-browse-bar__object-name').innerText(); + expect(objectName).toBe(createdTimeStrip.name); + + // Drag the existing Plan onto the newly created Time Strip, and save. + await page.dragAndDrop(`role=treeitem[name=/${plan.name}/]`, '.c-object-view'); + await page.click("button[title='Save']"); + await page.click("li[title='Save and Finish Editing']"); + + // Activate Independent Time Conductor in Fixed Time Mode + await page.click('.c-toggle-switch__slider'); + + // All events should be displayed at this point because the + // initial independent context bounds will match the global bounds + expect(await activityBounds.count()).toEqual(5); + + // Set the independent time bounds so that two events are shown + const startBound = testPlan.TEST_GROUP[0].start; + const endBound = testPlan.TEST_GROUP[1].end; + const startBoundString = new Date(startBound).toISOString().replace('T', ' '); + const endBoundString = new Date(endBound).toISOString().replace('T', ' '); + + await independentTimeConductorInputs.nth(0).fill(''); + await independentTimeConductorInputs.nth(0).fill(startBoundString); + await page.keyboard.press('Enter'); + await independentTimeConductorInputs.nth(1).fill(''); + await independentTimeConductorInputs.nth(1).fill(endBoundString); + await page.keyboard.press('Enter'); + + // Verify that two events are displayed + expect(await activityBounds.count()).toEqual(2); + + // Switch to the previous Time Strip and verify that only one event is displayed + await page.goto(timestrip.url); + expect(await activityBounds.count()).toEqual(1); + }); + }); +}); diff --git a/e2e/tests/functional/plugins/faultManagement/faultManagement.e2e.spec.js b/e2e/tests/functional/plugins/faultManagement/faultManagement.e2e.spec.js index 8bf08e0c9b..ff35dc79ba 100644 --- a/e2e/tests/functional/plugins/faultManagement/faultManagement.e2e.spec.js +++ b/e2e/tests/functional/plugins/faultManagement/faultManagement.e2e.spec.js @@ -28,14 +28,14 @@ test.describe('The Fault Management Plugin using example faults', () => { await utils.navigateToFaultManagementWithExample(page); }); - test('Shows a criticality icon for every fault', async ({ page }) => { + test('Shows a criticality icon for every fault @unstable', async ({ page }) => { const faultCount = await page.locator('c-fault-mgmt__list').count(); const criticalityIconCount = await page.locator('c-fault-mgmt__list-severity').count(); expect.soft(faultCount).toEqual(criticalityIconCount); }); - test('When selecting a fault, it has an "is-selected" class and it\'s information shows in the inspector', async ({ page }) => { + test('When selecting a fault, it has an "is-selected" class and it\'s information shows in the inspector @unstable', async ({ page }) => { await utils.selectFaultItem(page, 1); const selectedFaultName = await page.locator('.c-fault-mgmt__list.is-selected .c-fault-mgmt__list-faultname').textContent(); @@ -45,7 +45,7 @@ test.describe('The Fault Management Plugin using example faults', () => { expect.soft(inspectorFaultNameCount).toEqual(1); }); - test('When selecting multiple faults, no specific fault information is shown in the inspector', async ({ page }) => { + test('When selecting multiple faults, no specific fault information is shown in the inspector @unstable', async ({ page }) => { await utils.selectFaultItem(page, 1); await utils.selectFaultItem(page, 2); @@ -61,7 +61,7 @@ test.describe('The Fault Management Plugin using example faults', () => { expect.soft(secondNameInInspectorCount).toEqual(0); }); - test('Allows you to shelve a fault', async ({ page }) => { + test('Allows you to shelve a fault @unstable', async ({ page }) => { const shelvedFaultName = await utils.getFaultName(page, 2); const beforeShelvedFault = utils.getFaultByName(page, shelvedFaultName); @@ -80,7 +80,7 @@ test.describe('The Fault Management Plugin using example faults', () => { expect.soft(await shelvedViewFault.count()).toBe(1); }); - test('Allows you to acknowledge a fault', async ({ page }) => { + test('Allows you to acknowledge a fault @unstable', async ({ page }) => { const acknowledgedFaultName = await utils.getFaultName(page, 3); await utils.acknowledgeFault(page, 3); @@ -94,7 +94,7 @@ test.describe('The Fault Management Plugin using example faults', () => { expect.soft(acknowledgedFaultName).toEqual(acknowledgedViewFaultName); }); - test('Allows you to shelve multiple faults', async ({ page }) => { + test('Allows you to shelve multiple faults @unstable', async ({ page }) => { const shelvedFaultNameOne = await utils.getFaultName(page, 1); const shelvedFaultNameFour = await utils.getFaultName(page, 4); @@ -121,7 +121,7 @@ test.describe('The Fault Management Plugin using example faults', () => { expect.soft(await shelvedViewFaultFour.count()).toBe(1); }); - test('Allows you to acknowledge multiple faults', async ({ page }) => { + test('Allows you to acknowledge multiple faults @unstable', async ({ page }) => { const acknowledgedFaultNameTwo = await utils.getFaultName(page, 2); const acknowledgedFaultNameFive = await utils.getFaultName(page, 5); @@ -143,7 +143,7 @@ test.describe('The Fault Management Plugin using example faults', () => { expect.soft(await acknowledgedViewFaultFive.count()).toBe(1); }); - test('Allows you to search faults', async ({ page }) => { + test('Allows you to search faults @unstable', async ({ page }) => { const faultThreeNamespace = await utils.getFaultNamespace(page, 3); const faultTwoName = await utils.getFaultName(page, 2); const faultFiveTriggerTime = await utils.getFaultTriggerTime(page, 5); @@ -184,7 +184,7 @@ test.describe('The Fault Management Plugin using example faults', () => { expect.soft(await utils.getFaultTriggerTime(page, 1)).toEqual(faultFiveTriggerTime); }); - test('Allows you to sort faults', async ({ page }) => { + test('Allows you to sort faults @unstable', async ({ page }) => { const highestSeverity = await utils.getHighestSeverity(page); const lowestSeverity = await utils.getLowestSeverity(page); const faultOneName = 'Example Fault 1'; @@ -213,7 +213,7 @@ test.describe('The Fault Management Plugin without using example faults', () => await utils.navigateToFaultManagementWithoutExample(page); }); - test('Shows no faults when no faults are provided', async ({ page }) => { + test('Shows no faults when no faults are provided @unstable', async ({ page }) => { const faultCount = await page.locator('c-fault-mgmt__list').count(); expect.soft(faultCount).toEqual(0); @@ -227,7 +227,7 @@ test.describe('The Fault Management Plugin without using example faults', () => expect.soft(shelvedCount).toEqual(0); }); - test('Will return no faults when searching', async ({ page }) => { + test('Will return no faults when searching @unstable', async ({ page }) => { await utils.enterSearchTerm(page, 'fault'); const faultCount = await page.locator('c-fault-mgmt__list').count(); diff --git a/package.json b/package.json index ac1e3c838d..b901f58d2e 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "@braintree/sanitize-url": "6.0.0", "@percy/cli": "1.7.2", "@percy/playwright": "1.0.4", - "@playwright/test": "1.23.0", + "@playwright/test": "1.25.0", "@types/eventemitter3": "^1.0.0", "@types/jasmine": "^4.0.1", "@types/karma": "^6.3.2", diff --git a/src/api/time/TimeAPI.js b/src/api/time/TimeAPI.js index c7e3ddeff9..d22361d8d5 100644 --- a/src/api/time/TimeAPI.js +++ b/src/api/time/TimeAPI.js @@ -171,27 +171,38 @@ class TimeAPI extends GlobalTimeContext { * @memberof module:openmct.TimeAPI# * @method getContextForView */ - getContextForView(objectPath = []) { - const viewKey = objectPath.length && this.openmct.objects.makeKeyString(objectPath[0].identifier); - - if (viewKey) { - let viewTimeContext = this.getIndependentContext(viewKey); - if (viewTimeContext) { - this.independentContexts.delete(viewKey); - } else { - viewTimeContext = new IndependentTimeContext(this.openmct, this, objectPath); - } - - // return a new IndependentContext in case the objectPath is different - this.independentContexts.set(viewKey, viewTimeContext); - - return viewTimeContext; + getContextForView(objectPath) { + if (!objectPath || !Array.isArray(objectPath)) { + throw new Error('No objectPath provided'); } - // always follow the global time context - return this; - } + const viewKey = objectPath.length && this.openmct.objects.makeKeyString(objectPath[0].identifier); + if (!viewKey) { + // Return the global time context + return this; + } + + let viewTimeContext = this.getIndependentContext(viewKey); + if (!viewTimeContext) { + // If the context doesn't exist yet, create it. + viewTimeContext = new IndependentTimeContext(this.openmct, this, objectPath); + this.independentContexts.set(viewKey, viewTimeContext); + } else { + // If it already exists, compare the objectPath to see if it needs to be updated. + const currentPath = this.openmct.objects.getRelativePath(viewTimeContext.objectPath); + const newPath = this.openmct.objects.getRelativePath(objectPath); + + if (currentPath !== newPath) { + // If the path has changed, update the context. + this.independentContexts.delete(viewKey); + viewTimeContext = new IndependentTimeContext(this.openmct, this, objectPath); + this.independentContexts.set(viewKey, viewTimeContext); + } + } + + return viewTimeContext; + } } export default TimeAPI; diff --git a/src/plugins/remoteClock/requestInterceptor.js b/src/plugins/remoteClock/requestInterceptor.js index d6cffe7b3a..a047129106 100644 --- a/src/plugins/remoteClock/requestInterceptor.js +++ b/src/plugins/remoteClock/requestInterceptor.js @@ -20,17 +20,15 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -function remoteClockRequestInterceptor(openmct, remoteClockIdentifier, waitForBounds) { +function remoteClockRequestInterceptor(openmct, _remoteClockIdentifier, waitForBounds) { let remoteClockLoaded = false; return { appliesTo: () => { // Get the activeClock from the Global Time Context - const { activeClock } = openmct.time.getContextForView(); + const { activeClock } = openmct.time; - return activeClock !== undefined - && activeClock.key === 'remote-clock' - && !remoteClockLoaded; + return activeClock?.key === 'remote-clock' && !remoteClockLoaded; }, invoke: async (request) => { const { start, end } = await waitForBounds(); diff --git a/src/plugins/timeConductor/ConductorInputsFixed.vue b/src/plugins/timeConductor/ConductorInputsFixed.vue index 106e4cee8b..c3c14f72e3 100644 --- a/src/plugins/timeConductor/ConductorInputsFixed.vue +++ b/src/plugins/timeConductor/ConductorInputsFixed.vue @@ -78,6 +78,12 @@ export default { default() { return undefined; } + }, + objectPath: { + type: Array, + default() { + return []; + } } }, data() { @@ -127,7 +133,7 @@ export default { methods: { setTimeContext() { this.stopFollowingTimeContext(); - this.timeContext = this.openmct.time.getContextForView(this.keyString ? [{identifier: this.keyString}] : []); + this.timeContext = this.openmct.time.getContextForView(this.keyString ? this.objectPath : []); this.handleNewBounds(this.timeContext.bounds()); this.timeContext.on('bounds', this.handleNewBounds); diff --git a/src/plugins/timeConductor/ConductorInputsRealtime.vue b/src/plugins/timeConductor/ConductorInputsRealtime.vue index c6aee38ee7..4054869be1 100644 --- a/src/plugins/timeConductor/ConductorInputsRealtime.vue +++ b/src/plugins/timeConductor/ConductorInputsRealtime.vue @@ -90,6 +90,12 @@ export default { return undefined; } }, + objectPath: { + type: Array, + default() { + return []; + } + }, inputBounds: { type: Object, default() { @@ -162,7 +168,7 @@ export default { }, setTimeContext() { this.stopFollowingTime(); - this.timeContext = this.openmct.time.getContextForView(this.keyString ? [{identifier: this.keyString}] : []); + this.timeContext = this.openmct.time.getContextForView(this.keyString ? this.objectPath : []); this.followTime(); }, handleNewBounds(bounds) { diff --git a/src/plugins/timeConductor/independent/IndependentTimeConductor.vue b/src/plugins/timeConductor/independent/IndependentTimeConductor.vue index c64b0c38d5..8e40e90707 100644 --- a/src/plugins/timeConductor/independent/IndependentTimeConductor.vue +++ b/src/plugins/timeConductor/independent/IndependentTimeConductor.vue @@ -52,12 +52,14 @@ @@ -85,6 +87,10 @@ export default { domainObject: { type: Object, required: true + }, + objectPath: { + type: Array, + required: true } }, data() { @@ -164,7 +170,7 @@ export default { }, setTimeContext() { this.stopFollowingTimeContext(); - this.timeContext = this.openmct.time.getContextForView([this.domainObject]); + this.timeContext = this.openmct.time.getContextForView(this.objectPath); this.timeContext.on('clock', this.setTimeOptions); }, stopFollowingTimeContext() { diff --git a/src/ui/components/ObjectView.vue b/src/ui/components/ObjectView.vue index 2976f7dc78..1ab45a9479 100644 --- a/src/ui/components/ObjectView.vue +++ b/src/ui/components/ObjectView.vue @@ -6,6 +6,7 @@ > @@ -67,6 +68,9 @@ export default { }; }, computed: { + path() { + return this.domainObject && (this.currentObjectPath || this.objectPath); + }, objectFontStyle() { return this.domainObject && this.domainObject.configuration && this.domainObject.configuration.fontStyle; },