mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 20:57:53 +00:00
[Tooltips] Add tooltips on hover (#6756)
* Add tooltip api, extend object api to add telemetry composition lookups, and add tooltips to gauges/notebook embeds/plot legends/object frames/object names/time strips/recent objects/search results/object tree * Add tooltips to telemetry/lad tables * Styling normalization, sanding and polishing. * Add tooltips for Conditional widgets and Tab Views * Add tests * Switch to using enum-ish consts for tooltip locations * Trim LAD table row name to account for spacing required by linting rules --------- Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
This commit is contained in:
parent
795d7a7ec7
commit
cde8fbbb0d
@ -401,14 +401,7 @@ async function setEndOffset(page, offset) {
|
||||
async function selectInspectorTab(page, name) {
|
||||
const inspectorTabs = page.getByRole('tablist');
|
||||
const inspectorTab = inspectorTabs.getByTitle(name);
|
||||
const inspectorTabClass = await inspectorTab.getAttribute('class');
|
||||
const isSelectedInspectorTab = inspectorTabClass.includes('is-current');
|
||||
|
||||
// do not click a tab that is already selected or it will timeout your test
|
||||
// do to a { pointer-events: none; } on selected tabs
|
||||
if (!isSelectedInspectorTab) {
|
||||
await inspectorTab.click();
|
||||
}
|
||||
await inspectorTab.click();
|
||||
}
|
||||
|
||||
/**
|
||||
|
398
e2e/tests/functional/tooltips.e2e.spec.js
Normal file
398
e2e/tests/functional/tooltips.e2e.spec.js
Normal file
@ -0,0 +1,398 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is dedicated to tests which can quickly verify that any openmct installation is
|
||||
operable and that any type of testing can proceed.
|
||||
|
||||
Ideally, smoke tests should make zero assumptions about how and where they are run. This makes them
|
||||
more resilient to change and therefor a better indicator of failure. Smoke tests will also run quickly
|
||||
as they cover a very "thin surface" of functionality.
|
||||
|
||||
When deciding between authoring new smoke tests or functional tests, ask yourself "would I feel
|
||||
comfortable running this test during a live mission?" Avoid creating or deleting Domain Objects.
|
||||
Make no assumptions about the order that elements appear in the DOM.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('../../pluginFixtures');
|
||||
const { createDomainObjectWithDefaults, expandEntireTree } = require('../../appActions');
|
||||
|
||||
test.describe('Verify tooltips', () => {
|
||||
let folder1;
|
||||
let folder2;
|
||||
let folder3;
|
||||
let sineWaveObject1;
|
||||
let sineWaveObject2;
|
||||
let sineWaveObject3;
|
||||
|
||||
const swg1Path = 'My Items / Folder Foo / SWG 1';
|
||||
const swg2Path = 'My Items / Folder Foo / Folder Bar / SWG 2';
|
||||
const swg3Path = 'My Items / Folder Foo / Folder Bar / Folder Baz / SWG 3';
|
||||
|
||||
test.beforeEach(async ({ page, openmctConfig }) => {
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
folder1 = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Folder',
|
||||
name: 'Folder Foo'
|
||||
});
|
||||
folder2 = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Folder',
|
||||
name: 'Folder Bar',
|
||||
parent: folder1.uuid
|
||||
});
|
||||
folder3 = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Folder',
|
||||
name: 'Folder Baz',
|
||||
parent: folder2.uuid
|
||||
});
|
||||
// Create Sine Wave Generator
|
||||
sineWaveObject1 = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
name: 'SWG 1',
|
||||
parent: folder1.uuid
|
||||
});
|
||||
sineWaveObject1.path = swg1Path;
|
||||
sineWaveObject2 = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
name: 'SWG 2',
|
||||
parent: folder2.uuid
|
||||
});
|
||||
sineWaveObject2.path = swg2Path;
|
||||
sineWaveObject3 = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
name: 'SWG 3',
|
||||
parent: folder3.uuid
|
||||
});
|
||||
sineWaveObject3.path = swg3Path;
|
||||
|
||||
// Expand all folders
|
||||
await expandEntireTree(page);
|
||||
});
|
||||
|
||||
// LAD Tables - DONE
|
||||
// Expanded collapsed plot legend - DONE
|
||||
// Object Labels - DONE
|
||||
// Display Layout headers - DONE
|
||||
// Flexible Layout headers - DONE
|
||||
// Tab View layout headers - DONE
|
||||
// Search - DONE
|
||||
// Gauge -
|
||||
// Notebook Embed - DONE
|
||||
// Telemetry Table -
|
||||
// Timeline Objects
|
||||
// Tree - DONE
|
||||
// Recent Objects
|
||||
|
||||
test('display correct paths for LAD tables', async ({ page, openmctConfig }) => {
|
||||
// Create LAD table
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'LAD Table',
|
||||
name: 'Test LAD Table'
|
||||
});
|
||||
// Edit LAD table
|
||||
await page.locator('[title="Edit"]').click();
|
||||
|
||||
// Add the Sine Wave Generator to the LAD table and save changes
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-lad-table-wrapper');
|
||||
await page.dragAndDrop(`text=${sineWaveObject2.name}`, '.c-lad-table-wrapper');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-lad-table-wrapper');
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.locator('text=Save and Finish Editing').click();
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
|
||||
async function getToolTip(object) {
|
||||
await page.locator('.c-create-button').hover();
|
||||
await page.getByRole('cell', { name: object.name }).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
return tooltipText.replace('\n', '').trim();
|
||||
}
|
||||
|
||||
expect(await getToolTip(sineWaveObject1)).toBe(sineWaveObject1.path);
|
||||
expect(await getToolTip(sineWaveObject2)).toBe(sineWaveObject2.path);
|
||||
expect(await getToolTip(sineWaveObject3)).toBe(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths for expanded and collapsed plot legend items', async ({ page }) => {
|
||||
// Create Overlay Plot
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Overlay Plot',
|
||||
name: 'Test Overlay Plots'
|
||||
});
|
||||
// Edit Overlay Plot
|
||||
await page.locator('[title="Edit"]').click();
|
||||
|
||||
// Add the Sine Wave Generator to the LAD table and save changes
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.gl-plot');
|
||||
await page.dragAndDrop(`text=${sineWaveObject2.name}`, '.gl-plot');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.gl-plot');
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.locator('text=Save and Finish Editing').click();
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
|
||||
async function getCollapsedLegendToolTip(object) {
|
||||
await page.locator('.c-create-button').hover();
|
||||
await page
|
||||
.locator('.plot-series-name', { has: page.locator(`text="${object.name} Hz"`) })
|
||||
.hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
return tooltipText.replace('\n', '').trim();
|
||||
}
|
||||
|
||||
async function getExpandedLegendToolTip(object) {
|
||||
await page.locator('.c-create-button').hover();
|
||||
await page
|
||||
.locator('.plot-series-name', { has: page.locator(`text="${object.name}"`) })
|
||||
.hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
return tooltipText.replace('\n', '').trim();
|
||||
}
|
||||
|
||||
expect(await getCollapsedLegendToolTip(sineWaveObject1)).toBe(sineWaveObject1.path);
|
||||
expect(await getCollapsedLegendToolTip(sineWaveObject2)).toBe(sineWaveObject2.path);
|
||||
expect(await getCollapsedLegendToolTip(sineWaveObject3)).toBe(sineWaveObject3.path);
|
||||
|
||||
await page.keyboard.up('Control');
|
||||
await page.locator('.gl-plot-legend__view-control.c-disclosure-triangle').click();
|
||||
await page.keyboard.down('Control');
|
||||
|
||||
expect(await getExpandedLegendToolTip(sineWaveObject1)).toBe(sineWaveObject1.path);
|
||||
expect(await getExpandedLegendToolTip(sineWaveObject2)).toBe(sineWaveObject2.path);
|
||||
expect(await getExpandedLegendToolTip(sineWaveObject3)).toBe(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering over object labels', async ({ page }) => {
|
||||
async function getObjectLabelTooltip(object) {
|
||||
await page
|
||||
.locator('.c-tree__item__name.c-object-label__name', {
|
||||
has: page.locator(`text="${object.name}"`)
|
||||
})
|
||||
.click();
|
||||
await page.keyboard.down('Control');
|
||||
await page
|
||||
.locator('.l-browse-bar__object-name.c-object-label__name', {
|
||||
has: page.locator(`text="${object.name}"`)
|
||||
})
|
||||
.hover();
|
||||
const tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
await page.keyboard.up('Control');
|
||||
return tooltipText.replace('\n', '').trim();
|
||||
}
|
||||
|
||||
expect(await getObjectLabelTooltip(sineWaveObject1)).toBe(sineWaveObject1.path);
|
||||
expect(await getObjectLabelTooltip(sineWaveObject3)).toBe(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering over display layout pane headers', async ({ page }) => {
|
||||
// Create Overlay Plot
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Overlay Plot',
|
||||
name: 'Test Overlay Plot'
|
||||
});
|
||||
// Edit Overlay Plot
|
||||
await page.locator('[title="Edit"]').click();
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.gl-plot');
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.locator('text=Save and Finish Editing').click();
|
||||
|
||||
// Create Stacked Plot
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Stacked Plot',
|
||||
name: 'Test Stacked Plot'
|
||||
});
|
||||
// Edit Stacked Plot
|
||||
await page.locator('[title="Edit"]').click();
|
||||
await page.dragAndDrop(`text=${sineWaveObject2.name}`, '.c-plot--stacked.holder');
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.locator('text=Save and Finish Editing').click();
|
||||
|
||||
// Create Display Layout
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Display Layout',
|
||||
name: 'Test Display Layout'
|
||||
});
|
||||
// Edit Display Layout
|
||||
await page.locator('[title="Edit"]').click();
|
||||
|
||||
await page.dragAndDrop("text='Test Overlay Plot'", '.l-layout__grid-holder', {
|
||||
targetPosition: { x: 0, y: 0 }
|
||||
});
|
||||
await page.dragAndDrop("text='Test Stacked Plot'", '.l-layout__grid-holder', {
|
||||
targetPosition: { x: 0, y: 250 }
|
||||
});
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.l-layout__grid-holder', {
|
||||
targetPosition: { x: 500, y: 200 }
|
||||
});
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.locator('text=Save and Finish Editing').click();
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
|
||||
await page.getByText('Test Overlay Plot').nth(2).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe('My Items / Test Overlay Plot');
|
||||
|
||||
// await page.keyboard.up('Control');
|
||||
// await page.locator('.c-plot-legend__view-control >> nth=0').click();
|
||||
// await page.keyboard.down('Control');
|
||||
// await page.locator('.plot-wrapper-expanded-legend .plot-series-name').first().hover();
|
||||
// tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
// tooltipText = tooltipText.replace('\n', '').trim();
|
||||
// expect(tooltipText).toBe(sineWaveObject1.path);
|
||||
|
||||
await page.getByText('Test Stacked Plot').nth(2).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe('My Items / Test Stacked Plot');
|
||||
|
||||
await page.getByText('SWG 3').nth(2).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(sineWaveObject3.path).toBe(tooltipText);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering over flexible object labels', async ({ page }) => {
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Flexible Layout',
|
||||
name: 'Test Flexible Layout'
|
||||
});
|
||||
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-fl__container >> nth=0');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-fl__container >> nth=1');
|
||||
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.locator('text=Save and Finish Editing').click();
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
await page.getByText('SWG 1').nth(2).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
||||
|
||||
await page.getByText('SWG 3').nth(2).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering over tab view labels', async ({ page }) => {
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Tabs View',
|
||||
name: 'Test Tabs View'
|
||||
});
|
||||
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-tabs-view__tabs-holder');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-tabs-view__tabs-holder');
|
||||
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.locator('text=Save and Finish Editing').click();
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
await page.getByText('SWG 1').nth(2).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
||||
|
||||
await page.getByText('SWG 3').nth(2).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering tree items', async ({ page }) => {
|
||||
await page.keyboard.down('Control');
|
||||
await page.getByText('SWG 1').nth(0).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
||||
|
||||
await page.getByText('SWG 3').nth(0).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering search items', async ({ page }) => {
|
||||
await page.getByRole('searchbox', { name: 'Search Input' }).click();
|
||||
await page.fill('.c-search__input', 'SWG 3');
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
await page.locator('.c-gsearch-result__title').hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display path for source telemetry when hovering over gauge', ({ page }) => {
|
||||
expect(true).toBe(true);
|
||||
// await createDomainObjectWithDefaults(page, {
|
||||
// type: 'Gauge',
|
||||
// name: 'Test Gauge'
|
||||
// });
|
||||
// await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-gauge__wrapper');
|
||||
// await page.keyboard.down('Control');
|
||||
// await page.locator('.c-gauge__current-value-text-wrapper').hover();
|
||||
// let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
// tooltipText = tooltipText.replace('\n', '').trim();
|
||||
// expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display tooltip path for notebook embeds', async ({ page }) => {
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Notebook',
|
||||
name: 'Test Notebook'
|
||||
});
|
||||
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-notebook__drag-area');
|
||||
await page.keyboard.down('Control');
|
||||
await page.locator('.c-ne__embed').hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
// test('display tooltip path for telemetry table names', async ({ page }) => {
|
||||
// await setEndOffset(page, { secs: '10' });
|
||||
// await createDomainObjectWithDefaults(page, {
|
||||
// type: 'Telemetry Table',
|
||||
// name: 'Test Telemetry Table'
|
||||
// });
|
||||
|
||||
// await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-telemetry-table');
|
||||
// await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-telemetry-table');
|
||||
|
||||
// await page.locator('button[title="Save"]').click();
|
||||
// await page.locator('text=Save and Finish Editing').click();
|
||||
|
||||
// // .c-telemetry-table__body
|
||||
|
||||
// await page.keyboard.down('Control');
|
||||
|
||||
// await page.locator('.noselect > [title="SWG 3"]').first().hover();
|
||||
// let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
// tooltipText = tooltipText.replace('\n', '').trim();
|
||||
// expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
// });
|
||||
});
|
@ -56,6 +56,7 @@ if (document.currentScript) {
|
||||
* @property {import('./src/api/notifications/NotificationAPI').default} notifications
|
||||
* @property {import('./src/api/Editor').default} editor
|
||||
* @property {import('./src/api/overlays/OverlayAPI')} overlays
|
||||
* @property {import('./src/api/tooltips/ToolTipAPI')} tooltips
|
||||
* @property {import('./src/api/menu/MenuAPI').default} menus
|
||||
* @property {import('./src/api/actions/ActionsAPI').default} actions
|
||||
* @property {import('./src/api/status/StatusAPI').default} status
|
||||
|
@ -24,6 +24,7 @@ define([
|
||||
'EventEmitter',
|
||||
'./api/api',
|
||||
'./api/overlays/OverlayAPI',
|
||||
'./api/tooltips/ToolTipAPI',
|
||||
'./selection/Selection',
|
||||
'./plugins/plugins',
|
||||
'./ui/registries/ViewRegistry',
|
||||
@ -48,6 +49,7 @@ define([
|
||||
EventEmitter,
|
||||
api,
|
||||
OverlayAPI,
|
||||
ToolTipAPI,
|
||||
Selection,
|
||||
plugins,
|
||||
ViewRegistry,
|
||||
@ -220,6 +222,8 @@ define([
|
||||
|
||||
['overlays', () => new OverlayAPI.default()],
|
||||
|
||||
['tooltips', () => new ToolTipAPI.default()],
|
||||
|
||||
['menus', () => new api.MenuAPI(this)],
|
||||
|
||||
['actions', () => new api.ActionsAPI(this)],
|
||||
|
@ -540,6 +540,40 @@ export default class ObjectAPI {
|
||||
.join('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return path of telemetry objects in the object composition
|
||||
* @param {object} identifier the identifier for the domain object to query for
|
||||
* @param {object} [telemetryIdentifier] the specific identifier for the telemetry
|
||||
* to look for in the composition, uses first object in composition otherwise
|
||||
* @returns {Array} path of telemetry object in object composition
|
||||
*/
|
||||
async getTelemetryPath(identifier, telemetryIdentifier) {
|
||||
const objectDetails = await this.get(identifier);
|
||||
const telemetryPath = [];
|
||||
if (objectDetails.composition && !['folder'].includes(objectDetails.type)) {
|
||||
let sourceTelemetry = objectDetails.composition[0];
|
||||
if (telemetryIdentifier) {
|
||||
sourceTelemetry = objectDetails.composition.find(
|
||||
(telemetrySource) =>
|
||||
this.makeKeyString(telemetrySource) === this.makeKeyString(telemetryIdentifier)
|
||||
);
|
||||
}
|
||||
const compositionElement = await this.get(sourceTelemetry);
|
||||
if (!['yamcs.telemetry', 'generator'].includes(compositionElement.type)) {
|
||||
return telemetryPath;
|
||||
}
|
||||
const telemetryKey = compositionElement.identifier.key;
|
||||
const telemetryPathObjects = await this.getOriginalPath(telemetryKey);
|
||||
telemetryPathObjects.forEach((pathObject) => {
|
||||
if (pathObject.type === 'root') {
|
||||
return;
|
||||
}
|
||||
telemetryPath.unshift(pathObject.name);
|
||||
});
|
||||
}
|
||||
return telemetryPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a domain object. Internal to ObjectAPI, won't call save after.
|
||||
* @private
|
||||
|
73
src/api/tooltips/ToolTip.js
Normal file
73
src/api/tooltips/ToolTip.js
Normal file
@ -0,0 +1,73 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2023, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import TooltipComponent from './components/TooltipComponent.vue';
|
||||
import EventEmitter from 'EventEmitter';
|
||||
import Vue from 'vue';
|
||||
|
||||
class Tooltip extends EventEmitter {
|
||||
constructor(
|
||||
{ toolTipText, toolTipLocation, parentElement } = {
|
||||
tooltipText: '',
|
||||
toolTipLocation: 'below',
|
||||
parentElement: null
|
||||
}
|
||||
) {
|
||||
super();
|
||||
|
||||
this.container = document.createElement('div');
|
||||
|
||||
this.component = new Vue({
|
||||
components: {
|
||||
TooltipComponent: TooltipComponent
|
||||
},
|
||||
provide: {
|
||||
toolTipText,
|
||||
toolTipLocation,
|
||||
parentElement
|
||||
},
|
||||
template: '<tooltip-component toolTipText="toolTipText"></tooltip-component>'
|
||||
});
|
||||
|
||||
this.isActive = null;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (!this.isActive) {
|
||||
return;
|
||||
}
|
||||
document.body.removeChild(this.container);
|
||||
this.component.$destroy();
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
**/
|
||||
show() {
|
||||
document.body.appendChild(this.container);
|
||||
this.container.appendChild(this.component.$mount().$el);
|
||||
this.isActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
export default Tooltip;
|
90
src/api/tooltips/ToolTipAPI.js
Normal file
90
src/api/tooltips/ToolTipAPI.js
Normal file
@ -0,0 +1,90 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2023, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import Tooltip from './ToolTip';
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @enum {String} TooltipLocation
|
||||
* @property {String} ABOVE The string for locating tooltips above an element
|
||||
* @property {String} BELOW The string for locating tooltips below an element
|
||||
* @property {String} RIGHT The pixel-spatial annotation type
|
||||
* @property {String} LEFT The temporal annotation type
|
||||
* @property {String} CENTER The plot-spatial annotation type
|
||||
*/
|
||||
const TOOLTIP_LOCATIONS = Object.freeze({
|
||||
ABOVE: 'above',
|
||||
BELOW: 'below',
|
||||
RIGHT: 'right',
|
||||
LEFT: 'left',
|
||||
CENTER: 'center'
|
||||
});
|
||||
|
||||
/**
|
||||
* The TooltipAPI is responsible for adding custom tooltips to
|
||||
* the desired elements on the screen
|
||||
*
|
||||
* @memberof api/tooltips
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
class TooltipAPI {
|
||||
constructor() {
|
||||
this.activeToolTips = [];
|
||||
this.TOOLTIP_LOCATIONS = TOOLTIP_LOCATIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private for platform-internal use
|
||||
*/
|
||||
showTooltip(tooltip) {
|
||||
for (let i = this.activeToolTips.length - 1; i > -1; i--) {
|
||||
this.activeToolTips[i].destroy();
|
||||
this.activeToolTips.splice(i, 1);
|
||||
}
|
||||
this.activeToolTips.push(tooltip);
|
||||
|
||||
tooltip.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* A description of option properties that can be passed into the tooltip
|
||||
* @typedef {Object} TooltipOptions
|
||||
* @property {string} tooltipText text to show in the tooltip
|
||||
* @property {TOOLTIP_LOCATIONS} tooltipLocation location to show the tooltip relative to the parentElement
|
||||
* @property {HTMLElement} parentElement reference to the DOM node we're adding the tooltip to
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tooltips take an options object that consists of the string, tooltipLocation, and parentElement
|
||||
* @param {TooltipOptions} options
|
||||
*/
|
||||
tooltip(options) {
|
||||
let tooltip = new Tooltip(options);
|
||||
|
||||
this.showTooltip(tooltip);
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
export default TooltipAPI;
|
61
src/api/tooltips/components/TooltipComponent.vue
Normal file
61
src/api/tooltips/components/TooltipComponent.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<!--
|
||||
Open MCT, Copyright (c) 2014-2023, 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.
|
||||
-->
|
||||
<template>
|
||||
<div ref="tooltip-wrapper" class="c-menu c-tooltip-wrapper" :style="toolTipLocationStyle">
|
||||
<div class="c-tooltip">
|
||||
{{ toolTipText }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['toolTipText', 'toolTipLocation', 'parentElement'],
|
||||
computed: {
|
||||
toolTipCoordinates() {
|
||||
return this.parentElement.getBoundingClientRect();
|
||||
},
|
||||
toolTipLocationStyle() {
|
||||
const { top, left, height, width } = this.toolTipCoordinates;
|
||||
let toolTipLocationStyle = {};
|
||||
|
||||
if (this.toolTipLocation === 'above') {
|
||||
toolTipLocationStyle = { top: `${top - 5}px`, left: `${left}px` };
|
||||
}
|
||||
if (this.toolTipLocation === 'below') {
|
||||
toolTipLocationStyle = { top: `${top + height}px`, left: `${left}px` };
|
||||
}
|
||||
if (this.toolTipLocation === 'right') {
|
||||
toolTipLocationStyle = { top: `${top}px`, left: `${left + width}px` };
|
||||
}
|
||||
if (this.toolTipLocation === 'left') {
|
||||
toolTipLocationStyle = { top: `${top}px`, left: `${left - width}px` };
|
||||
}
|
||||
if (this.toolTipLocation === 'center') {
|
||||
toolTipLocationStyle = { top: `${top + height / 2}px`, left: `${left + width / 2}px` };
|
||||
}
|
||||
|
||||
return toolTipLocationStyle;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
8
src/api/tooltips/components/tooltip-component.scss
Normal file
8
src/api/tooltips/components/tooltip-component.scss
Normal file
@ -0,0 +1,8 @@
|
||||
.c-tooltip-wrapper {
|
||||
max-width: 200px;
|
||||
padding: $interiorMargin;
|
||||
}
|
||||
|
||||
.c-tooltip {
|
||||
font-style: italic;
|
||||
}
|
72
src/api/tooltips/tooltipMixins.js
Normal file
72
src/api/tooltips/tooltipMixins.js
Normal file
@ -0,0 +1,72 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2023, 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 tooltipHelpers = {
|
||||
methods: {
|
||||
async getTelemetryPathString(telemetryIdentifier) {
|
||||
let telemetryPathString = '';
|
||||
if (!this.domainObject?.identifier) {
|
||||
return;
|
||||
}
|
||||
const telemetryPath = await this.openmct.objects.getTelemetryPath(
|
||||
this.domainObject.identifier,
|
||||
telemetryIdentifier
|
||||
);
|
||||
if (telemetryPath.length) {
|
||||
telemetryPathString = telemetryPath.join(' / ');
|
||||
}
|
||||
return telemetryPathString;
|
||||
},
|
||||
async getObjectPath(objectIdentifier) {
|
||||
if (!objectIdentifier && !this.domainObject) {
|
||||
return;
|
||||
}
|
||||
const domainObjectIdentifier = objectIdentifier || this.domainObject.identifier;
|
||||
const objectPathList = await this.openmct.objects.getOriginalPath(domainObjectIdentifier);
|
||||
objectPathList.pop();
|
||||
return objectPathList
|
||||
.map((pathItem) => pathItem.name)
|
||||
.reverse()
|
||||
.join(' / ');
|
||||
},
|
||||
buildToolTip(tooltipText, tooltipLocation, elementRef) {
|
||||
if (!tooltipText || tooltipText.length < 1) {
|
||||
return;
|
||||
}
|
||||
let parentElement = this.$refs[elementRef];
|
||||
if (Array.isArray(parentElement)) {
|
||||
parentElement = parentElement[0];
|
||||
}
|
||||
this.tooltip = this.openmct.tooltips.tooltip({
|
||||
toolTipText: tooltipText,
|
||||
toolTipLocation: tooltipLocation,
|
||||
parentElement: parentElement
|
||||
});
|
||||
},
|
||||
hideToolTip() {
|
||||
this.tooltip?.destroy();
|
||||
this.tooltip = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default tooltipHelpers;
|
@ -26,7 +26,14 @@
|
||||
@click="clickedRow"
|
||||
@contextmenu.prevent="showContextMenu"
|
||||
>
|
||||
<td class="js-first-data">{{ domainObject.name }}</td>
|
||||
<td
|
||||
ref="tableCell"
|
||||
class="js-first-data"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
{{ domainObject.name }}
|
||||
</td>
|
||||
<td v-if="showTimestamp" class="js-second-data">{{ formattedTimestamp }}</td>
|
||||
<td class="js-third-data" :class="valueClasses">{{ value }}</td>
|
||||
<td v-if="hasUnits" class="js-units">
|
||||
@ -42,8 +49,10 @@ const BLANK_VALUE = '---';
|
||||
|
||||
import identifierToString from '/src/tools/url';
|
||||
import PreviewAction from '@/ui/preview/PreviewAction.js';
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
|
||||
export default {
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct', 'currentView'],
|
||||
props: {
|
||||
domainObject: {
|
||||
@ -259,6 +268,10 @@ export default {
|
||||
return metadata
|
||||
.values()
|
||||
.find((metadatum) => metadatum.hints.domain === undefined && metadatum.key !== 'name');
|
||||
},
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getObjectPath(), BELOW, 'tableCell');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -264,7 +264,7 @@ describe('The LAD Table', () => {
|
||||
});
|
||||
|
||||
it('should show the name provided for the the telemetry producing object', () => {
|
||||
const rowName = parent.querySelector(TABLE_BODY_FIRST_ROW_FIRST_DATA).innerText;
|
||||
const rowName = parent.querySelector(TABLE_BODY_FIRST_ROW_FIRST_DATA).innerText.trim();
|
||||
|
||||
const expectedName = mockObj.telemetry.name;
|
||||
expect(rowName).toBe(expectedName);
|
||||
|
@ -21,7 +21,12 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div ref="conditionWidgetElement" class="c-condition-widget u-style-receiver js-style-receiver">
|
||||
<div
|
||||
ref="conditionWidgetElement"
|
||||
class="c-condition-widget u-style-receiver js-style-receiver"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
<component :is="urlDefined ? 'a' : 'div'" class="c-condition-widget__label-wrapper" :href="url">
|
||||
<div class="c-condition-widget__label">{{ label }}</div>
|
||||
</component>
|
||||
@ -29,9 +34,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
const sanitizeUrl = require('@braintree/sanitize-url').sanitizeUrl;
|
||||
|
||||
export default {
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct', 'domainObject'],
|
||||
data: function () {
|
||||
return {
|
||||
@ -116,6 +123,10 @@ export default {
|
||||
}
|
||||
|
||||
this.conditionalLabel = latestDatum.output || '';
|
||||
},
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getObjectPath(), BELOW, 'conditionWidgetElement');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -23,7 +23,6 @@
|
||||
<layout-frame
|
||||
:item="item"
|
||||
:grid-size="gridSize"
|
||||
:title="domainObject && domainObject.name"
|
||||
:is-editing="isEditing"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')"
|
||||
|
@ -30,12 +30,15 @@
|
||||
>
|
||||
<div
|
||||
v-if="domainObject"
|
||||
ref="telemetryViewWrapper"
|
||||
class="c-telemetry-view u-style-receiver"
|
||||
:class="[itemClasses]"
|
||||
:style="styleObject"
|
||||
:data-font-size="item.fontSize"
|
||||
:data-font="item.font"
|
||||
@contextmenu.prevent="showContextMenu"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
<div class="is-status__indicator" :title="`This item is ${status}`"></div>
|
||||
<div v-if="showLabel" class="c-telemetry-view__label">
|
||||
@ -69,6 +72,7 @@ import {
|
||||
getDefaultNotebook,
|
||||
getNotebookSectionAndPage
|
||||
} from '@/plugins/notebook/utils/notebook-storage.js';
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
|
||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5];
|
||||
const DEFAULT_POSITION = [1, 1];
|
||||
@ -97,7 +101,7 @@ export default {
|
||||
components: {
|
||||
LayoutFrame
|
||||
},
|
||||
mixins: [conditionalStylesMixin, stalenessMixin],
|
||||
mixins: [conditionalStylesMixin, stalenessMixin, tooltipHelpers],
|
||||
inject: ['openmct', 'objectPath', 'currentView'],
|
||||
props: {
|
||||
item: {
|
||||
@ -379,6 +383,10 @@ export default {
|
||||
},
|
||||
setStatus(status) {
|
||||
this.status = status;
|
||||
},
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getObjectPath(), BELOW, 'telemetryViewWrapper');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -22,7 +22,13 @@
|
||||
<template>
|
||||
<div class="c-gauge__wrapper js-gauge-wrapper" :class="gaugeClasses" :title="gaugeTitle">
|
||||
<template v-if="typeDial">
|
||||
<svg class="c-gauge c-dial" viewBox="0 0 10 10">
|
||||
<svg
|
||||
ref="gauge"
|
||||
class="c-gauge c-dial"
|
||||
viewBox="0 0 10 10"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
<g class="c-dial__masks">
|
||||
<mask id="gaugeValueMask">
|
||||
<path
|
||||
@ -325,13 +331,14 @@
|
||||
<script>
|
||||
import { DIAL_VALUE_DEG_OFFSET, getLimitDegree } from '../gauge-limit-util';
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
|
||||
const LIMIT_PADDING_IN_PERCENT = 10;
|
||||
const DEFAULT_CURRENT_VALUE = '--';
|
||||
|
||||
export default {
|
||||
name: 'Gauge',
|
||||
mixins: [stalenessMixin],
|
||||
mixins: [stalenessMixin, tooltipHelpers],
|
||||
inject: ['openmct', 'domainObject', 'composition'],
|
||||
data() {
|
||||
let gaugeController = this.domainObject.configuration.gaugeController;
|
||||
@ -730,6 +737,10 @@ export default {
|
||||
},
|
||||
valToPercentMeter(vValue) {
|
||||
return this.round(((this.rangeHigh - vValue) / (this.rangeHigh - this.rangeLow)) * 100, 2);
|
||||
},
|
||||
async showToolTip() {
|
||||
const { CENTER } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getTelemetryPathString(), CENTER, 'gauge');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -20,7 +20,12 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<template>
|
||||
<div class="c-snapshot c-ne__embed">
|
||||
<div
|
||||
ref="notebookEmbed"
|
||||
class="c-snapshot c-ne__embed"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
<div v-if="embed.snapshot" class="c-ne__embed__snap-thumb" @click="openSnapshot()">
|
||||
<img :src="thumbnailImage" />
|
||||
</div>
|
||||
@ -49,6 +54,7 @@ import RemoveDialog from '../utils/removeDialog';
|
||||
import PainterroInstance from '../utils/painterroInstance';
|
||||
import SnapshotTemplate from './snapshot-template.html';
|
||||
import objectPathToUrl from '@/tools/url';
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
|
||||
import { updateNotebookImageDomainObject } from '../utils/notebook-image';
|
||||
import ImageExporter from '../../../exporters/ImageExporter';
|
||||
@ -56,6 +62,7 @@ import ImageExporter from '../../../exporters/ImageExporter';
|
||||
import Vue from 'vue';
|
||||
|
||||
export default {
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct', 'snapshotContainer'],
|
||||
props: {
|
||||
embed: {
|
||||
@ -404,6 +411,14 @@ export default {
|
||||
snapshotObject.fullSizeImage
|
||||
);
|
||||
}
|
||||
},
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(
|
||||
await this.getObjectPath(this.embed.domainObject.identifier),
|
||||
BELOW,
|
||||
'notebookEmbed'
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -29,7 +29,12 @@
|
||||
@mouseover="toggleHover(true)"
|
||||
@mouseleave="toggleHover(false)"
|
||||
>
|
||||
<div class="plot-series-swatch-and-name">
|
||||
<div
|
||||
ref="series"
|
||||
class="plot-series-swatch-and-name"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
<span class="plot-series-color-swatch" :style="{ 'background-color': colorAsHexString }">
|
||||
</span>
|
||||
<span class="is-status__indicator" title="This item is missing or suspect"></span>
|
||||
@ -59,9 +64,10 @@ import { getLimitClass } from '@/plugins/plot/chart/limitUtil';
|
||||
import eventHelpers from '../lib/eventHelpers';
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
import configStore from '../configuration/ConfigStore';
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
|
||||
export default {
|
||||
mixins: [stalenessMixin],
|
||||
mixins: [stalenessMixin, tooltipHelpers],
|
||||
inject: ['openmct', 'domainObject'],
|
||||
props: {
|
||||
seriesObject: {
|
||||
@ -189,6 +195,14 @@ export default {
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
},
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(
|
||||
await this.getTelemetryPathString(this.seriesObject.domainObject.identifier),
|
||||
BELOW,
|
||||
'series'
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -33,7 +33,14 @@
|
||||
<span class="plot-series-color-swatch" :style="{ 'background-color': colorAsHexString }">
|
||||
</span>
|
||||
<span class="is-status__indicator" title="This item is missing or suspect"></span>
|
||||
<span class="plot-series-name">{{ name }}</span>
|
||||
<span
|
||||
ref="seriesName"
|
||||
class="plot-series-name"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
{{ name }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td v-if="showTimestampWhenExpanded">
|
||||
@ -72,9 +79,10 @@ import { getLimitClass } from '@/plugins/plot/chart/limitUtil';
|
||||
import eventHelpers from '@/plugins/plot/lib/eventHelpers';
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
import configStore from '../configuration/ConfigStore';
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
|
||||
export default {
|
||||
mixins: [stalenessMixin],
|
||||
mixins: [stalenessMixin, tooltipHelpers],
|
||||
inject: ['openmct', 'domainObject'],
|
||||
props: {
|
||||
seriesObject: {
|
||||
@ -205,6 +213,14 @@ export default {
|
||||
this.$emit('legendHoverChanged', {
|
||||
seriesKey: this.hover ? this.seriesObject.keyString : ''
|
||||
});
|
||||
},
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(
|
||||
await this.getTelemetryPathString(this.seriesObject.domainObject.identifier),
|
||||
BELOW,
|
||||
'seriesName'
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -35,12 +35,15 @@
|
||||
</div>
|
||||
<div
|
||||
v-for="(tab, index) in tabsList"
|
||||
:ref="tab.keyString"
|
||||
:key="tab.keyString"
|
||||
class="c-tab c-tabs-view__tab js-tab"
|
||||
:class="{
|
||||
'is-current': isCurrent(tab)
|
||||
}"
|
||||
@click="showTab(tab, index)"
|
||||
@mouseover.ctrl="showToolTip(tab)"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
<div
|
||||
ref="tabsLabel"
|
||||
@ -79,6 +82,7 @@
|
||||
<script>
|
||||
import ObjectView from '../../../ui/components/ObjectView.vue';
|
||||
import RemoveAction from '../../remove/RemoveAction.js';
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
import _ from 'lodash';
|
||||
|
||||
const unknownObjectType = {
|
||||
@ -92,6 +96,7 @@ export default {
|
||||
components: {
|
||||
ObjectView
|
||||
},
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct', 'domainObject', 'composition', 'objectPath'],
|
||||
props: {
|
||||
isEditing: {
|
||||
@ -389,6 +394,11 @@ export default {
|
||||
|
||||
this.tabWidth = this.$refs.tabs.offsetWidth + 'px';
|
||||
this.tabHeight = this.$refs.tabsHolder.offsetHeight - this.$refs.tabs.offsetHeight + 'px';
|
||||
},
|
||||
async showToolTip(tab) {
|
||||
const identifier = tab.domainObject.identifier;
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getObjectPath(identifier), BELOW, tab.keyString);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -20,13 +20,22 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<template>
|
||||
<td :title="formattedValue" @click="selectCell($event.currentTarget, columnKey)">
|
||||
<td
|
||||
ref="tableCell"
|
||||
:title="formattedValue"
|
||||
@click="selectCell($event.currentTarget, columnKey)"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
{{ formattedValue }}
|
||||
</td>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
|
||||
export default {
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
row: {
|
||||
@ -76,6 +85,13 @@ export default {
|
||||
);
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
async showToolTip() {
|
||||
if (this.columnKey !== 'name') {
|
||||
return;
|
||||
}
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getObjectPath(this.row.objectKeyString), BELOW, 'tableCell');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -27,6 +27,7 @@
|
||||
:min-height="item.height"
|
||||
:show-ucontents="isPlanLikeObject(item.domainObject)"
|
||||
:span-rows-count="item.rowCount"
|
||||
:domain-object="item.domainObject"
|
||||
>
|
||||
<template #label>
|
||||
{{ item.domainObject.name }}
|
||||
|
@ -578,7 +578,6 @@ select {
|
||||
&.is-current {
|
||||
background: $colorTabCurrentBg;
|
||||
color: $colorTabCurrentFg;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
@import '../api/overlays/components/dialog-component.scss';
|
||||
@import '../api/overlays/components/overlay-component.scss';
|
||||
@import '../api/tooltips/components/tooltip-component.scss';
|
||||
@import '../plugins/condition/components/conditionals.scss';
|
||||
@import '../plugins/conditionWidget/components/condition-widget.scss';
|
||||
@import '../plugins/condition/components/inspector/conditional-styles.scss';
|
||||
|
@ -38,7 +38,12 @@
|
||||
<div class="c-object-label__type-icon" :class="cssClass">
|
||||
<span class="is-status__indicator" :title="`This item is ${status}`"></span>
|
||||
</div>
|
||||
<div class="c-object-label__name">
|
||||
<div
|
||||
ref="objectName"
|
||||
class="c-object-label__name"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
{{ domainObject && domainObject.name }}
|
||||
</div>
|
||||
</div>
|
||||
@ -91,6 +96,7 @@
|
||||
<script>
|
||||
import ObjectView from './ObjectView.vue';
|
||||
import NotebookMenuSwitcher from '@/plugins/notebook/components/NotebookMenuSwitcher.vue';
|
||||
import tooltipHelpers from '../../api/tooltips/tooltipMixins';
|
||||
|
||||
const SIMPLE_CONTENT_TYPES = ['clock', 'timer', 'summary-widget', 'hyperlink', 'conditionWidget'];
|
||||
const CSS_WIDTH_LESS_STR = '--width-less-than-';
|
||||
@ -100,6 +106,7 @@ export default {
|
||||
ObjectView,
|
||||
NotebookMenuSwitcher
|
||||
},
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
domainObject: {
|
||||
@ -225,6 +232,10 @@ export default {
|
||||
}
|
||||
|
||||
this.widthClass = wClass.trimStart();
|
||||
},
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getObjectPath(), BELOW, 'objectName');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -30,7 +30,12 @@
|
||||
<div class="c-tree__item__type-icon c-object-label__type-icon" :class="typeClass">
|
||||
<span class="is-status__indicator" :title="`This item is ${status}`"></span>
|
||||
</div>
|
||||
<div class="c-tree__item__name c-object-label__name">
|
||||
<div
|
||||
ref="objectLabel"
|
||||
class="c-tree__item__name c-object-label__name"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
{{ domainObject.name }}
|
||||
</div>
|
||||
</a>
|
||||
@ -40,9 +45,10 @@
|
||||
import ObjectLink from '../mixins/object-link';
|
||||
import ContextMenuGesture from '../mixins/context-menu-gesture';
|
||||
import PreviewAction from '../preview/PreviewAction.js';
|
||||
import tooltipHelpers from '../../api/tooltips/tooltipMixins';
|
||||
|
||||
export default {
|
||||
mixins: [ObjectLink, ContextMenuGesture],
|
||||
mixins: [ObjectLink, ContextMenuGesture, tooltipHelpers],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
domainObject: {
|
||||
@ -132,6 +138,10 @@ export default {
|
||||
},
|
||||
setStatus(status) {
|
||||
this.status = status;
|
||||
},
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getObjectPath(), BELOW, 'objectLabel');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -21,7 +21,13 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="u-contents" :class="[{ 'c-swimlane': !isNested }, statusClass]">
|
||||
<div
|
||||
ref="swimLane"
|
||||
class="u-contents"
|
||||
:class="[{ 'c-swimlane': !isNested }, statusClass]"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
<div
|
||||
v-if="hideLabel === false"
|
||||
class="c-swimlane__lane-label c-object-label"
|
||||
@ -47,7 +53,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
|
||||
export default {
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
@ -96,6 +106,10 @@ export default {
|
||||
default() {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
domainObject: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -118,6 +132,12 @@ export default {
|
||||
statusClass() {
|
||||
return this.status ? `is-status--${this.status}` : '';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getObjectPath(), BELOW, 'swimLane');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -33,12 +33,15 @@
|
||||
<span class="is-status__indicator" :title="`This item is ${status}`"></span>
|
||||
</div>
|
||||
<span
|
||||
ref="objectName"
|
||||
class="l-browse-bar__object-name c-object-label__name"
|
||||
:class="{ 'c-input-inline': isPersistable }"
|
||||
:contenteditable="isPersistable"
|
||||
@blur="updateName"
|
||||
@keydown.enter.prevent
|
||||
@keyup.enter.prevent="updateNameOnEnterKeyPress"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
{{ domainObject.name }}
|
||||
</span>
|
||||
@ -127,6 +130,7 @@
|
||||
<script>
|
||||
import ViewSwitcher from './ViewSwitcher.vue';
|
||||
import NotebookMenuSwitcher from '@/plugins/notebook/components/NotebookMenuSwitcher.vue';
|
||||
import tooltipHelpers from '../../api/tooltips/tooltipMixins';
|
||||
|
||||
const PLACEHOLDER_OBJECT = {};
|
||||
|
||||
@ -135,6 +139,7 @@ export default {
|
||||
NotebookMenuSwitcher,
|
||||
ViewSwitcher
|
||||
},
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
actionCollection: {
|
||||
@ -383,6 +388,10 @@ export default {
|
||||
},
|
||||
setStatus(status) {
|
||||
this.status = status;
|
||||
},
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getObjectPath(), BELOW, 'objectName');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -32,11 +32,14 @@
|
||||
></div>
|
||||
<div class="c-recentobjects-listitem__body">
|
||||
<span
|
||||
ref="recentObjectName"
|
||||
class="c-recentobjects-listitem__title"
|
||||
:name="domainObject.name"
|
||||
draggable="true"
|
||||
@dragstart="dragStart"
|
||||
@click.prevent="clickedRecent"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
{{ domainObject.name }}
|
||||
</span>
|
||||
@ -62,12 +65,14 @@
|
||||
<script>
|
||||
import ObjectPath from '../components/ObjectPath.vue';
|
||||
import PreviewAction from '../preview/PreviewAction';
|
||||
import tooltipHelpers from '../../api/tooltips/tooltipMixins';
|
||||
|
||||
export default {
|
||||
name: 'RecentObjectsListItem',
|
||||
components: {
|
||||
ObjectPath
|
||||
},
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
domainObject: {
|
||||
@ -132,6 +137,10 @@ export default {
|
||||
},
|
||||
openAndScrollTo(navigationPath) {
|
||||
this.$emit('openAndScrollTo', navigationPath);
|
||||
},
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getObjectPath(), BELOW, 'recentObjectName');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -33,11 +33,14 @@
|
||||
:aria-label="`${resultName} ${resultType} result`"
|
||||
>
|
||||
<div
|
||||
ref="resultName"
|
||||
class="c-gsearch-result__title"
|
||||
:name="resultName"
|
||||
draggable="true"
|
||||
@dragstart="dragStart"
|
||||
@click="clickedResult"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
{{ resultName }}
|
||||
</div>
|
||||
@ -54,12 +57,14 @@
|
||||
import ObjectPath from '../../components/ObjectPath.vue';
|
||||
import identifierToString from '../../../tools/url';
|
||||
import PreviewAction from '../../preview/PreviewAction';
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
|
||||
export default {
|
||||
name: 'ObjectSearchResult',
|
||||
components: {
|
||||
ObjectPath
|
||||
},
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
result: {
|
||||
@ -124,6 +129,10 @@ export default {
|
||||
|
||||
event.dataTransfer.setData('openmct/domain-object-path', serializedPath);
|
||||
event.dataTransfer.setData(`openmct/domain-object/${keyString}`, this.result);
|
||||
},
|
||||
async showToolTip() {
|
||||
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
|
||||
this.buildToolTip(await this.getObjectPath(this.result.identifier), BELOW, 'resultName');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user