Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tooltips] Add tooltips on hover #6756

Merged
merged 23 commits into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
90ee655
Add tooltip api, extend object api to add telemetry composition looku…
khalidadil Jun 22, 2023
37f5c1c
Merge branch 'release/2.2.5' into feature/issue-6656
khalidadil Jun 22, 2023
78c293b
Add tooltips to telemetry/lad tables
khalidadil Jun 23, 2023
b133fe4
Clean up docs
khalidadil Jun 23, 2023
a9d4ead
Merge branch 'master' into feature/issue-6656
khalidadil Jun 23, 2023
92b7f99
Closes #6656
charlesh88 Jul 6, 2023
aa3d987
Add tooltips for Conditional widgets and Tab Views and tests
khalidadil Jul 13, 2023
1eaea39
Add tests
khalidadil Jul 13, 2023
1f165fb
Linting
khalidadil Jul 13, 2023
7830064
Merge branch 'master' into feature/issue-6656
khalidadil Jul 13, 2023
16ab9c4
Fix bad merge
khalidadil Jul 13, 2023
57d4120
Comment out test for telemetry tables
khalidadil Jul 13, 2023
41759c4
Linting
khalidadil Jul 13, 2023
b7b8356
Merge branch 'master' into feature/issue-6656
khalidadil Jul 13, 2023
a9ab584
Switch to using enum-ish consts for tooltip locations
khalidadil Jul 13, 2023
07ff673
Cleanup
khalidadil Jul 13, 2023
c2436c7
Trim LAD table row name to account for spacing required by linting rules
khalidadil Jul 13, 2023
83d81b8
Move tooltips tests to e2e
khalidadil Jul 13, 2023
6dfebb3
Fix comment
khalidadil Jul 13, 2023
fa278af
Switch from indexOf to includes
khalidadil Jul 14, 2023
c66fc84
Cleanup appActions test safety check since we're removing pointer-eve…
khalidadil Jul 14, 2023
86a2384
Rename tooltip mixin from getTelemetryPath to getTelemetryPathString
khalidadil Jul 14, 2023
aa699cb
Fixing JSDoc for tooltip API
khalidadil Jul 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 1 addition & 8 deletions e2e/appActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

/**
Expand Down
398 changes: 398 additions & 0 deletions e2e/tests/functional/tooltips.e2e.spec.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions openmct.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/MCT.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ define([
'EventEmitter',
'./api/api',
'./api/overlays/OverlayAPI',
'./api/tooltips/ToolTipAPI',
'./selection/Selection',
'./plugins/plugins',
'./ui/registries/ViewRegistry',
Expand All @@ -48,6 +49,7 @@ define([
EventEmitter,
api,
OverlayAPI,
ToolTipAPI,
Selection,
plugins,
ViewRegistry,
Expand Down Expand Up @@ -220,6 +222,8 @@ define([

['overlays', () => new OverlayAPI.default()],

['tooltips', () => new ToolTipAPI.default()],

['menus', () => new api.MenuAPI(this)],

['actions', () => new api.ActionsAPI(this)],
Expand Down
34 changes: 34 additions & 0 deletions src/api/objects/ObjectAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,40 @@
.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;

Check warning on line 563 in src/api/objects/ObjectAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/objects/ObjectAPI.js#L563

Added line #L563 was not covered by tests
}
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
Expand Down
73 changes: 73 additions & 0 deletions src/api/tooltips/ToolTip.js
Original file line number Diff line number Diff line change
@@ -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 changes: 90 additions & 0 deletions src/api/tooltips/ToolTipAPI.js
Original file line number Diff line number Diff line change
@@ -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 changes: 61 additions & 0 deletions src/api/tooltips/components/TooltipComponent.vue
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions src/api/tooltips/components/tooltip-component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.c-tooltip-wrapper {
max-width: 200px;
padding: $interiorMargin;
}

.c-tooltip {
font-style: italic;
}
72 changes: 72 additions & 0 deletions src/api/tooltips/tooltipMixins.js
Original file line number Diff line number Diff line change
@@ -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;

Check warning on line 28 in src/api/tooltips/tooltipMixins.js

View check run for this annotation

Codecov / codecov/patch

src/api/tooltips/tooltipMixins.js#L28

Added line #L28 was not covered by tests
}
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;

Check warning on line 41 in src/api/tooltips/tooltipMixins.js

View check run for this annotation

Codecov / codecov/patch

src/api/tooltips/tooltipMixins.js#L41

Added line #L41 was not covered by tests
}
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;

Check warning on line 53 in src/api/tooltips/tooltipMixins.js

View check run for this annotation

Codecov / codecov/patch

src/api/tooltips/tooltipMixins.js#L53

Added line #L53 was not covered by tests
}
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;