mirror of
https://github.com/grafana/grafana.git
synced 2025-02-10 23:55:47 -06:00
feat(graph): refactoring shared tooltip PR #6274
This commit is contained in:
parent
51165e76ba
commit
fa393c282a
@ -89,6 +89,7 @@ export class KeybindingSrv {
|
||||
|
||||
this.bind('mod+o', () => {
|
||||
dashboard.sharedCrosshair = !dashboard.sharedCrosshair;
|
||||
appEvents.emit('graph-hover-clear');
|
||||
scope.broadcastRefresh();
|
||||
});
|
||||
|
||||
|
@ -123,11 +123,11 @@
|
||||
</div>
|
||||
|
||||
<div class="gf-form-button-row">
|
||||
<button type="button" class="btn gf-form-btn btn-success width-10" ng-click="ctrl.saveDashboard()" ng-hide="ctrl.nameExists" ng-disabled="!ctrl.inputsValid">
|
||||
<i class="fa fa-save"></i> Save & Open
|
||||
<button type="button" class="btn gf-form-btn btn-success width-12" ng-click="ctrl.saveDashboard()" ng-hide="ctrl.nameExists" ng-disabled="!ctrl.inputsValid">
|
||||
<i class="fa fa-save"></i> Import
|
||||
</button>
|
||||
<button type="button" class="btn gf-form-btn btn-danger width-10" ng-click="ctrl.saveDashboard()" ng-show="ctrl.nameExists" ng-disabled="!ctrl.inputsValid">
|
||||
<i class="fa fa-save"></i> Overwrite & Open
|
||||
<button type="button" class="btn gf-form-btn btn-danger width-12" ng-click="ctrl.saveDashboard()" ng-show="ctrl.nameExists" ng-disabled="!ctrl.inputsValid">
|
||||
<i class="fa fa-save"></i> Import (Overwrite)
|
||||
</button>
|
||||
<a class="btn btn-link" ng-click="dismiss()">Cancel</a>
|
||||
<a class="btn btn-link" ng-click="ctrl.back()">Back</a>
|
||||
|
@ -19,7 +19,6 @@ export class DashboardModel {
|
||||
timezone: any;
|
||||
editable: any;
|
||||
sharedCrosshair: any;
|
||||
sharedTooltip: any;
|
||||
rows: DashboardRow[];
|
||||
time: any;
|
||||
timepicker: any;
|
||||
@ -53,7 +52,6 @@ export class DashboardModel {
|
||||
this.timezone = data.timezone || '';
|
||||
this.editable = data.editable !== false;
|
||||
this.sharedCrosshair = data.sharedCrosshair || false;
|
||||
this.sharedTooltip = data.sharedTooltip || false;
|
||||
this.hideControls = data.hideControls || false;
|
||||
this.time = data.time || { from: 'now-6h', to: 'now' };
|
||||
this.timepicker = data.timepicker || {};
|
||||
|
@ -57,20 +57,14 @@
|
||||
</gf-form-switch>
|
||||
<gf-form-switch class="gf-form"
|
||||
label="Hide Controls"
|
||||
tooltip="Hide row controls. Shortcut: CTRL+H"
|
||||
tooltip="Hide row controls. Shortcut: CTRL+H or CMD+H"
|
||||
checked="dashboard.hideControls"
|
||||
label-class="width-11">
|
||||
</gf-form-switch>
|
||||
<gf-form-switch class="gf-form"
|
||||
label="Shared Crosshair"
|
||||
tooltip="Shared Crosshair line on all graphs. Shortcut: CTRL+O"
|
||||
checked="dashboard.sharedCrosshair"
|
||||
label-class="width-11">
|
||||
</gf-form-switch>
|
||||
<gf-form-switch class="gf-form"
|
||||
label="Shared Tooltip"
|
||||
tooltip="Shared Tooltip on all graphs."
|
||||
checked="dashboard.sharedTooltip"
|
||||
tooltip="Shared Tooltip on all graphs. Shortcut: CTRL+O or CMD+O"
|
||||
checked="dashboard.sharedCrosshair"
|
||||
label-class="width-11">
|
||||
</gf-form-switch>
|
||||
</div>
|
||||
|
@ -9,18 +9,17 @@ import 'jquery.flot.fillbelow';
|
||||
import 'jquery.flot.crosshair';
|
||||
import './jquery.flot.events';
|
||||
|
||||
import angular from 'angular';
|
||||
import $ from 'jquery';
|
||||
import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import {appEvents, coreModule} from 'app/core/core';
|
||||
import GraphTooltip from './graph_tooltip';
|
||||
import {ThresholdManager} from './threshold_manager';
|
||||
|
||||
var module = angular.module('grafana.directives');
|
||||
var labelWidthCache = {};
|
||||
|
||||
module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
||||
coreModule.directive('grafanaGraph', function($rootScope, timeSrv) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
template: '',
|
||||
@ -28,7 +27,9 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
||||
var ctrl = scope.ctrl;
|
||||
var dashboard = ctrl.dashboard;
|
||||
var panel = ctrl.panel;
|
||||
var data, annotations;
|
||||
var data;
|
||||
var annotations;
|
||||
var plot;
|
||||
var sortedSeries;
|
||||
var legendSideLastValue = null;
|
||||
var rootScope = scope.$root;
|
||||
@ -37,8 +38,8 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
||||
var tooltip = new GraphTooltip(elem, dashboard, scope, function() {
|
||||
return sortedSeries;
|
||||
});
|
||||
var plot;
|
||||
|
||||
// panel events
|
||||
ctrl.events.on('panel-teardown', () => {
|
||||
thresholdManager = null;
|
||||
|
||||
@ -48,39 +49,6 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
||||
}
|
||||
});
|
||||
|
||||
rootScope.onAppEvent('setCrosshair', function(event, info) {
|
||||
// do not need to to this if event is from this panel
|
||||
if (info.scope === scope) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dashboard.sharedCrosshair) {
|
||||
if (plot) {
|
||||
plot.setCrosshair({ x: info.pos.x, y: info.pos.y });
|
||||
}
|
||||
}
|
||||
}, scope);
|
||||
|
||||
rootScope.onAppEvent('clearCrosshair', function() {
|
||||
if (plot) {
|
||||
plot.clearCrosshair();
|
||||
}
|
||||
}, scope);
|
||||
|
||||
rootScope.onAppEvent('setTooltip', function(event, info) {
|
||||
// do not need to to this if event is from this panel
|
||||
// or another panel is in fullscreen mode
|
||||
if (info.scope === scope || ctrl.otherPanelInFullscreenMode()) {
|
||||
return;
|
||||
}
|
||||
tooltip.setTooltip(info.pos);
|
||||
}, scope);
|
||||
|
||||
rootScope.onAppEvent('clearTooltip', function() {
|
||||
tooltip.clearTooltip();
|
||||
}, scope);
|
||||
|
||||
// Receive render events
|
||||
ctrl.events.on('render', function(renderData) {
|
||||
data = renderData || data;
|
||||
if (!data) {
|
||||
@ -90,6 +58,27 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
||||
render_panel();
|
||||
});
|
||||
|
||||
// global events
|
||||
appEvents.on('graph-hover', function(evt) {
|
||||
// ignore other graph hover events if shared tooltip is disabled
|
||||
if (!dashboard.sharedCrosshair) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore if we are the emitter
|
||||
if (!plot || evt.panel.id === panel.id || ctrl.otherPanelInFullscreenMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
tooltip.show(evt.pos);
|
||||
}, scope);
|
||||
|
||||
appEvents.on('graph-hover-clear', function(event, info) {
|
||||
if (plot) {
|
||||
tooltip.clear(plot);
|
||||
}
|
||||
}, scope);
|
||||
|
||||
function getLegendHeight(panelHeight) {
|
||||
if (!panel.legend.show || panel.legend.rightSide) {
|
||||
return 0;
|
||||
@ -288,7 +277,7 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
||||
color: '#666'
|
||||
},
|
||||
crosshair: {
|
||||
mode: panel.tooltip.shared || dashboard.sharedCrosshair ? "x" : null
|
||||
mode: 'x'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
define([
|
||||
'jquery',
|
||||
'lodash'
|
||||
'app/core/core',
|
||||
],
|
||||
function ($) {
|
||||
function ($, core) {
|
||||
'use strict';
|
||||
|
||||
var appEvents = core.appEvents;
|
||||
|
||||
function GraphTooltip(elem, dashboard, scope, getSeriesFn) {
|
||||
var self = this;
|
||||
var ctrl = scope.ctrl;
|
||||
@ -41,7 +43,7 @@ function ($) {
|
||||
return j - 1;
|
||||
};
|
||||
|
||||
this.showTooltip = function(absoluteTime, innerHtml, pos, xMode) {
|
||||
this.renderAndShow = function(absoluteTime, innerHtml, pos, xMode) {
|
||||
if (xMode === 'time') {
|
||||
innerHtml = '<div class="graph-tooltip-time">'+ absoluteTime + '</div>' + innerHtml;
|
||||
}
|
||||
@ -140,43 +142,34 @@ function ($) {
|
||||
plot.unhighlight();
|
||||
}
|
||||
}
|
||||
|
||||
if (dashboard.sharedTooltip) {
|
||||
ctrl.publishAppEvent('clearTooltip');
|
||||
}
|
||||
|
||||
if (dashboard.sharedCrosshair) {
|
||||
ctrl.publishAppEvent('clearCrosshair');
|
||||
}
|
||||
appEvents.emit('graph-hover-clear');
|
||||
});
|
||||
|
||||
elem.bind("plothover", function (event, pos, item) {
|
||||
if (dashboard.sharedCrosshair) {
|
||||
ctrl.publishAppEvent('setCrosshair', {pos: pos, scope: scope});
|
||||
}
|
||||
self.show(pos, item);
|
||||
|
||||
if (dashboard.sharedTooltip) {
|
||||
pos.panelRelY = (pos.pageY - elem.offset().top) / elem.height();
|
||||
ctrl.publishAppEvent('setTooltip', {pos: pos, scope: scope});
|
||||
}
|
||||
self.setTooltip(pos, item);
|
||||
// broadcast to other graph panels that we are hovering!
|
||||
pos.panelRelY = (pos.pageY - elem.offset().top) / elem.height();
|
||||
appEvents.emit('graph-hover', {pos: pos, panel: panel});
|
||||
});
|
||||
|
||||
this.clearTooltip = function() {
|
||||
this.clear = function(plot) {
|
||||
$tooltip.detach();
|
||||
plot.clearCrosshair();
|
||||
};
|
||||
|
||||
this.setTooltip = function(pos, item) {
|
||||
this.show = function(pos, item) {
|
||||
var plot = elem.data().plot;
|
||||
var plotData = plot.getData();
|
||||
var xAxes = plot.getXAxes();
|
||||
var xMode = xAxes[0].options.mode;
|
||||
var seriesList = getSeriesFn();
|
||||
var allSeriesMode = panel.tooltip.shared;
|
||||
var group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat;
|
||||
|
||||
// if panelRelY is defined another panel wants us to show a tooltip
|
||||
// get pageX from position on x axis and pageY from relative position in original panel
|
||||
if (pos.panelRelY !== undefined) {
|
||||
if (pos.panelRelY) {
|
||||
var pointOffset = plot.pointOffset({x: pos.x});
|
||||
if (Number.isNaN(pointOffset.left) || pointOffset.left < 0) {
|
||||
$tooltip.detach();
|
||||
@ -184,6 +177,8 @@ function ($) {
|
||||
}
|
||||
pos.pageX = elem.offset().left + pointOffset.left;
|
||||
pos.pageY = elem.offset().top + elem.height() * pos.panelRelY;
|
||||
plot.setCrosshair(pos);
|
||||
allSeriesMode = true;
|
||||
}
|
||||
|
||||
if (seriesList.length === 0) {
|
||||
@ -196,7 +191,7 @@ function ($) {
|
||||
tooltipFormat = 'YYYY-MM-DD HH:mm:ss';
|
||||
}
|
||||
|
||||
if (panel.tooltip.shared) {
|
||||
if (allSeriesMode) {
|
||||
plot.unhighlight();
|
||||
|
||||
var seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos);
|
||||
@ -239,7 +234,7 @@ function ($) {
|
||||
plot.highlight(hoverInfo.index, hoverInfo.hoverIndex);
|
||||
}
|
||||
|
||||
self.showTooltip(absoluteTime, seriesHtml, pos, xMode);
|
||||
self.renderAndShow(absoluteTime, seriesHtml, pos, xMode);
|
||||
}
|
||||
// single series tooltip
|
||||
else if (item) {
|
||||
@ -260,7 +255,7 @@ function ($) {
|
||||
|
||||
group += '<div class="graph-tooltip-value">' + value + '</div>';
|
||||
|
||||
self.showTooltip(absoluteTime, group, pos, xMode);
|
||||
self.renderAndShow(absoluteTime, group, pos, xMode);
|
||||
}
|
||||
// no hit
|
||||
else {
|
||||
|
@ -96,7 +96,6 @@ class GraphCtrl extends MetricsPanelCtrl {
|
||||
value_type: 'individual',
|
||||
shared: true,
|
||||
sort: 0,
|
||||
msResolution: false,
|
||||
},
|
||||
// time overrides
|
||||
timeFrom: null,
|
||||
|
@ -12,7 +12,7 @@ import {Emitter} from 'app/core/core';
|
||||
|
||||
describe('grafanaGraph', function() {
|
||||
|
||||
beforeEach(angularMocks.module('grafana.directives'));
|
||||
beforeEach(angularMocks.module('grafana.core'));
|
||||
|
||||
function graphScenario(desc, func, elementWidth = 500) {
|
||||
describe(desc, function() {
|
||||
|
@ -48,7 +48,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="section gf-form-group">
|
||||
<h5 class="section-heading">Hover info</h5>
|
||||
<h5 class="section-heading">Hover tooltip</h5>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-9">Mode</label>
|
||||
<div class="gf-form-select-wrapper max-width-8">
|
||||
|
Loading…
Reference in New Issue
Block a user