diff --git a/.cspell.json b/.cspell.json index 9915d5f666..a8f4777ce9 100644 --- a/.cspell.json +++ b/.cspell.json @@ -484,7 +484,8 @@ "darkmatter", "Undeletes", "SSSZ", - "pageerror" + "pageerror", + "annotatable" ], "dictionaries": ["npm", "softwareTerms", "node", "html", "css", "bash", "en_US", "en-gb", "misc"], "ignorePaths": [ diff --git a/API.md b/API.md index 40d8b7a178..e607deeb3c 100644 --- a/API.md +++ b/API.md @@ -31,6 +31,10 @@ - [`latest` request strategy](#latest-request-strategy) - [`minmax` request strategy](#minmax-request-strategy) - [Telemetry Formats](#telemetry-formats) + - [Built-in Formats](#built-in-formats) + - [**Number Format (default):**](#number-format-default) + - [**String Format**](#string-format) + - [**Enum Format**](#enum-format) - [Registering Formats](#registering-formats) - [Telemetry Data](#telemetry-data) - [Telemetry Datums](#telemetry-datums) @@ -59,6 +63,12 @@ - [Custom Indicators](#custom-indicators) - [Priority API](#priority-api) - [Priority Types](#priority-types) + - [User API](#user-api) + - [Example](#example) + - [Visibility-Based Rendering in View Providers](#visibility-based-rendering-in-view-providers) + - [Overview](#overview) + - [Implementing Visibility-Based Rendering](#implementing-visibility-based-rendering) + - [Example](#example-1) @@ -1301,9 +1311,11 @@ Open MCT provides some built-in priority values that can be used in the applicat Currently, the Open MCT Priority API provides (type: numeric value): +- HIGHEST: Infinity - HIGH: 1000 - Default: 0 - LOW: -1000 +- LOWEST: -Infinity View provider Example: diff --git a/e2e/appActions.js b/e2e/appActions.js index 7bb82726f2..105854b4cc 100644 --- a/e2e/appActions.js +++ b/e2e/appActions.js @@ -68,7 +68,11 @@ import { v4 as genUuid } from 'uuid'; * @param {string | import('../src/api/objects/ObjectAPI').Identifier} [options.parent='mine'] - The Identifier or uuid of the parent object. Defaults to 'mine' folder * @returns {Promise} An object containing information about the newly created domain object. */ -async function createDomainObjectWithDefaults(page, { type, name, parent = 'mine' }) { +async function createDomainObjectWithDefaults( + page, + { type, name, parent = 'mine' }, + additionalOptions = {} +) { if (!name) { name = `${type}:${genUuid()}`; } @@ -89,6 +93,13 @@ async function createDomainObjectWithDefaults(page, { type, name, parent = 'mine await page.getByLabel('Title', { exact: true }).fill(''); await page.getByLabel('Title', { exact: true }).fill(name); + if (additionalOptions) { + for (const [key, value] of Object.entries(additionalOptions)) { + // eslint-disable-next-line playwright/no-raw-locators + await page.locator(`#form-${key}`).fill(value); + } + } + if (page.testNotes) { // Fill the "Notes" section with information about the // currently running test and its project. @@ -105,7 +116,7 @@ async function createDomainObjectWithDefaults(page, { type, name, parent = 'mine if (await _isInEditMode(page, uuid)) { // Save (exit edit mode) - await page.getByRole('button', { name: 'Save' }).click(); + await page.getByRole('button', { name: 'Save', exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); } diff --git a/e2e/helper/planningUtils.js b/e2e/helper/planningUtils.js index 2e1d8a8a70..40e5e0193f 100644 --- a/e2e/helper/planningUtils.js +++ b/e2e/helper/planningUtils.js @@ -224,7 +224,7 @@ export async function createTimelistWithPlanAndSetActivityInProgress(page, planJ await page.getByRole('button', { name: 'Edit Object' }).click(); // Find the display properties section in the inspector - await page.getByRole('tab', { name: 'View Properties' }).click(); + await page.getByRole('tab', { name: 'Config' }).click(); // Switch to expanded view and save the setting await page.getByLabel('Display Style').selectOption({ label: 'Expanded' }); diff --git a/e2e/helper/plotTagsUtils.js b/e2e/helper/plotTagsUtils.js index e3af8faf4b..0393f544b7 100644 --- a/e2e/helper/plotTagsUtils.js +++ b/e2e/helper/plotTagsUtils.js @@ -76,6 +76,7 @@ export async function createTags({ page, canvas, xEnd = 700, yEnd = 520 }) { export async function testTelemetryItem(page, telemetryItem) { // Check that telemetry item also received the tag await page.goto(telemetryItem.url); + await page.getByRole('tab', { name: 'Annotations' }).click(); await expect(page.getByText('No tags to display for this item')).toBeVisible(); @@ -93,6 +94,7 @@ export async function testTelemetryItem(page, telemetryItem) { y: 100 } }); + await page.getByRole('tab', { name: 'Annotations' }).click(); await expect(page.getByText('Science')).toBeVisible(); await expect(page.getByText('Driving')).toBeHidden(); @@ -107,6 +109,8 @@ export async function basicTagsTests(page) { // Search for Driving await page.getByRole('searchbox', { name: 'Search Input' }).click(); + await page.getByRole('tab', { name: 'Annotations' }).click(); + // Clicking elsewhere should cause annotation selection to be cleared await expect(page.getByText('No tags to display for this item')).toBeVisible(); // @@ -119,6 +123,8 @@ export async function basicTagsTests(page) { .first() .click(); + await page.getByRole('tab', { name: 'Annotations' }).click(); + // Delete Driving Tag await page.hover('[aria-label="Tag"]:has-text("Driving")'); await page.locator('[aria-label="Remove tag Driving"]').click(); @@ -155,6 +161,8 @@ export async function basicTagsTests(page) { } }); + await page.getByRole('tab', { name: 'Annotations' }).click(); + //Expect Science to be visible but Driving to be hidden await expect(page.getByText('Science')).toBeVisible(); await expect(page.getByText('Driving')).toBeHidden(); @@ -170,7 +178,7 @@ export async function basicTagsTests(page) { }); // Add Driving Tag again - await page.getByText('Annotations').click(); + await page.getByRole('tab', { name: 'Annotations' }).click(); await page.getByRole('button', { name: /Add Tag/ }).click(); await page.getByPlaceholder('Type to select tag').click(); await page.getByText('Driving').click(); diff --git a/e2e/tests/functional/planning/timelist.e2e.spec.js b/e2e/tests/functional/planning/timelist.e2e.spec.js index abfef4a584..1e43a87919 100644 --- a/e2e/tests/functional/planning/timelist.e2e.spec.js +++ b/e2e/tests/functional/planning/timelist.e2e.spec.js @@ -132,7 +132,7 @@ test("View a timelist in expanded view, verify all the activities are displayed await page.getByRole('button', { name: 'Edit Object' }).click(); // Find the display properties section in the inspector - await page.getByRole('tab', { name: 'View Properties' }).click(); + await page.getByRole('tab', { name: 'Config' }).click(); // Switch to expanded view and save the setting await page.getByLabel('Display Style').selectOption({ label: 'Expanded' }); diff --git a/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js b/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js index a2e8fd42a7..7b916a4789 100644 --- a/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js +++ b/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js @@ -236,7 +236,7 @@ test.describe('Display Layout', () => { name: new RegExp(sineWaveObject.name) }); await sineWaveGeneratorTreeItem.dragTo(page.getByLabel('Layout Grid')); - await page.locator('button[title="Save"]').click(); + await page.getByLabel('Save', { exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); // Subscribe to the Sine Wave Generator data @@ -278,7 +278,7 @@ test.describe('Display Layout', () => { name: new RegExp(sineWaveObject.name) }); await sineWaveGeneratorTreeItem.dragTo(page.getByLabel('Layout Grid')); - await page.locator('button[title="Save"]').click(); + await page.getByLabel('Save', { exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); // Subscribe to the Sine Wave Generator data @@ -317,7 +317,7 @@ test.describe('Display Layout', () => { name: new RegExp(sineWaveObject.name) }); await sineWaveGeneratorTreeItem.dragTo(page.getByLabel('Layout Grid')); - await page.locator('button[title="Save"]').click(); + await page.getByLabel('Save', { exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); expect.soft(await page.locator('.l-layout .l-layout__frame').count()).toEqual(1); @@ -358,7 +358,7 @@ test.describe('Display Layout', () => { name: new RegExp(sineWaveObject.name) }); await sineWaveGeneratorTreeItem.dragTo(page.getByLabel('Layout Grid')); - await page.locator('button[title="Save"]').click(); + await page.getByLabel('Save', { exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); expect.soft(await page.locator('.l-layout .l-layout__frame').count()).toEqual(1); @@ -413,7 +413,7 @@ test.describe('Display Layout', () => { await page.locator('div[title="Resize object width"] > input').click(); await page.locator('div[title="Resize object width"] > input').fill('70'); - await page.locator('button[title="Save"]').click(); + await page.getByLabel('Save', { exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); const startDate = '2021-12-30 01:01:00.000Z'; @@ -473,7 +473,7 @@ test.describe('Display Layout', () => { await page.getByText('View type').click(); await page.getByText('Overlay Plot').click(); - await page.getByLabel('Save').click(); + await page.getByLabel('Save', { exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); // Time to inspect some network traffic @@ -531,7 +531,7 @@ test.describe('Display Layout', () => { await stateGeneratorTreeItem.click({ button: 'right' }); await page.getByLabel('Edit Properties...').click(); await page.getByLabel('State Duration (seconds)', { exact: true }).fill('0.1'); - await page.getByLabel('Save').click(); + await page.getByLabel('Save', { exact: true }).click(); // Create a Table for filtering ON values const tableFilterOnValue = await createDomainObjectWithDefaults(page, { @@ -555,14 +555,14 @@ test.describe('Display Layout', () => { await page.goto(tableFilterOnValue.url); await stateGeneratorTreeItem.dragTo(page.getByLabel('Object View')); await selectFilterOption(page, '1'); - await page.getByLabel('Save').click(); + await page.getByLabel('Save', { exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); // Navigate to OFF filtering table and add state generator and setup filters await page.goto(tableFilterOffValue.url); await stateGeneratorTreeItem.dragTo(page.getByLabel('Object View')); await selectFilterOption(page, '0'); - await page.getByLabel('Save').click(); + await page.getByLabel('Save', { exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); // Navigate to the display layout and edit it @@ -586,7 +586,7 @@ test.describe('Display Layout', () => { // eslint-disable-next-line playwright/no-force-option force: true }); - await page.getByLabel('Save').click(); + await page.getByLabel('Save', { exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); // Get the tables so we can verify filtering is working as expected diff --git a/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js b/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js index 80e28bb5cd..181968e517 100644 --- a/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js +++ b/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js @@ -54,8 +54,8 @@ test.describe('Testing numeric data with inspector data visualization (i.e., dat await page.goto(exampleDataVisualizationSource.url); - await page.getByRole('tab', { name: 'Data Visualization' }).click(); await page.getByRole('cell', { name: /First Sine Wave Generator/ }).click(); + await page.getByRole('tab', { name: 'Data Visualization' }).click(); await expect(page.getByText('Numeric Data')).toBeVisible(); await expect( page.locator('span.plot-series-name', { hasText: 'First Sine Wave Generator Hz' }) @@ -63,6 +63,7 @@ test.describe('Testing numeric data with inspector data visualization (i.e., dat await expect(page.locator('.js-series-data-loaded')).toBeVisible(); await page.getByRole('cell', { name: /Second Sine Wave Generator/ }).click(); + await page.getByRole('tab', { name: 'Data Visualization' }).click(); await expect(page.getByText('Numeric Data')).toBeVisible(); await expect( page.locator('span.plot-series-name', { hasText: 'Second Sine Wave Generator Hz' }) @@ -77,6 +78,8 @@ test.describe('Testing numeric data with inspector data visualization (i.e., dat // ensure our new tab's title is correct const newPage = await pagePromise; await newPage.waitForLoadState(); + await page.getByRole('tab', { name: 'Data Visualization' }).click(); + // expect new tab title to contain 'Second Sine Wave Generator' await expect(newPage).toHaveTitle('Second Sine Wave Generator'); diff --git a/e2e/tests/functional/plugins/lad/lad.e2e.spec.js b/e2e/tests/functional/plugins/lad/lad.e2e.spec.js index fd2f385bd7..23b5de4206 100644 --- a/e2e/tests/functional/plugins/lad/lad.e2e.spec.js +++ b/e2e/tests/functional/plugins/lad/lad.e2e.spec.js @@ -53,7 +53,6 @@ test.describe('Testing LAD table configuration', () => { test('in edit mode, LAD Tables provide ability to hide columns', async ({ page }) => { // Edit LAD table await page.getByLabel('Edit Object').click(); - await page.getByRole('tab', { name: 'LAD Table Configuration' }).click(); // make sure headers are visible initially await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeVisible(); @@ -114,7 +113,6 @@ test.describe('Testing LAD table configuration', () => { // Edit LAD table await page.getByLabel('Edit Object').click(); - await page.getByRole('tab', { name: 'LAD Table Configuration' }).click(); // show timestamp column await page.getByLabel('Timestamp', { exact: true }).check(); @@ -142,7 +140,6 @@ test.describe('Testing LAD table configuration', () => { // Edit LAD table await page.getByLabel('Edit Object').click(); - await page.getByRole('tab', { name: 'LAD Table Configuration' }).click(); // show units, type, and WATCH columns await page.getByLabel('Units').check(); @@ -182,7 +179,6 @@ test.describe('Testing LAD table configuration', () => { // Edit LAD table await page.getByLabel('Edit Object').click(); - await page.getByRole('tab', { name: 'LAD Table Configuration' }).click(); // make sure Sine Wave headers are visible initially too await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeVisible(); diff --git a/e2e/tests/functional/plugins/notebook/notebookTags.e2e.spec.js b/e2e/tests/functional/plugins/notebook/notebookTags.e2e.spec.js index 4c86a2a97f..a0c9d6e50d 100644 --- a/e2e/tests/functional/plugins/notebook/notebookTags.e2e.spec.js +++ b/e2e/tests/functional/plugins/notebook/notebookTags.e2e.spec.js @@ -65,9 +65,11 @@ test.describe('Tagging in Notebooks @addInit', () => { }); test('Can add tags with blank entry', async ({ page }) => { await createDomainObjectWithDefaults(page, { type: 'Notebook' }); - await page.getByRole('tab', { name: 'Annotations' }).click(); await enterTextEntry(page, ''); + + await page.getByRole('tab', { name: 'Annotations' }).click(); + await page.hover(`button:has-text("Add Tag")`); await page.locator(`button:has-text("Add Tag")`).click(); diff --git a/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js b/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js index 62e9df6229..e6c1d88963 100644 --- a/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js +++ b/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js @@ -47,8 +47,6 @@ test.describe('Notebook Tests with CouchDB @couchdb @network', () => { }); test('Inspect Notebook Entry Network Requests', async ({ page }) => { - //Ensure we're on the annotations Tab in the inspector - await page.getByText('Annotations').click(); // Expand sidebar await page.locator('.c-notebook__toggle-nav-button').click(); @@ -86,6 +84,9 @@ test.describe('Notebook Tests with CouchDB @couchdb @network', () => { await page.waitForLoadState('networkidle'); expect(notebookElementsRequests.length).toBeLessThanOrEqual(2); + //Ensure we're on the annotations Tab in the inspector + await page.getByText('Annotations').click(); + // Add some tags // Network Requests are for each tag creation are: // 1) Getting the original path of the parent object @@ -180,8 +181,8 @@ test.describe('Notebook Tests with CouchDB @couchdb @network', () => { type: 'issue', description: 'https://github.com/akhenry/openmct-yamcs/issues/69' }); - await page.getByText('Annotations').click(); await nbUtils.enterTextEntry(page, 'First Entry'); + await page.getByText('Annotations').click(); // Add three tags await addTagAndAwaitNetwork(page, 'Science'); diff --git a/e2e/tests/functional/plugins/plot/overlayPlot.e2e.spec.js b/e2e/tests/functional/plugins/plot/overlayPlot.e2e.spec.js index 7af05a00ea..fe177f4548 100644 --- a/e2e/tests/functional/plugins/plot/overlayPlot.e2e.spec.js +++ b/e2e/tests/functional/plugins/plot/overlayPlot.e2e.spec.js @@ -100,6 +100,9 @@ test.describe('Overlay Plot', () => { await page.getByLabel('Expand By Default').check(); await page.getByLabel('Save').click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); + + await page.getByRole('tab', { name: 'Config' }).click(); + // Assert that the legend is now open await expect(page.getByLabel('Plot Legend Collapsed')).toBeHidden(); await expect(page.getByLabel('Plot Legend Expanded')).toBeVisible(); @@ -111,6 +114,9 @@ test.describe('Overlay Plot', () => { // Assert that the legend is expanded on page load await page.reload(); + + await page.getByRole('tab', { name: 'Config' }).click(); + await expect(page.getByLabel('Plot Legend Collapsed')).toBeHidden(); await expect(page.getByLabel('Plot Legend Expanded')).toBeVisible(); await expect(page.getByRole('cell', { name: 'Name' })).toBeVisible(); diff --git a/e2e/tests/functional/plugins/plot/previews.e2e.spec.js b/e2e/tests/functional/plugins/plot/previews.e2e.spec.js index 7a5b425fe4..c1f51fec18 100644 --- a/e2e/tests/functional/plugins/plot/previews.e2e.spec.js +++ b/e2e/tests/functional/plugins/plot/previews.e2e.spec.js @@ -50,7 +50,7 @@ test.describe('Plots work in Previews', () => { }); const layoutGridHolder = page.getByLabel('Test Display Layout Layout Grid'); await sineWaveGeneratorTreeItem.dragTo(layoutGridHolder); - await page.getByLabel('Save').click(); + await page.getByLabel('Save', { exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); // right click on the plot and select view large @@ -67,7 +67,7 @@ test.describe('Plots work in Previews', () => { await page.getByLabel('Move Sub-object Frame').click(); await page.getByText('View type').click(); await page.getByText('Overlay Plot').click(); - await page.getByLabel('Save').click(); + await page.getByLabel('Save', { exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); await expect( page.getByLabel('Test Display Layout Layout', { exact: true }).getByLabel('Plot Canvas') diff --git a/e2e/tests/functional/plugins/plot/stackedPlot.e2e.spec.js b/e2e/tests/functional/plugins/plot/stackedPlot.e2e.spec.js index 6926097c8f..6df0b12a39 100644 --- a/e2e/tests/functional/plugins/plot/stackedPlot.e2e.spec.js +++ b/e2e/tests/functional/plugins/plot/stackedPlot.e2e.spec.js @@ -152,14 +152,14 @@ test.describe('Stacked Plot', () => { }) => { await page.goto(stackedPlot.url); - await page.getByRole('tab', { name: 'Config' }).click(); - // Click on the 1st plot await page .getByLabel('Stacked Plot Item Sine Wave Generator A') .getByLabel('Plot Canvas') .click(); + await page.getByRole('tab', { name: 'Config' }).click(); + // Assert that the inspector shows the Y Axis properties for swgA await expect(page.getByRole('heading', { name: 'Plot Series' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible(); @@ -172,6 +172,9 @@ test.describe('Stacked Plot', () => { .getByLabel('Stacked Plot Item Sine Wave Generator B') .getByLabel('Plot Canvas') .click(); + + await page.getByRole('tab', { name: 'Config' }).click(); + // Assert that the inspector shows the Y Axis properties for swgB await expect(page.getByRole('heading', { name: 'Plot Series' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible(); @@ -184,6 +187,9 @@ test.describe('Stacked Plot', () => { .getByLabel('Stacked Plot Item Sine Wave Generator C') .getByLabel('Plot Canvas') .click(); + + await page.getByRole('tab', { name: 'Config' }).click(); + // Assert that the inspector shows the Y Axis properties for swgB await expect(page.getByRole('heading', { name: 'Plot Series' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible(); @@ -194,7 +200,7 @@ test.describe('Stacked Plot', () => { // Go into edit mode await page.getByLabel('Edit Object').click(); - await page.getByRole('tab', { name: 'Config' }).click(); + // await page.getByRole('tab', { name: 'Config' }).click(); // Click on the 1st plot await page.getByLabel('Stacked Plot Item Sine Wave Generator A').click(); @@ -233,11 +239,11 @@ test.describe('Stacked Plot', () => { // Go into edit mode await page.getByLabel('Edit Object').click(); - await page.getByRole('tab', { name: 'Config' }).click(); - // Click on canvas for the 1st plot await page.getByLabel(`Stacked Plot Item ${swgA.name}`).click(); + await page.getByRole('tab', { name: 'Config' }).click(); + // Expand config for the series await page.getByLabel('Expand Sine Wave Generator A Plot Series Options').click(); @@ -255,6 +261,8 @@ test.describe('Stacked Plot', () => { // Click on canvas for the 1st plot await page.getByLabel(`Stacked Plot Item ${swgA.name}`).click(); + await page.getByRole('tab', { name: 'Config' }).click(); + // Expand config for the series await page.getByLabel('Expand Sine Wave Generator A Plot Series Options').click(); diff --git a/e2e/tests/functional/plugins/styling/stackedPlotStyling.e2e.spec.js b/e2e/tests/functional/plugins/styling/stackedPlotStyling.e2e.spec.js index 3c5310d56f..d0976712c2 100644 --- a/e2e/tests/functional/plugins/styling/stackedPlotStyling.e2e.spec.js +++ b/e2e/tests/functional/plugins/styling/stackedPlotStyling.e2e.spec.js @@ -45,6 +45,8 @@ const setFontFamily = '"Andale Mono", sans-serif'; test.describe('Stacked Plot styling', () => { let stackedPlot; + let overlayPlot1; + let overlayPlot2; test.beforeEach(async ({ page }) => { await page.goto('./', { waitUntil: 'domcontentloaded' }); @@ -54,17 +56,30 @@ test.describe('Stacked Plot styling', () => { name: 'StackedPlot1' }); + // create two overlay plots + overlayPlot1 = await createDomainObjectWithDefaults(page, { + type: 'Overlay Plot', + name: 'Overlay Plot 1', + parent: stackedPlot.uuid + }); + + overlayPlot2 = await createDomainObjectWithDefaults(page, { + type: 'Overlay Plot', + name: 'Overlay Plot 2', + parent: stackedPlot.uuid + }); + // Create two SWGs and attach them to the Stacked Plot await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator', name: 'Sine Wave Generator 1', - parent: stackedPlot.uuid + parent: overlayPlot1.uuid }); await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator', name: 'Sine Wave Generator 2', - parent: stackedPlot.uuid + parent: overlayPlot2.uuid }); }); @@ -138,21 +153,21 @@ test.describe('Stacked Plot styling', () => { NO_STYLE_RGBA, NO_STYLE_RGBA, hexToRGB(setTextColor), - page.getByLabel('Stacked Plot Item Sine Wave Generator 1') + page.getByLabel('Stacked Plot Item Overlay Plot 1') ); await checkStyles( NO_STYLE_RGBA, NO_STYLE_RGBA, hexToRGB(setTextColor), - page.getByLabel('Stacked Plot Item Sine Wave Generator 2') + page.getByLabel('Stacked Plot Item Overlay Plot 2') ); await checkFontStyles( setFontSize, setFontWeight, setFontFamily, - page.getByLabel('Stacked Plot Item Sine Wave Generator 1') + page.getByLabel('Stacked Plot Item Overlay Plot 1') ); }); @@ -169,19 +184,19 @@ test.describe('Stacked Plot styling', () => { await page.getByRole('tab', { name: 'Styles' }).click(); - //Check default styles for SWG1 and SWG2 + //Check default styles for overlayPlot1 and overlayPlot2 await checkStyles( NO_STYLE_RGBA, NO_STYLE_RGBA, hexToRGB(defaultTextColor), - page.getByLabel('Stacked Plot Item Sine Wave Generator 1') + page.getByLabel('Stacked Plot Item Overlay Plot 1') ); await checkStyles( NO_STYLE_RGBA, NO_STYLE_RGBA, hexToRGB(defaultTextColor), - page.getByLabel('Stacked Plot Item Sine Wave Generator 2') + page.getByLabel('Stacked Plot Item Overlay Plot 2') ); // Set styles using setStyles function on StackedPlot1 but not StackedPlot2 @@ -190,11 +205,11 @@ test.describe('Stacked Plot styling', () => { setBorderColor, setBackgroundColor, setTextColor, - page.getByLabel('Stacked Plot Item Sine Wave Generator 1') + page.getByLabel('Stacked Plot Item Overlay Plot 1') ); //Set Font Styles on SWG1 but not SWG2 - await page.getByLabel('Stacked Plot Item Sine Wave Generator 1').click(); + await page.getByLabel('Stacked Plot Item Overlay Plot 1').click(); //Set Font Size to 72 await page.getByLabel('Set Font Size').click(); await page.getByRole('menuitem', { name: '72px' }).click(); diff --git a/e2e/tests/functional/ui/inspector.e2e.spec.js b/e2e/tests/functional/ui/inspector.e2e.spec.js index 54ddc99502..10d45e3a2d 100644 --- a/e2e/tests/functional/ui/inspector.e2e.spec.js +++ b/e2e/tests/functional/ui/inspector.e2e.spec.js @@ -1,3 +1,5 @@ +/* eslint-disable playwright/no-conditional-in-test */ +/* eslint-disable playwright/no-conditional-expect */ /***************************************************************************** * Open MCT, Copyright (c) 2014-2024, United States Government * as represented by the Administrator of the National Aeronautics and Space @@ -31,6 +33,104 @@ Enim nec dui nunc mattis. Cursus turpis massa tincidunt dui ut. Donec adipiscing Proin libero nunc consequat interdum varius sit amet mattis vulputate. Metus dictum at tempor commodo ullamcorper a lacus vestibulum sed. Quisque non tellus orci ac auctor augue mauris. Id ornare arcu odio ut. Rhoncus est pellentesque elit ullamcorper dignissim. Senectus et netus et malesuada fames ac turpis egestas. Volutpat ac tincidunt vitae semper quis lectus nulla. Adipiscing elit duis tristique sollicitudin. Ipsum faucibus vitae aliquet nec ullamcorper sit. Gravida neque convallis a cras semper auctor neque vitae tempus. Porttitor leo a diam sollicitudin tempor id. Dictum non consectetur a erat nam at lectus. At volutpat diam ut venenatis tellus in. Morbi enim nunc faucibus a pellentesque sit amet. Cursus in hac habitasse platea. Sed augue lacus viverra vitae. `; +const viewsTabsMatrix = { + Clock: { + Browse: ['Properties'] + }, + 'Condition Set': { + Browse: ['Properties', 'Elements', 'Annotations'], + Edit: ['Elements', 'Properties'] + }, + 'Condition Widget': { + Browse: ['Properties', 'Styles'], + Edit: ['Styles', 'Properties'] + }, + 'Display Layout': { + Browse: ['Properties', 'Elements', 'Styles'], + Edit: ['Elements', 'Styles', 'Properties'] + }, + 'Event Message Generator': { + Browse: ['Properties'] + }, + 'Event Message Generator with Acknowledge': { + Browse: ['Properties'] + }, + 'Example Imagery': { + Browse: ['Properties', 'Annotations'] + }, + 'Flexible Layout': { + Browse: ['Properties', 'Elements', 'Styles'], + Edit: ['Elements', 'Styles', 'Properties'] + }, + Folder: { + Browse: ['Properties'] + }, + 'Gantt Chart': { + Browse: ['Properties', 'Config', 'Elements'], + Edit: ['Config', 'Elements', 'Properties'] + }, + Gauge: { + Browse: ['Properties', 'Elements', 'Styles'], + Edit: ['Elements', 'Styles', 'Properties'] + }, + Graph: { + Browse: ['Properties', 'Config', 'Elements', 'Styles'], + Edit: ['Config', 'Elements', 'Styles', 'Properties'] + }, + Hyperlink: { + Browse: ['Properties'], + required: { + url: 'https://www.google.com', + displayText: 'Google' + } + }, + 'LAD Table': { + Browse: ['Properties', 'Config', 'Elements', 'Styles'], + Edit: ['Config', 'Elements', 'Styles', 'Properties'] + }, + 'LAD Table Set': { + Browse: ['Properties', 'Config', 'Elements', 'Styles'], + Edit: ['Config', 'Elements', 'Styles', 'Properties'] + }, + Notebook: { + Browse: ['Properties'] + }, + 'Overlay Plot': { + Browse: ['Properties', 'Config', 'Annotations', 'Styles'], + Edit: ['Config', 'Elements', 'Styles', 'Filters', 'Properties'] + }, + 'Scatter Plot': { + Browse: ['Properties', 'Config', 'Elements', 'Styles'], + Edit: ['Config', 'Elements', 'Styles', 'Properties'] + }, + 'Sine Wave Generator': { + Browse: ['Properties', 'Annotations'] + }, + 'Stacked Plot': { + Browse: ['Properties', 'Config', 'Annotations', 'Elements', 'Styles'], + Edit: ['Config', 'Elements', 'Styles', 'Properties'] + }, + 'Tabs View': { + Browse: ['Properties', 'Elements', 'Styles'], + Edit: ['Elements', 'Styles', 'Properties'] + }, + 'Telemetry Table': { + Browse: ['Properties', 'Config', 'Elements', 'Styles'], + Edit: ['Config', 'Elements', 'Styles', 'Filters', 'Properties'] + }, + 'Time List': { + Browse: ['Properties', 'Config', 'Elements'], + Edit: ['Config', 'Elements', 'Properties'] + }, + 'Time Strip': { + Browse: ['Properties', 'Elements'], + Edit: ['Elements', 'Properties'] + }, + Timer: { + Browse: ['Properties'] + } +}; + test.describe('Inspector tests', () => { test.beforeEach(async ({ page }) => { await page.goto('./', { waitUntil: 'domcontentloaded' }); @@ -72,4 +172,49 @@ test.describe('Inspector tests', () => { await expect(lastInspectorPropertyValue).toBeInViewport(); }); + + test(`Inspector tabs show the correct tabs per view and mode`, async ({ page }) => { + // loop through each view type + for (const view of Object.keys(viewsTabsMatrix)) { + const viewConfig = viewsTabsMatrix[view]; + const createOptions = { + type: view, + name: view + }; + + // create and navigate to view; + const objectInfo = await createDomainObjectWithDefaults( + page, + createOptions, + viewConfig.required ? viewConfig.required : {} + ); + await page.goto(objectInfo.url); + + // verify correct number of tabs for browse mode + expect(await page.getByRole('tab').count()).toBe(Object.keys(viewConfig.Browse).length); + + // verify correct order of tabs for browse mode + for (const [index, value] of Object.entries(viewConfig.Browse)) { + const tab = page.getByRole('tab').nth(index); + await expect(tab).toHaveText(value); + } + + // enter Edit if necessary + if (viewConfig.Edit) { + await page.getByLabel('Edit Object').click(); + + // verify correct number of tabs for edit mode + expect(await page.getByRole('tab').count()).toBe(Object.keys(viewConfig.Edit).length); + + // verify correct order of tabs for edit mode + for (const [index, value] of Object.entries(viewConfig.Edit)) { + const tab = page.getByRole('tab').nth(index); + await expect(tab).toHaveText(value); + } + + await page.getByLabel('Save').first().click(); + await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); + } + } + }); }); diff --git a/e2e/tests/visual-a11y/components/inspector.visual.spec.js b/e2e/tests/visual-a11y/components/inspector.visual.spec.js index 5f1dd2be00..d840ec7941 100644 --- a/e2e/tests/visual-a11y/components/inspector.visual.spec.js +++ b/e2e/tests/visual-a11y/components/inspector.visual.spec.js @@ -40,6 +40,9 @@ test.describe('Visual - Inspector @ally @clock', () => { }); test('Inspector from overlay_plot_with_delay_storage @localStorage', async ({ page, theme }) => { + // navigate to the plot + await page.getByRole('gridcell', { name: 'Overlay Plot with 5s Delay' }).click(); + //Expand the Inspector Pane await page.getByRole('button', { name: 'Inspect' }).click(); diff --git a/e2e/tests/visual-a11y/search.visual.spec.js b/e2e/tests/visual-a11y/search.visual.spec.js index b399556913..3de737a2e3 100644 --- a/e2e/tests/visual-a11y/search.visual.spec.js +++ b/e2e/tests/visual-a11y/search.visual.spec.js @@ -83,7 +83,7 @@ test.describe('Grand Search @a11y', () => { ); // Save and finish editing the Display Layout - await page.getByRole('button', { name: 'Save' }).click(); + await page.getByRole('button', { name: 'Save', exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); // Search for the object diff --git a/e2e/tests/visual-a11y/styling.visual.spec.js b/e2e/tests/visual-a11y/styling.visual.spec.js index d8edc3b165..38cbd7b254 100644 --- a/e2e/tests/visual-a11y/styling.visual.spec.js +++ b/e2e/tests/visual-a11y/styling.visual.spec.js @@ -100,9 +100,12 @@ test.describe('Flexible Layout styling @a11y', () => { ); // Save Flexible Layout - await page.getByRole('button', { name: 'Save' }).click(); + await page.getByRole('button', { name: 'Save', exact: true }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); + // Select styles tab + await page.getByRole('tab', { name: 'Styles' }).click(); + await percySnapshot( page, `Saved Styled Flex Layout with Styled StackedPlot (theme: '${theme}')` @@ -124,17 +127,30 @@ test.describe('Stacked Plot styling @a11y', () => { name: 'StackedPlot1' }); - // Create two SWGs and attach them to the Stacked Plot + // Create an overlay plots to hold the SWGs + const overlayPlot1 = await createDomainObjectWithDefaults(page, { + type: 'Overlay Plot', + name: 'Overlay Plot 1', + parent: stackedPlot.uuid + }); + + const overlayPlot2 = await createDomainObjectWithDefaults(page, { + type: 'Overlay Plot', + name: 'Overlay Plot 2', + parent: stackedPlot.uuid + }); + + // Create two SWGs and attach them to the overlay plots await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator', name: 'Sine Wave Generator 1', - parent: stackedPlot.uuid + parent: overlayPlot1.uuid }); await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator', name: 'Sine Wave Generator 2', - parent: stackedPlot.uuid + parent: overlayPlot2.uuid }); }); @@ -177,7 +193,7 @@ test.describe('Stacked Plot styling @a11y', () => { setBorderColor, setBackgroundColor, setTextColor, - page.getByLabel('Stacked Plot Item Sine Wave Generator 1') + page.getByLabel('Stacked Plot Item Overlay Plot 1') ); await percySnapshot(page, `Edit Mode StackedPlot with Styled SWG (theme: '${theme}')`); diff --git a/src/api/annotation/AnnotationAPI.js b/src/api/annotation/AnnotationAPI.js index 3ca5889b1b..e5b52e731e 100644 --- a/src/api/annotation/AnnotationAPI.js +++ b/src/api/annotation/AnnotationAPI.js @@ -582,4 +582,15 @@ export default class AnnotationAPI extends EventEmitter { _.isEqual(targets, otherTargets) ); } + + /** + * Checks if the given type is annotatable + * @param {string} type The type to check + * @returns {boolean} Returns true if the type is annotatable + */ + isAnnotatableType(type) { + const types = this.openmct.types.getAllTypes(); + + return types[type]?.definition?.annotatable; + } } diff --git a/src/api/priority/PriorityAPI.js b/src/api/priority/PriorityAPI.js index 8d915b1e1f..e82ff8f3f8 100644 --- a/src/api/priority/PriorityAPI.js +++ b/src/api/priority/PriorityAPI.js @@ -21,8 +21,11 @@ *****************************************************************************/ const PRIORITIES = Object.freeze({ + HIGHEST: Infinity, HIGH: 1000, DEFAULT: 0, - LOW: -1000 + LOW: -1000, + LOWEST: -Infinity }); + export default PRIORITIES; diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js index e5be70590f..122a38ec96 100644 --- a/src/api/telemetry/TelemetryAPI.js +++ b/src/api/telemetry/TelemetryAPI.js @@ -284,6 +284,33 @@ export default class TelemetryAPI { return value; } + /** + * Determines whether a domain object has numeric telemetry data. + * A domain object has numeric telemetry if it: + * 1. Has a telemetry property + * 2. Has telemetry metadata with domain values (like timestamps) + * 3. Has range values (measurements) where at least one is numeric + * + * @method hasNumericTelemetry + * @param {import('openmct').DomainObject} domainObject The domain object to check + * @returns {boolean} True if the object has numeric telemetry, false otherwise + */ + hasNumericTelemetry(domainObject) { + if (!Object.prototype.hasOwnProperty.call(domainObject, 'telemetry')) { + return false; + } + + const metadata = this.openmct.telemetry.getMetadata(domainObject); + const rangeValues = metadata.valuesForHints(['range']); + const domains = metadata.valuesForHints(['domain']); + + return ( + domains.length > 0 && + rangeValues.length > 0 && + !rangeValues.every((value) => value.format === 'string') + ); + } + /** * Generates a numeric hash value for an options object. The hash is consistent * for equivalent option objects regardless of property order. diff --git a/src/api/types/TypeRegistry.js b/src/api/types/TypeRegistry.js index 9506992edb..45928f8724 100644 --- a/src/api/types/TypeRegistry.js +++ b/src/api/types/TypeRegistry.js @@ -89,6 +89,17 @@ export default class TypeRegistry { get(typeKey) { return this.types[typeKey] || UNKNOWN_TYPE; } + /** + * List all registered types. + * @returns {Type[]} all registered types + */ + getAllTypes() { + return this.types; + } + /** + * Import legacy types. + * @param {TypeDefinition[]} types the types to import + */ importLegacyTypes(types) { types .filter((t) => this.get(t.key) === UNKNOWN_TYPE) diff --git a/src/plugins/LADTable/LADTableConfiguration.js b/src/plugins/LADTable/LADTableConfiguration.js index fac6ba63d9..d5c69671f6 100644 --- a/src/plugins/LADTable/LADTableConfiguration.js +++ b/src/plugins/LADTable/LADTableConfiguration.js @@ -41,9 +41,10 @@ export default class LADTableConfiguration extends EventEmitter { } getConfiguration() { - const configuration = this.domainObject.configuration || {}; - configuration.hiddenColumns = configuration.hiddenColumns || {}; + const configuration = this.domainObject.configuration ?? {}; + configuration.hiddenColumns = configuration.hiddenColumns ?? {}; configuration.isFixedLayout = configuration.isFixedLayout ?? true; + configuration.objectStyles = configuration.objectStyles ?? {}; return configuration; } diff --git a/src/plugins/LADTable/LADTableConfigurationViewProvider.js b/src/plugins/LADTable/LADTableConfigurationViewProvider.js index 143079d17e..65c0918df7 100644 --- a/src/plugins/LADTable/LADTableConfigurationViewProvider.js +++ b/src/plugins/LADTable/LADTableConfigurationViewProvider.js @@ -27,7 +27,7 @@ import LadTableConfiguration from './components/LadTableConfiguration.vue'; export default function LADTableConfigurationViewProvider(openmct) { return { key: 'lad-table-configuration', - name: 'LAD Table Configuration', + name: 'Config', canView(selection) { if (selection.length !== 1 || selection[0].length === 0) { return false; @@ -61,7 +61,7 @@ export default function LADTableConfigurationViewProvider(openmct) { _destroy = destroy; }, priority() { - return 1; + return openmct.editor.isEditing() ? openmct.priority.HIGH : openmct.priority.DEFAULT; }, destroy() { if (_destroy) { diff --git a/src/plugins/LADTable/components/LadTableConfiguration.vue b/src/plugins/LADTable/components/LadTableConfiguration.vue index 2c44f6e23b..b76d81157b 100644 --- a/src/plugins/LADTable/components/LadTableConfiguration.vue +++ b/src/plugins/LADTable/components/LadTableConfiguration.vue @@ -22,28 +22,24 @@ @@ -62,7 +58,8 @@ export default { isEditing: this.openmct.editor.isEditing(), configuration: ladTableConfiguration.getConfiguration(), items: [], - ladTableObjects: [] + ladTableObjects: [], + ladTelemetryObjects: {} }; }, computed: { @@ -150,11 +147,14 @@ export default { this.ladTableObjects.push(ladTable); const composition = this.openmct.composition.get(ladTable.domainObject); - + composition.on('add', this.addItem); + composition.on('remove', this.removeItem); composition.load(); this.compositions.push({ - composition + composition, + addCallback: this.addItem, + removeCallback: this.removeItem }); }, removeLadTable(identifier) { diff --git a/src/plugins/LADTable/plugin.js b/src/plugins/LADTable/plugin.js index 8df5c07ae4..a3456872a6 100644 --- a/src/plugins/LADTable/plugin.js +++ b/src/plugins/LADTable/plugin.js @@ -39,6 +39,9 @@ export default function plugin() { cssClass: 'icon-tabular-lad', initialize(domainObject) { domainObject.composition = []; + domainObject.configuration = { + objectStyles: {} + }; } }); diff --git a/src/plugins/charts/bar/inspector/BarGraphInspectorViewProvider.js b/src/plugins/charts/bar/inspector/BarGraphInspectorViewProvider.js index b081aaa159..f30c3017a0 100644 --- a/src/plugins/charts/bar/inspector/BarGraphInspectorViewProvider.js +++ b/src/plugins/charts/bar/inspector/BarGraphInspectorViewProvider.js @@ -41,7 +41,7 @@ export default function BarGraphInspectorViewProvider(openmct) { _destroy = destroy; }, priority: function () { - return openmct.priority.HIGH + 1; + return openmct.editor.isEditing() ? openmct.priority.HIGH : openmct.priority.DEFAULT; }, destroy: function () { if (_destroy) { diff --git a/src/plugins/charts/scatter/inspector/ScatterPlotInspectorViewProvider.js b/src/plugins/charts/scatter/inspector/ScatterPlotInspectorViewProvider.js index a88ffee33a..dc11b6650e 100644 --- a/src/plugins/charts/scatter/inspector/ScatterPlotInspectorViewProvider.js +++ b/src/plugins/charts/scatter/inspector/ScatterPlotInspectorViewProvider.js @@ -40,7 +40,7 @@ export default function ScatterPlotInspectorViewProvider(openmct) { _destroy = destroy; }, priority: function () { - return openmct.priority.HIGH + 1; + return openmct.editor.isEditing() ? openmct.priority.HIGH : openmct.priority.DEFAULT; }, destroy: function () { if (_destroy) { diff --git a/src/plugins/conditionWidget/conditionWidgetStylesInterceptor.js b/src/plugins/conditionWidget/conditionWidgetStylesInterceptor.js new file mode 100644 index 0000000000..e7d3417164 --- /dev/null +++ b/src/plugins/conditionWidget/conditionWidgetStylesInterceptor.js @@ -0,0 +1,38 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2024, 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. + *****************************************************************************/ + +export default function conditionWidgetStylesInterceptor(openmct) { + return { + appliesTo: (identifier, domainObject) => { + return domainObject?.type === 'conditionWidget' && !domainObject.configuration?.objectStyles; + }, + invoke: (identifier, domainObject) => { + if (!domainObject.configuration) { + domainObject.configuration = {}; + } + + domainObject.configuration.objectStyles = {}; + + return domainObject; + } + }; +} diff --git a/src/plugins/conditionWidget/plugin.js b/src/plugins/conditionWidget/plugin.js index 0a22610f2b..74f372d8a4 100644 --- a/src/plugins/conditionWidget/plugin.js +++ b/src/plugins/conditionWidget/plugin.js @@ -20,11 +20,13 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ +import conditionWidgetStylesInterceptor from './conditionWidgetStylesInterceptor.js'; import ConditionWidgetViewProvider from './ConditionWidgetViewProvider.js'; export default function plugin() { return function install(openmct) { openmct.objectViews.addProvider(new ConditionWidgetViewProvider(openmct)); + openmct.objects.addGetInterceptor(conditionWidgetStylesInterceptor(openmct)); openmct.types.addType('conditionWidget', { key: 'conditionWidget', @@ -34,7 +36,9 @@ export default function plugin() { creatable: true, cssClass: 'icon-condition-widget', initialize(domainObject) { - domainObject.configuration = {}; + domainObject.configuration = { + objectStyles: {} + }; domainObject.label = 'Condition Widget'; domainObject.conditionalLabel = ''; domainObject.url = ''; diff --git a/src/plugins/displayLayout/AlphanumericFormatViewProvider.js b/src/plugins/displayLayout/AlphanumericFormatViewProvider.js index 0a35317a71..2d6e7f51d0 100644 --- a/src/plugins/displayLayout/AlphanumericFormatViewProvider.js +++ b/src/plugins/displayLayout/AlphanumericFormatViewProvider.js @@ -65,7 +65,9 @@ class AlphanumericFormatView { } priority() { - return 1; + return this.openmct.editor.isEditing() + ? this.openmct.priority.DEFAULT + : this.openmct.priority.LOW; } destroy() { diff --git a/src/plugins/displayLayout/DisplayLayoutType.js b/src/plugins/displayLayout/DisplayLayoutType.js index b0d28126bd..aad2b5b2ae 100644 --- a/src/plugins/displayLayout/DisplayLayoutType.js +++ b/src/plugins/displayLayout/DisplayLayoutType.js @@ -31,7 +31,8 @@ export default function DisplayLayoutType() { domainObject.composition = []; domainObject.configuration = { items: [], - layoutGrid: [10, 10] + layoutGrid: [10, 10], + objectStyles: {} }; }, form: [ diff --git a/src/plugins/displayLayout/components/AlphanumericFormat.vue b/src/plugins/displayLayout/components/AlphanumericFormat.vue index 27ab7b2848..66a6cd7f70 100644 --- a/src/plugins/displayLayout/components/AlphanumericFormat.vue +++ b/src/plugins/displayLayout/components/AlphanumericFormat.vue @@ -32,13 +32,16 @@
+
diff --git a/src/plugins/displayLayout/displayLayoutStylesInterceptor.js b/src/plugins/displayLayout/displayLayoutStylesInterceptor.js new file mode 100644 index 0000000000..b9bfb5216b --- /dev/null +++ b/src/plugins/displayLayout/displayLayoutStylesInterceptor.js @@ -0,0 +1,40 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2024, 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. + *****************************************************************************/ + +export default function displayLayoutStylesInterceptor(openmct) { + return { + appliesTo: (identifier, domainObject) => { + return domainObject?.type === 'layout'; + }, + invoke: (identifier, domainObject) => { + if (!domainObject.configuration) { + domainObject.configuration = {}; + } + + if (!domainObject.configuration.objectStyles) { + domainObject.configuration.objectStyles = {}; + } + + return domainObject; + } + }; +} diff --git a/src/plugins/displayLayout/plugin.js b/src/plugins/displayLayout/plugin.js index 67ba40bd61..a2533fb2c4 100644 --- a/src/plugins/displayLayout/plugin.js +++ b/src/plugins/displayLayout/plugin.js @@ -25,6 +25,7 @@ import mount from 'utils/mount'; import CopyToClipboardAction from './actions/CopyToClipboardAction.js'; import AlphaNumericFormatViewProvider from './AlphanumericFormatViewProvider.js'; import DisplayLayout from './components/DisplayLayout.vue'; +import displayLayoutStylesInterceptor from './displayLayoutStylesInterceptor.js'; import DisplayLayoutToolbar from './DisplayLayoutToolbar.js'; import DisplayLayoutType from './DisplayLayoutType.js'; import DisplayLayoutDrawingObjectTypes from './DrawingObjectTypes.js'; @@ -123,6 +124,7 @@ export default function DisplayLayoutPlugin(options) { return 100; } }); + openmct.objects.addGetInterceptor(displayLayoutStylesInterceptor(openmct)); openmct.types.addType('layout', DisplayLayoutType()); openmct.toolbars.addProvider(new DisplayLayoutToolbar(openmct)); openmct.inspectorViews.addProvider(new AlphaNumericFormatViewProvider(openmct, options)); diff --git a/src/plugins/faultManagement/FaultManagementInspectorViewProvider.js b/src/plugins/faultManagement/FaultManagementInspectorViewProvider.js index 4e5902925a..6f65fc8a9a 100644 --- a/src/plugins/faultManagement/FaultManagementInspectorViewProvider.js +++ b/src/plugins/faultManagement/FaultManagementInspectorViewProvider.js @@ -63,7 +63,7 @@ export default function FaultManagementInspectorViewProvider(openmct) { _destroy = destroy; }, priority: function () { - return openmct.priority.HIGH + 1; + return openmct.priority.HIGH; }, destroy: function () { if (_destroy) { diff --git a/src/plugins/filters/FiltersInspectorViewProvider.js b/src/plugins/filters/FiltersInspectorViewProvider.js index ef98a017b4..29bd53b5dd 100644 --- a/src/plugins/filters/FiltersInspectorViewProvider.js +++ b/src/plugins/filters/FiltersInspectorViewProvider.js @@ -43,8 +43,6 @@ export default class FiltersInspectorViewProvider { let openmct = this.openmct; let _destroy = null; - const domainObject = selection?.[0]?.[0]?.context?.item; - return { show: function (element) { const { destroy } = mount( @@ -69,13 +67,6 @@ export default class FiltersInspectorViewProvider { if (isEditing) { return true; } - - const metadata = openmct.telemetry.getMetadata(domainObject); - const metadataWithFilters = metadata - ? metadata.valueMetadatas.filter((value) => value.filters) - : []; - - return metadataWithFilters.length; }, priority: function () { return openmct.priority.DEFAULT; diff --git a/src/plugins/filters/components/FiltersView.vue b/src/plugins/filters/components/FiltersView.vue index c903599337..1df8b8ffbb 100644 --- a/src/plugins/filters/components/FiltersView.vue +++ b/src/plugins/filters/components/FiltersView.vue @@ -38,6 +38,9 @@ @update-filters="persistFilters" /> + + This view doesn't include any parameters that have configured filter criteria. +