Refactor plot actions to save space (#5201)

* Move image export actions to 3-dot menu
* Move cursor guide and toggle grid lines to local controls for plots (on hover)
* toggle cursor and gridlines affect all plots in a stacked plot
* Fix tests
* Better message when exporting plots, fixed typo

Co-authored-by: Joe Pea <trusktr@gmail.com>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
This commit is contained in:
Shefali Joshi
2022-05-20 11:41:01 -07:00
committed by GitHub
parent 1ca5271c3e
commit 48916564e4
12 changed files with 168 additions and 104 deletions

View File

@ -51,7 +51,7 @@ class ImageExporter {
const overlays = this.openmct.overlays; const overlays = this.openmct.overlays;
const dialog = overlays.dialog({ const dialog = overlays.dialog({
iconClass: 'info', iconClass: 'info',
message: 'Caputuring an image', message: 'Capturing image, please wait...',
buttons: [ buttons: [
{ {
label: 'Cancel', label: 'Cancel',

View File

@ -154,6 +154,22 @@
> >
</button> </button>
</div> </div>
<div class="c-button-set c-button-set--strip-h">
<button
class="c-button icon-crosshair"
:class="{ 'is-active': cursorGuide }"
title="Toggle cursor guides"
@click="toggleCursorGuide"
>
</button>
<button
class="c-button"
:class="{ 'icon-grid-on': gridLines, 'icon-grid-off': !gridLines }"
title="Toggle grid lines"
@click="toggleGridLines"
>
</button>
</div>
</div> </div>
<!--Cursor guides--> <!--Cursor guides-->
@ -213,16 +229,16 @@ export default {
}; };
} }
}, },
gridLines: { initGridLines: {
type: Boolean, type: Boolean,
default() { default() {
return true; return true;
} }
}, },
cursorGuide: { initCursorGuide: {
type: Boolean, type: Boolean,
default() { default() {
return true; return false;
} }
}, },
plotTickWidth: { plotTickWidth: {
@ -252,7 +268,9 @@ export default {
isTimeOutOfSync: false, isTimeOutOfSync: false,
showLimitLineLabels: undefined, showLimitLineLabels: undefined,
isFrozenOnMouseDown: false, isFrozenOnMouseDown: false,
hasSameRangeValue: true hasSameRangeValue: true,
cursorGuide: this.initCursorGuide,
gridLines: this.initGridLines
}; };
}, },
computed: { computed: {
@ -273,6 +291,14 @@ export default {
return this.plotTickWidth || this.tickWidth; return this.plotTickWidth || this.tickWidth;
} }
}, },
watch: {
initGridLines(newGridLines) {
this.gridLines = newGridLines;
},
initCursorGuide(newCursorGuide) {
this.cursorGuide = newCursorGuide;
}
},
mounted() { mounted() {
document.addEventListener('keydown', this.handleKeyDown); document.addEventListener('keydown', this.handleKeyDown);
document.addEventListener('keyup', this.handleKeyUp); document.addEventListener('keyup', this.handleKeyUp);
@ -1130,6 +1156,14 @@ export default {
}, },
legendHoverChanged(data) { legendHoverChanged(data) {
this.showLimitLineLabels = data; this.showLimitLineLabels = data;
},
toggleCursorGuide() {
this.cursorGuide = !this.cursorGuide;
this.$emit('cursorGuide', this.cursorGuide);
},
toggleGridLines() {
this.gridLines = !this.gridLines;
this.$emit('gridLines', this.gridLines);
} }
} }
}; };

View File

@ -24,41 +24,6 @@
ref="plotWrapper" ref="plotWrapper"
class="c-plot holder holder-plot has-control-bar" class="c-plot holder holder-plot has-control-bar"
> >
<div
v-if="!options.compact"
class="c-control-bar"
>
<span class="c-button-set c-button-set--strip-h">
<button
class="c-button icon-download"
title="Export This View's Data as PNG"
@click="exportPNG()"
>
<span class="c-button__label">PNG</span>
</button>
<button
class="c-button"
title="Export This View's Data as JPG"
@click="exportJPG()"
>
<span class="c-button__label">JPG</span>
</button>
</span>
<button
class="c-button icon-crosshair"
:class="{ 'is-active': cursorGuide }"
title="Toggle cursor guides"
@click="toggleCursorGuide"
>
</button>
<button
class="c-button"
:class="{ 'icon-grid-on': gridLines, 'icon-grid-off': !gridLines }"
title="Toggle grid lines"
@click="toggleGridLines"
>
</button>
</div>
<div <div
ref="plotContainer" ref="plotContainer"
@ -70,8 +35,8 @@
class="c-loading--overlay loading" class="c-loading--overlay loading"
></div> ></div>
<mct-plot <mct-plot
:grid-lines="gridLines" :init-grid-lines="gridLines"
:cursor-guide="cursorGuide" :init-cursor-guide="cursorGuide"
:options="options" :options="options"
@loadingUpdated="loadingUpdated" @loadingUpdated="loadingUpdated"
@statusUpdated="setStatus" @statusUpdated="setStatus"
@ -135,15 +100,14 @@ export default {
this.imageExporter.exportPNG(plotElement, 'plot.png', 'export-plot'); this.imageExporter.exportPNG(plotElement, 'plot.png', 'export-plot');
}, },
toggleCursorGuide() {
this.cursorGuide = !this.cursorGuide;
},
toggleGridLines() {
this.gridLines = !this.gridLines;
},
setStatus(status) { setStatus(status) {
this.status = status; this.status = status;
},
getViewContext() {
return {
exportPNG: this.exportPNG,
exportJPG: this.exportJPG
};
} }
} }
}; };

View File

@ -80,9 +80,16 @@ export default function PlotViewProvider(openmct) {
} }
}; };
}, },
template: '<plot :options="options"></plot>' template: '<plot ref="plotComponent" :options="options"></plot>'
}); });
}, },
getViewContext() {
if (!component) {
return {};
}
return component.$refs.plotComponent.getViewContext();
},
destroy: function () { destroy: function () {
component.$destroy(); component.$destroy();
component = undefined; component = undefined;

View File

@ -0,0 +1,57 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import {isPlotView} from "@/plugins/plot/actions/utils";
const exportPNG = {
name: 'Export as PNG',
key: 'export-as-png',
description: 'Export This View\'s Data as PNG',
cssClass: 'c-icon-button icon-download',
group: 'view',
invoke(objectPath, view) {
view.getViewContext().exportPNG();
}
};
const exportJPG = {
name: 'Export as JPG',
key: 'export-as-jpg',
description: 'Export This View\'s Data as JPG',
cssClass: 'c-icon-button icon-download',
group: 'view',
invoke(objectPath, view) {
view.getViewContext().exportJPG();
}
};
const viewActions = [
exportPNG,
exportJPG
];
viewActions.forEach(action => {
action.appliesTo = (objectPath, view = {}) => {
return isPlotView(view);
};
});
export default viewActions;

View File

@ -0,0 +1,3 @@
export function isPlotView(view) {
return view.key === 'plot-single' || view.key === 'plot-overlay' || view.key === 'plot-stacked';
}

View File

@ -65,9 +65,16 @@ export default function OverlayPlotViewProvider(openmct) {
} }
}; };
}, },
template: '<plot :options="options"></plot>' template: '<plot ref="plotComponent" :options="options"></plot>'
}); });
}, },
getViewContext() {
if (!component) {
return {};
}
return component.$refs.plotComponent.getViewContext();
},
destroy: function () { destroy: function () {
component.$destroy(); component.$destroy();
component = undefined; component = undefined;

View File

@ -25,6 +25,7 @@ import StackedPlotViewProvider from './stackedPlot/StackedPlotViewProvider';
import PlotsInspectorViewProvider from './inspector/PlotsInspectorViewProvider'; import PlotsInspectorViewProvider from './inspector/PlotsInspectorViewProvider';
import OverlayPlotCompositionPolicy from './overlayPlot/OverlayPlotCompositionPolicy'; import OverlayPlotCompositionPolicy from './overlayPlot/OverlayPlotCompositionPolicy';
import StackedPlotCompositionPolicy from './stackedPlot/StackedPlotCompositionPolicy'; import StackedPlotCompositionPolicy from './stackedPlot/StackedPlotCompositionPolicy';
import PlotViewActions from "./actions/ViewActions";
export default function () { export default function () {
return function install(openmct) { return function install(openmct) {
@ -67,6 +68,9 @@ export default function () {
openmct.composition.addPolicy(new OverlayPlotCompositionPolicy(openmct).allow); openmct.composition.addPolicy(new OverlayPlotCompositionPolicy(openmct).allow);
openmct.composition.addPolicy(new StackedPlotCompositionPolicy(openmct).allow); openmct.composition.addPolicy(new StackedPlotCompositionPolicy(openmct).allow);
PlotViewActions.forEach(action => {
openmct.actions.register(action);
});
}; };
} }

View File

@ -724,16 +724,16 @@ describe("the plugin", function () {
}); });
it("turns on cursor Guides all telemetry objects", (done) => { it("turns on cursor Guides all telemetry objects", (done) => {
expect(plotViewComponentObject.cursorGuide).toBeFalse(); expect(plotViewComponentObject.$children[0].cursorGuide).toBeFalse();
plotViewComponentObject.toggleCursorGuide(); plotViewComponentObject.$children[0].cursorGuide = true;
Vue.nextTick(() => { Vue.nextTick(() => {
expect(plotViewComponentObject.$children[0].component.$children[0].cursorGuide).toBeTrue(); expect(plotViewComponentObject.$children[0].cursorGuide).toBeTrue();
done(); done();
}); });
}); });
it("shows grid lines for all telemetry objects", () => { it("shows grid lines for all telemetry objects", () => {
expect(plotViewComponentObject.gridLines).toBeTrue(); expect(plotViewComponentObject.$children[0].gridLines).toBeTrue();
let gridLinesContainer = element.querySelectorAll(".gl-plot-display-area .js-ticks"); let gridLinesContainer = element.querySelectorAll(".gl-plot-display-area .js-ticks");
let visible = 0; let visible = 0;
gridLinesContainer.forEach(el => { gridLinesContainer.forEach(el => {
@ -745,10 +745,10 @@ describe("the plugin", function () {
}); });
it("hides grid lines for all telemetry objects", (done) => { it("hides grid lines for all telemetry objects", (done) => {
expect(plotViewComponentObject.gridLines).toBeTrue(); expect(plotViewComponentObject.$children[0].gridLines).toBeTrue();
plotViewComponentObject.toggleGridLines(); plotViewComponentObject.$children[0].gridLines = false;
Vue.nextTick(() => { Vue.nextTick(() => {
expect(plotViewComponentObject.gridLines).toBeFalse(); expect(plotViewComponentObject.$children[0].gridLines).toBeFalse();
let gridLinesContainer = element.querySelectorAll(".gl-plot-display-area .js-ticks"); let gridLinesContainer = element.querySelectorAll(".gl-plot-display-area .js-ticks");
let visible = 0; let visible = 0;
gridLinesContainer.forEach(el => { gridLinesContainer.forEach(el => {

View File

@ -22,41 +22,6 @@
<template> <template>
<div class="c-plot c-plot--stacked holder holder-plot has-control-bar"> <div class="c-plot c-plot--stacked holder holder-plot has-control-bar">
<div
v-show="!hideExportButtons && !options.compact"
class="c-control-bar"
>
<span class="c-button-set c-button-set--strip-h">
<button
class="c-button icon-download"
title="Export This View's Data as PNG"
@click="exportPNG()"
>
<span class="c-button__label">PNG</span>
</button>
<button
class="c-button"
title="Export This View's Data as JPG"
@click="exportJPG()"
>
<span class="c-button__label">JPG</span>
</button>
</span>
<button
class="c-button icon-crosshair"
:class="{ 'is-active': cursorGuide }"
title="Toggle cursor guides"
@click="toggleCursorGuide"
>
</button>
<button
class="c-button"
:class="{ 'icon-grid-on': gridLines, 'icon-grid-off': !gridLines }"
title="Toggle grid lines"
@click="toggleGridLines"
>
</button>
</div>
<div class="l-view-section"> <div class="l-view-section">
<stacked-plot-item <stacked-plot-item
v-for="object in compositionObjects" v-for="object in compositionObjects"
@ -69,6 +34,8 @@
:plot-tick-width="maxTickWidth" :plot-tick-width="maxTickWidth"
@plotTickWidth="onTickWidthChange" @plotTickWidth="onTickWidthChange"
@loadingUpdated="loadingUpdated" @loadingUpdated="loadingUpdated"
@cursorGuide="onCursorGuideChange"
@gridLines="onGridLinesChange"
/> />
</div> </div>
</div> </div>
@ -184,20 +151,24 @@ export default {
this.hideExportButtons = false; this.hideExportButtons = false;
}.bind(this)); }.bind(this));
}, },
toggleCursorGuide() {
this.cursorGuide = !this.cursorGuide;
},
toggleGridLines() {
this.gridLines = !this.gridLines;
},
onTickWidthChange(width, plotId) { onTickWidthChange(width, plotId) {
if (!Object.prototype.hasOwnProperty.call(this.tickWidthMap, plotId)) { if (!Object.prototype.hasOwnProperty.call(this.tickWidthMap, plotId)) {
return; return;
} }
this.$set(this.tickWidthMap, plotId, width); this.$set(this.tickWidthMap, plotId, width);
},
onCursorGuideChange(cursorGuide) {
this.cursorGuide = cursorGuide === true;
},
onGridLinesChange(gridLines) {
this.gridLines = gridLines === true;
},
getViewContext() {
return {
exportPNG: this.exportPNG,
exportJPG: this.exportJPG
};
} }
} }
}; };

View File

@ -96,6 +96,8 @@ export default {
} }
const onTickWidthChange = this.onTickWidthChange; const onTickWidthChange = this.onTickWidthChange;
const onCursorGuideChange = this.onCursorGuideChange;
const onGridLinesChange = this.onGridLinesChange;
const loadingUpdated = this.loadingUpdated; const loadingUpdated = this.loadingUpdated;
const setStatus = this.setStatus; const setStatus = this.setStatus;
@ -121,16 +123,24 @@ export default {
return { return {
...getProps(), ...getProps(),
onTickWidthChange, onTickWidthChange,
onCursorGuideChange,
onGridLinesChange,
loadingUpdated, loadingUpdated,
setStatus setStatus
}; };
}, },
template: '<div ref="plotWrapper" class="l-view-section u-style-receiver js-style-receiver" :class="{\'s-status-timeconductor-unsynced\': status && status === \'timeconductor-unsynced\'}"><div v-show="!!loading" class="c-loading--overlay loading"></div><mct-plot :grid-lines="gridLines" :cursor-guide="cursorGuide" :plot-tick-width="plotTickWidth" :options="options" @plotTickWidth="onTickWidthChange" @statusUpdated="setStatus" @loadingUpdated="loadingUpdated"/></div>' template: '<div ref="plotWrapper" class="l-view-section u-style-receiver js-style-receiver" :class="{\'s-status-timeconductor-unsynced\': status && status === \'timeconductor-unsynced\'}"><div v-show="!!loading" class="c-loading--overlay loading"></div><mct-plot :init-grid-lines="gridLines" :init-cursor-guide="cursorGuide" :plot-tick-width="plotTickWidth" :options="options" @plotTickWidth="onTickWidthChange" @cursorGuide="onCursorGuideChange" @gridLines="onGridLinesChange" @statusUpdated="setStatus" @loadingUpdated="loadingUpdated"/></div>'
}); });
}, },
onTickWidthChange() { onTickWidthChange() {
this.$emit('plotTickWidth', ...arguments); this.$emit('plotTickWidth', ...arguments);
}, },
onCursorGuideChange() {
this.$emit('cursorGuide', ...arguments);
},
onGridLinesChange() {
this.$emit('gridLines', ...arguments);
},
setStatus(status) { setStatus(status) {
this.status = status; this.status = status;
this.updateComponentProp('status', status); this.updateComponentProp('status', status);

View File

@ -67,9 +67,16 @@ export default function StackedPlotViewProvider(openmct) {
} }
}; };
}, },
template: '<stacked-plot :options="options"></stacked-plot>' template: '<stacked-plot ref="plotComponent" :options="options"></stacked-plot>'
}); });
}, },
getViewContext() {
if (!component) {
return {};
}
return component.$refs.plotComponent.getViewContext();
},
destroy: function () { destroy: function () {
component.$destroy(); component.$destroy();
component = undefined; component = undefined;