mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Add optional data point markers and mouse-over tooltips to display values on graphs. Fixes #3514
Ensure queries are no longer executed when dashboards are closed. Fixes #3576
This commit is contained in:
parent
0e59be237b
commit
a74b9c96c1
@ -11,7 +11,10 @@ Features
|
|||||||
********
|
********
|
||||||
|
|
||||||
| `Feature #2927 <https://redmine.postgresql.org/issues/2927>`_ - Move all CSS into SCSS files for consistency and ease of colour maintenance etc.
|
| `Feature #2927 <https://redmine.postgresql.org/issues/2927>`_ - Move all CSS into SCSS files for consistency and ease of colour maintenance etc.
|
||||||
|
| `Feature #3514 <https://redmine.postgresql.org/issues/3514>`_ - Add optional data point markers and mouse-over tooltips to display values on graphs.
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
| `Bug #3576 <https://redmine.postgresql.org/issues/3576>`_ - Ensure queries are no longer executed when dashboards are closed.
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"axios-mock-adapter": "^1.14.1",
|
"axios-mock-adapter": "^1.14.1",
|
||||||
"babel-core": "~6.24.0",
|
"babel-core": "~6.24.0",
|
||||||
"babel-loader": "~7.1.2",
|
"babel-loader": "~7.1.2",
|
||||||
|
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||||
"babel-preset-airbnb": "^2.4.0",
|
"babel-preset-airbnb": "^2.4.0",
|
||||||
"babel-preset-es2015": "~6.24.0",
|
"babel-preset-es2015": "~6.24.0",
|
||||||
"babel-preset-react": "~6.23.0",
|
"babel-preset-react": "~6.23.0",
|
||||||
@ -66,7 +67,7 @@
|
|||||||
"dropzone": "^5.1.1",
|
"dropzone": "^5.1.1",
|
||||||
"eonasdan-bootstrap-datetimepicker": "^4.17.47",
|
"eonasdan-bootstrap-datetimepicker": "^4.17.47",
|
||||||
"exports-loader": "~0.6.4",
|
"exports-loader": "~0.6.4",
|
||||||
"flotr2": "^0.1.0",
|
"flotr2": "git+https://github.com/EnterpriseDB/Flotr2.git",
|
||||||
"font-awesome": "^4.7.0",
|
"font-awesome": "^4.7.0",
|
||||||
"hard-source-webpack-plugin": "^0.4.9",
|
"hard-source-webpack-plugin": "^0.4.9",
|
||||||
"immutability-helper": "^2.2.0",
|
"immutability-helper": "^2.2.0",
|
||||||
|
@ -165,7 +165,8 @@ define(
|
|||||||
|
|
||||||
if (eventName == 'panelClosed') {
|
if (eventName == 'panelClosed') {
|
||||||
pgBrowser.save_current_layout(pgBrowser);
|
pgBrowser.save_current_layout(pgBrowser);
|
||||||
pgAdmin.Dashboard.toggleVisibility(false);
|
/* Pass the closed flag also */
|
||||||
|
pgAdmin.Dashboard.toggleVisibility(false, true);
|
||||||
} else if (eventName == 'panelVisibilityChanged') {
|
} else if (eventName == 'panelVisibilityChanged') {
|
||||||
if (pgBrowser.tree) {
|
if (pgBrowser.tree) {
|
||||||
pgBrowser.save_current_layout(pgBrowser);
|
pgBrowser.save_current_layout(pgBrowser);
|
||||||
@ -174,8 +175,10 @@ define(
|
|||||||
pgAdmin.Dashboard.toggleVisibility(pgBrowser.panels.dashboard.panel.isVisible());
|
pgAdmin.Dashboard.toggleVisibility(pgBrowser.panels.dashboard.panel.isVisible());
|
||||||
}
|
}
|
||||||
// Explicitly trigger tree selected event when we add the tab.
|
// Explicitly trigger tree selected event when we add the tab.
|
||||||
pgBrowser.Events.trigger('pgadmin-browser:tree:selected', selectedNode,
|
if(selectedNode.length) {
|
||||||
pgBrowser.tree.itemData(selectedNode), pgBrowser.Node);
|
pgBrowser.Events.trigger('pgadmin-browser:tree:selected', selectedNode,
|
||||||
|
pgBrowser.tree.itemData(selectedNode), pgBrowser.Node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -81,7 +81,7 @@ class DashboardModule(PgAdminModule):
|
|||||||
help_str=gettext('The number of seconds between graph samples.')
|
help_str=gettext('The number of seconds between graph samples.')
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session_stats_refresh = self.dashboard_preference.register(
|
self.tps_stats_refresh = self.dashboard_preference.register(
|
||||||
'dashboards', 'tps_stats_refresh',
|
'dashboards', 'tps_stats_refresh',
|
||||||
gettext("Transaction throughput refresh rate"), 'integer',
|
gettext("Transaction throughput refresh rate"), 'integer',
|
||||||
1, min_val=1, max_val=999999,
|
1, min_val=1, max_val=999999,
|
||||||
@ -89,7 +89,7 @@ class DashboardModule(PgAdminModule):
|
|||||||
help_str=gettext('The number of seconds between graph samples.')
|
help_str=gettext('The number of seconds between graph samples.')
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session_stats_refresh = self.dashboard_preference.register(
|
self.ti_stats_refresh = self.dashboard_preference.register(
|
||||||
'dashboards', 'ti_stats_refresh',
|
'dashboards', 'ti_stats_refresh',
|
||||||
gettext("Tuples in refresh rate"), 'integer',
|
gettext("Tuples in refresh rate"), 'integer',
|
||||||
1, min_val=1, max_val=999999,
|
1, min_val=1, max_val=999999,
|
||||||
@ -97,7 +97,7 @@ class DashboardModule(PgAdminModule):
|
|||||||
help_str=gettext('The number of seconds between graph samples.')
|
help_str=gettext('The number of seconds between graph samples.')
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session_stats_refresh = self.dashboard_preference.register(
|
self.to_stats_refresh = self.dashboard_preference.register(
|
||||||
'dashboards', 'to_stats_refresh',
|
'dashboards', 'to_stats_refresh',
|
||||||
gettext("Tuples out refresh rate"), 'integer',
|
gettext("Tuples out refresh rate"), 'integer',
|
||||||
1, min_val=1, max_val=999999,
|
1, min_val=1, max_val=999999,
|
||||||
@ -105,7 +105,7 @@ class DashboardModule(PgAdminModule):
|
|||||||
help_str=gettext('The number of seconds between graph samples.')
|
help_str=gettext('The number of seconds between graph samples.')
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session_stats_refresh = self.dashboard_preference.register(
|
self.bio_stats_refresh = self.dashboard_preference.register(
|
||||||
'dashboards', 'bio_stats_refresh',
|
'dashboards', 'bio_stats_refresh',
|
||||||
gettext("Block I/O statistics refresh rate"), 'integer',
|
gettext("Block I/O statistics refresh rate"), 'integer',
|
||||||
1, min_val=1, max_val=999999,
|
1, min_val=1, max_val=999999,
|
||||||
@ -129,6 +129,23 @@ class DashboardModule(PgAdminModule):
|
|||||||
'will be displayed on dashboards.')
|
'will be displayed on dashboards.')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.graph_data_points = self.dashboard_preference.register(
|
||||||
|
'display', 'graph_data_points',
|
||||||
|
gettext("Show graph data points?"), 'boolean', False,
|
||||||
|
category_label=gettext('Display'),
|
||||||
|
help_str=gettext('If set to True, data points will be '
|
||||||
|
'visible on graph lines.')
|
||||||
|
)
|
||||||
|
|
||||||
|
self.graph_mouse_track = self.dashboard_preference.register(
|
||||||
|
'display', 'graph_mouse_track',
|
||||||
|
gettext("Show mouse hover tooltip?"), 'boolean', True,
|
||||||
|
category_label=gettext('Display'),
|
||||||
|
help_str=gettext('If set to True, tooltip will appear on mouse '
|
||||||
|
'hover on the graph lines giving the data point '
|
||||||
|
'details')
|
||||||
|
)
|
||||||
|
|
||||||
def get_exposed_url_endpoints(self):
|
def get_exposed_url_endpoints(self):
|
||||||
"""
|
"""
|
||||||
Returns:
|
Returns:
|
||||||
|
99
web/pgadmin/dashboard/static/js/charting.js
Normal file
99
web/pgadmin/dashboard/static/js/charting.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import Flotr from 'flotr2';
|
||||||
|
|
||||||
|
export class Chart {
|
||||||
|
constructor(container, options) {
|
||||||
|
this.chartApi = Flotr;
|
||||||
|
/* Html Node where the graph goes */
|
||||||
|
this._container = container;
|
||||||
|
/* Graph library options */
|
||||||
|
this._options = {};
|
||||||
|
this._defaultOptions = {
|
||||||
|
legend: {
|
||||||
|
position: 'nw',
|
||||||
|
backgroundColor: '#D2E8FF',
|
||||||
|
},
|
||||||
|
lines: {
|
||||||
|
show: true,
|
||||||
|
lineWidth: 2,
|
||||||
|
},
|
||||||
|
shadowSize: 0,
|
||||||
|
resolution : 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
this._dataset = null;
|
||||||
|
this._tooltipFormatter = null;
|
||||||
|
/* Just to store other data related to charts. Used nowhere here in the module */
|
||||||
|
this._otherData = {};
|
||||||
|
this.setOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
getContainer() {
|
||||||
|
return this._container;
|
||||||
|
}
|
||||||
|
|
||||||
|
getContainerDimensions() {
|
||||||
|
return {
|
||||||
|
height: this._container.clientHeight,
|
||||||
|
width: this._container.clientWidth,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getOptions() {
|
||||||
|
return this._options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This should be changed if library changed */
|
||||||
|
setOptions(options, mergeOptions=true) {
|
||||||
|
/* If mergeOptions then merge the options, else replace existing options */
|
||||||
|
if(mergeOptions) {
|
||||||
|
this._options = {...this._defaultOptions, ...this._options, ...options};
|
||||||
|
} else {
|
||||||
|
this._options = {...this._defaultOptions, ...options};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeOptions(optionKey) {
|
||||||
|
if(this._options[optionKey]) {
|
||||||
|
delete this._options[optionKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getOtherData(key) {
|
||||||
|
return this._otherData[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
setOtherData(key, value) {
|
||||||
|
this._otherData[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
isVisible() {
|
||||||
|
let dim = this.getContainerDimensions();
|
||||||
|
return (dim.height > 0 && dim.width > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
isInPage() {
|
||||||
|
return (this._container === document.body) ? false : document.body.contains(this._container);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTooltipFormatter(tooltipFormatter) {
|
||||||
|
let opt = this.getOptions();
|
||||||
|
|
||||||
|
this._tooltipFormatter = tooltipFormatter;
|
||||||
|
|
||||||
|
if(this._tooltipFormatter) {
|
||||||
|
this.setOptions({
|
||||||
|
mouse: {
|
||||||
|
...opt.mouse,
|
||||||
|
trackFormatter: this._tooltipFormatter,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(dataset) {
|
||||||
|
this._dataset = dataset;
|
||||||
|
if(this._container) {
|
||||||
|
Flotr.draw(this._container, this._dataset, this._options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
define('pgadmin.dashboard', [
|
define('pgadmin.dashboard', [
|
||||||
'sources/url_for', 'sources/gettext', 'require', 'jquery', 'underscore',
|
'sources/url_for', 'sources/gettext', 'require', 'jquery', 'underscore',
|
||||||
'sources/pgadmin', 'backbone', 'backgrid', 'flotr2',
|
'sources/pgadmin', 'backbone', 'backgrid', './charting',
|
||||||
'pgadmin.alertifyjs', 'pgadmin.backform',
|
'pgadmin.alertifyjs', 'pgadmin.backform',
|
||||||
'sources/nodes/dashboard', 'backgrid.filter',
|
'sources/nodes/dashboard', 'backgrid.filter',
|
||||||
'pgadmin.browser', 'bootstrap', 'wcdocker',
|
'pgadmin.browser', 'bootstrap', 'wcdocker',
|
||||||
], function(
|
], function(
|
||||||
url_for, gettext, r, $, _, pgAdmin, Backbone, Backgrid, Flotr,
|
url_for, gettext, r, $, _, pgAdmin, Backbone, Backgrid, charting,
|
||||||
Alertify, Backform, NodesDashboard
|
Alertify, Backform, NodesDashboard
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -210,10 +210,8 @@ define('pgadmin.dashboard', [
|
|||||||
// Load the default welcome dashboard
|
// Load the default welcome dashboard
|
||||||
var url = url_for('dashboard.index');
|
var url = url_for('dashboard.index');
|
||||||
|
|
||||||
/* Store the interval ids of the graph interval functions so that we can clear
|
/* Store the chart objects and there interval ids in this store */
|
||||||
* them when graphs are disabled
|
this.chartStore = {};
|
||||||
*/
|
|
||||||
this.intervalIds = {};
|
|
||||||
|
|
||||||
var dashboardPanel = pgBrowser.panels['dashboard'].panel;
|
var dashboardPanel = pgBrowser.panels['dashboard'].panel;
|
||||||
if (dashboardPanel) {
|
if (dashboardPanel) {
|
||||||
@ -266,7 +264,7 @@ define('pgadmin.dashboard', [
|
|||||||
!_.isUndefined(itemData.connected) &&
|
!_.isUndefined(itemData.connected) &&
|
||||||
itemData.connected !== true
|
itemData.connected !== true
|
||||||
) {
|
) {
|
||||||
self.clearIntervalId();
|
self.clearChartFromStore();
|
||||||
}
|
}
|
||||||
} else if (itemData && itemData._type) {
|
} else if (itemData && itemData._type) {
|
||||||
var treeHierarchy = node.getTreeNodeHierarchy(item),
|
var treeHierarchy = node.getTreeNodeHierarchy(item),
|
||||||
@ -331,8 +329,8 @@ define('pgadmin.dashboard', [
|
|||||||
) {
|
) {
|
||||||
$(div).empty();
|
$(div).empty();
|
||||||
|
|
||||||
/* Clear all the interval functions of previous dashboards */
|
/* Clear all the charts previous dashboards */
|
||||||
self.clearIntervalId();
|
self.clearChartFromStore();
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url,
|
url: url,
|
||||||
@ -356,8 +354,8 @@ define('pgadmin.dashboard', [
|
|||||||
!_.isUndefined(itemData.connected) &&
|
!_.isUndefined(itemData.connected) &&
|
||||||
itemData.connected !== true
|
itemData.connected !== true
|
||||||
) {
|
) {
|
||||||
/* Clear all the interval functions of previous dashboards */
|
/* Clear all the charts previous dashboards */
|
||||||
self.clearIntervalId();
|
self.clearChartFromStore();
|
||||||
}
|
}
|
||||||
$(div).html(
|
$(div).html(
|
||||||
'<div class="alert alert-info pg-panel-message" role="alert">' + gettext('Please connect to the selected server to view the dashboard.') + '</div>'
|
'<div class="alert alert-info pg-panel-message" role="alert">' + gettext('Please connect to the selected server to view the dashboard.') + '</div>'
|
||||||
@ -371,7 +369,7 @@ define('pgadmin.dashboard', [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
renderChartLoop: function(container, sid, did, url, options, counter, refresh) {
|
renderChartLoop: function(chartObj, sid, did, url, counter, refresh) {
|
||||||
var data = [],
|
var data = [],
|
||||||
dataset = [];
|
dataset = [];
|
||||||
|
|
||||||
@ -386,24 +384,22 @@ define('pgadmin.dashboard', [
|
|||||||
dataType: 'html',
|
dataType: 'html',
|
||||||
})
|
})
|
||||||
.done(function(resp) {
|
.done(function(resp) {
|
||||||
$(container).removeClass('graph-error');
|
$(chartObj.getContainer()).removeClass('graph-error');
|
||||||
data = JSON.parse(resp);
|
data = JSON.parse(resp);
|
||||||
if (!dashboardVisible)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var y = 0,
|
var y = 0,
|
||||||
x;
|
x;
|
||||||
if (dataset.length == 0) {
|
if (dataset.length == 0) {
|
||||||
if (counter == true) {
|
if (counter == true) {
|
||||||
// Have we stashed initial values?
|
// Have we stashed initial values?
|
||||||
if (_.isUndefined($(container).data('counter_previous_vals'))) {
|
if (_.isUndefined(chartObj.getOtherData('counter_previous_vals'))) {
|
||||||
$(container).data('counter_previous_vals', data[0]);
|
chartObj.setOtherData('counter_previous_vals', data[0]);
|
||||||
} else {
|
} else {
|
||||||
// Create the initial data structure
|
// Create the initial data structure
|
||||||
for (x in data[0]) {
|
for (x in data[0]) {
|
||||||
dataset.push({
|
dataset.push({
|
||||||
'data': [
|
'data': [
|
||||||
[0, data[0][x] - $(container).data('counter_previous_vals')[x]],
|
[0, data[0][x] - chartObj.getOtherData('counter_previous_vals')[x]],
|
||||||
],
|
],
|
||||||
'label': x,
|
'label': x,
|
||||||
});
|
});
|
||||||
@ -429,10 +425,10 @@ define('pgadmin.dashboard', [
|
|||||||
} else {
|
} else {
|
||||||
// Store the current value, minus the previous one we stashed.
|
// Store the current value, minus the previous one we stashed.
|
||||||
// It's possible the tab has been reloaded, in which case out previous values are gone
|
// It's possible the tab has been reloaded, in which case out previous values are gone
|
||||||
if (_.isUndefined($(container).data('counter_previous_vals')))
|
if (_.isUndefined(chartObj.getOtherData('counter_previous_vals')))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dataset[y]['data'].unshift([0, data[0][x] - $(container).data('counter_previous_vals')[x]]);
|
dataset[y]['data'].unshift([0, data[0][x] - chartObj.getOtherData('counter_previous_vals')[x]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the time index to get a proper scrolling display
|
// Reset the time index to get a proper scrolling display
|
||||||
@ -442,7 +438,7 @@ define('pgadmin.dashboard', [
|
|||||||
|
|
||||||
y++;
|
y++;
|
||||||
}
|
}
|
||||||
$(container).data('counter_previous_vals', data[0]);
|
chartObj.setOtherData('counter_previous_vals', data[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove uneeded elements
|
// Remove uneeded elements
|
||||||
@ -453,12 +449,9 @@ define('pgadmin.dashboard', [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw Graph, if the container still exists and has a size
|
if (chartObj.isInPage()) {
|
||||||
var dashboardPanel = pgBrowser.panels['dashboard'].panel;
|
if (chartObj.isVisible()) {
|
||||||
var div = dashboardPanel.layout().scene().find('.pg-panel-content');
|
chartObj.draw(dataset);
|
||||||
if ($(div).find(container).length) { // Exists?
|
|
||||||
if (container.clientHeight > 0 && container.clientWidth > 0) { // Not hidden?
|
|
||||||
Flotr.draw(container, dataset, options);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
@ -487,8 +480,8 @@ define('pgadmin.dashboard', [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(container).addClass('graph-error');
|
$(chartObj.getContainer()).addClass('graph-error');
|
||||||
$(container).html(
|
$(chartObj.getContainer()).html(
|
||||||
'<div class="alert alert-' + cls + ' pg-panel-message" role="alert">' + msg + '</div>'
|
'<div class="alert alert-' + cls + ' pg-panel-message" role="alert">' + msg + '</div>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -510,15 +503,41 @@ define('pgadmin.dashboard', [
|
|||||||
// { data: [[0, y0], [1, y1]...], label: 'Label 3', [options] }
|
// { data: [[0, y0], [1, y1]...], label: 'Label 3', [options] }
|
||||||
// ]
|
// ]
|
||||||
|
|
||||||
let self = this;
|
let self = this,
|
||||||
if(self.intervalIds[chartName]
|
tooltipFormatter = function(refresh, currVal) {
|
||||||
|
return(`Seconds ago: ${parseInt(currVal.x * refresh)}</br>
|
||||||
|
Value: ${currVal.y}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(self.chartStore[chartName]
|
||||||
&& self.old_preferences[prefName] != self.preferences[prefName]) {
|
&& self.old_preferences[prefName] != self.preferences[prefName]) {
|
||||||
self.clearIntervalId(chartName);
|
self.clearChartFromStore(chartName);
|
||||||
}
|
}
|
||||||
if(!self.intervalIds[chartName]) {
|
|
||||||
self.intervalIds[chartName] = self.renderChartLoop(
|
if(self.chartStore[chartName]) {
|
||||||
container, self.sid, self.did, url,
|
let chartObj = self.chartStore[chartName].chartObj;
|
||||||
options, counter, self.preferences[prefName]
|
chartObj.setOptions(options, false);
|
||||||
|
chartObj.setTooltipFormatter(
|
||||||
|
tooltipFormatter.bind(null, self.preferences[prefName])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!self.chartStore[chartName]) {
|
||||||
|
|
||||||
|
let chartObj = new charting.Chart(container, options);
|
||||||
|
|
||||||
|
chartObj.setTooltipFormatter(
|
||||||
|
tooltipFormatter.bind(null, self.preferences[prefName])
|
||||||
|
);
|
||||||
|
|
||||||
|
self.chartStore[chartName] = {
|
||||||
|
'chartObj' : chartObj,
|
||||||
|
'intervalId' : undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.chartStore[chartName]['intervalId'] = self.renderChartLoop(
|
||||||
|
self.chartStore[chartName]['chartObj'], self.sid, self.did, url,
|
||||||
|
counter, self.preferences[prefName]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -666,17 +685,17 @@ define('pgadmin.dashboard', [
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
clearIntervalId: function(intervalId) {
|
clearChartFromStore: function(chartName) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if(!intervalId){
|
if(!chartName){
|
||||||
_.each(self.intervalIds, function(id, key) {
|
_.each(self.chartStore, function(chart, key) {
|
||||||
clearInterval(id);
|
clearInterval(chart.intervalId);
|
||||||
delete self.intervalIds[key];
|
delete self.chartStore[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
clearInterval(self.intervalIds[intervalId]);
|
clearInterval(self.chartStore[chartName].intervalId);
|
||||||
delete self.intervalIds[intervalId];
|
delete self.chartStore[chartName];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -737,20 +756,41 @@ define('pgadmin.dashboard', [
|
|||||||
yaxis: {
|
yaxis: {
|
||||||
autoscale: 1,
|
autoscale: 1,
|
||||||
},
|
},
|
||||||
legend: {
|
|
||||||
position: 'nw',
|
|
||||||
backgroundColor: '#D2E8FF',
|
|
||||||
},
|
|
||||||
shadowSize: 0,
|
|
||||||
resolution : 5,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if(self.preferences.graph_data_points) {
|
||||||
|
/* Merge data points related options */
|
||||||
|
options_line = {
|
||||||
|
...options_line,
|
||||||
|
...{
|
||||||
|
points: {
|
||||||
|
show:true,
|
||||||
|
radius: 1,
|
||||||
|
hitRadius: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(self.preferences.graph_mouse_track) {
|
||||||
|
/* Merge mouse track related options */
|
||||||
|
options_line = {
|
||||||
|
...options_line,
|
||||||
|
...{
|
||||||
|
mouse: {
|
||||||
|
track:true,
|
||||||
|
position: 'sw',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if(self.preferences.show_graphs && $('#dashboard-graphs').hasClass('dashboard-hidden')) {
|
if(self.preferences.show_graphs && $('#dashboard-graphs').hasClass('dashboard-hidden')) {
|
||||||
$('#dashboard-graphs').removeClass('dashboard-hidden');
|
$('#dashboard-graphs').removeClass('dashboard-hidden');
|
||||||
}
|
}
|
||||||
else if(!self.preferences.show_graphs) {
|
else if(!self.preferences.show_graphs) {
|
||||||
$('#dashboard-graphs').addClass('dashboard-hidden');
|
$('#dashboard-graphs').addClass('dashboard-hidden');
|
||||||
self.clearIntervalId();
|
self.clearChartFromStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.preferences.show_activity && $('#dashboard-activity').hasClass('dashboard-hidden')) {
|
if (self.preferences.show_activity && $('#dashboard-activity').hasClass('dashboard-hidden')) {
|
||||||
@ -1344,8 +1384,11 @@ define('pgadmin.dashboard', [
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleVisibility: function(flag) {
|
toggleVisibility: function(visible, closed=false) {
|
||||||
dashboardVisible = flag;
|
dashboardVisible = visible;
|
||||||
|
if(closed) {
|
||||||
|
this.clearChartFromStore();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
can_take_action: function(m) {
|
can_take_action: function(m) {
|
||||||
// We will validate if user is allowed to cancel the active query
|
// We will validate if user is allowed to cancel the active query
|
||||||
|
91
web/regression/javascript/dashboard/charting_spec.js
Normal file
91
web/regression/javascript/dashboard/charting_spec.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import $ from 'jquery';
|
||||||
|
import {Chart} from 'top/dashboard/static/js/charting';
|
||||||
|
|
||||||
|
describe('In charting related testcases', ()=> {
|
||||||
|
let chartObj = undefined,
|
||||||
|
chartDiv = undefined,
|
||||||
|
options = {};
|
||||||
|
|
||||||
|
beforeEach(()=> {
|
||||||
|
$('body').append(
|
||||||
|
'<div id="charting-test-container"></div>'
|
||||||
|
);
|
||||||
|
chartDiv = $('#charting-test-container')[0];
|
||||||
|
chartObj = new Chart(chartDiv, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Chart api should be defined', ()=>{
|
||||||
|
expect(chartObj.chartApi).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Return the correct container', ()=>{
|
||||||
|
expect(chartObj.getContainer()).toBe(chartDiv);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Returns the container dimensions', ()=>{
|
||||||
|
let dim = chartObj.getContainerDimensions();
|
||||||
|
expect(dim.height).toBeDefined();
|
||||||
|
expect(dim.width).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Check if options are set', ()=>{
|
||||||
|
chartObj.setOptions({
|
||||||
|
mouse: {
|
||||||
|
track:true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let opt = chartObj.getOptions();
|
||||||
|
|
||||||
|
expect(opt.mouse).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Check if options are set with mergeOptions false', ()=>{
|
||||||
|
let overOpt = {
|
||||||
|
mouse: {
|
||||||
|
track:true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
chartObj.setOptions(overOpt, false);
|
||||||
|
|
||||||
|
let newOptShouldBe = {...chartObj._defaultOptions, ...overOpt};
|
||||||
|
|
||||||
|
let opt = chartObj.getOptions();
|
||||||
|
expect(JSON.stringify(opt)).toEqual(JSON.stringify(newOptShouldBe));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Check if other data is set', ()=>{
|
||||||
|
chartObj.setOtherData('some_val', 1);
|
||||||
|
expect(chartObj._otherData['some_val']).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Check if other data is get', ()=>{
|
||||||
|
chartObj.setOtherData('some_val', 1);
|
||||||
|
expect(chartObj.getOtherData('some_val')).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Check if isVisible returns correct', ()=>{
|
||||||
|
let dimSpy = spyOn(chartObj, 'getContainerDimensions');
|
||||||
|
|
||||||
|
dimSpy.and.returnValue({
|
||||||
|
height: 1, width: 1,
|
||||||
|
});
|
||||||
|
expect(chartObj.isVisible()).toBe(true);
|
||||||
|
dimSpy.and.stub();
|
||||||
|
|
||||||
|
dimSpy.and.returnValue({
|
||||||
|
height: 0, width: 0,
|
||||||
|
});
|
||||||
|
expect(chartObj.isVisible()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Check if isInPage returns correct', ()=>{
|
||||||
|
expect(chartObj.isInPage()).toBe(true);
|
||||||
|
$('body').find('#charting-test-container').remove();
|
||||||
|
expect(chartObj.isInPage()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(()=>{
|
||||||
|
$('body').find('#charting-test-container').remove();
|
||||||
|
});
|
||||||
|
});
|
@ -168,6 +168,7 @@ module.exports = {
|
|||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
presets: ['es2015', 'react'],
|
presets: ['es2015', 'react'],
|
||||||
|
plugins: ['transform-object-rest-spread'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
|
@ -58,6 +58,7 @@ module.exports = {
|
|||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.js', '.jsx'],
|
extensions: ['.js', '.jsx'],
|
||||||
alias: {
|
alias: {
|
||||||
|
'top': path.join(__dirname, './pgadmin'),
|
||||||
'jquery': path.join(__dirname, './node_modules/jquery/dist/jquery'),
|
'jquery': path.join(__dirname, './node_modules/jquery/dist/jquery'),
|
||||||
'alertify': path.join(__dirname, './node_modules/alertifyjs/build/alertify'),
|
'alertify': path.join(__dirname, './node_modules/alertifyjs/build/alertify'),
|
||||||
'jquery.event.drag': path.join(__dirname, './node_modules/slickgrid/lib/jquery.event.drag-2.3.0'),
|
'jquery.event.drag': path.join(__dirname, './node_modules/slickgrid/lib/jquery.event.drag-2.3.0'),
|
||||||
@ -75,6 +76,8 @@ module.exports = {
|
|||||||
'slickgrid': nodeModulesDir + '/slickgrid/',
|
'slickgrid': nodeModulesDir + '/slickgrid/',
|
||||||
'slickgrid.plugins': nodeModulesDir + '/slickgrid/plugins/',
|
'slickgrid.plugins': nodeModulesDir + '/slickgrid/plugins/',
|
||||||
'slickgrid.grid': nodeModulesDir + '/slickgrid/slick.grid',
|
'slickgrid.grid': nodeModulesDir + '/slickgrid/slick.grid',
|
||||||
|
'bean': path.join(__dirname, './node_modules/flotr2/lib/bean'),
|
||||||
|
'flotr2': path.join(__dirname, './node_modules/flotr2/flotr2.amd'),
|
||||||
'browser': path.resolve(__dirname, 'pgadmin/browser/static/js'),
|
'browser': path.resolve(__dirname, 'pgadmin/browser/static/js'),
|
||||||
'pgadmin': sourcesDir + '/js/pgadmin',
|
'pgadmin': sourcesDir + '/js/pgadmin',
|
||||||
'pgadmin.sqlfoldcode': sourcesDir + '/js/codemirror/addon/fold/pgadmin-sqlfoldcode',
|
'pgadmin.sqlfoldcode': sourcesDir + '/js/codemirror/addon/fold/pgadmin-sqlfoldcode',
|
||||||
|
@ -907,7 +907,7 @@ babel-plugin-transform-jscript@^6.22.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
babel-runtime "^6.22.0"
|
babel-runtime "^6.22.0"
|
||||||
|
|
||||||
babel-plugin-transform-object-rest-spread@^6.23.0:
|
babel-plugin-transform-object-rest-spread@^6.23.0, babel-plugin-transform-object-rest-spread@^6.26.0:
|
||||||
version "6.26.0"
|
version "6.26.0"
|
||||||
resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06"
|
resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3766,9 +3766,9 @@ flatten@^1.0.2:
|
|||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
|
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
|
||||||
|
|
||||||
flotr2@^0.1.0:
|
"flotr2@git+https://github.com/EnterpriseDB/Flotr2.git":
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/flotr2/-/flotr2-0.1.0.tgz#8d31b0d1b3dc46f5e399edeb7a179e075b36e036"
|
resolved "git+https://github.com/EnterpriseDB/Flotr2.git#dc514e32d93b2c593ab379a52de1664ee9da63e1"
|
||||||
|
|
||||||
flush-write-stream@^1.0.2:
|
flush-write-stream@^1.0.2:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
|
Loading…
Reference in New Issue
Block a user