summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/graph
diff options
context:
space:
mode:
authorDhruv Patel <dhp@odoo.com>2020-03-16 04:55:53 +0000
committerAaron Bohy <aab@odoo.com>2020-03-31 19:15:10 +0000
commitae610d0e205da921111b5a320dec833d84b05056 (patch)
treecee9944daad0d58986d75823ac5562768d1f264d /addons/web/static/src/js/views/graph
parent2516c0a212a7181fb8f54ce1bd5b46b30ffd8edf (diff)
[IMP] web: graph: drill down to records in bar/pie charts
After this commit, in the graph view, when the user clicks on parts of the bar and pie charts, it opens the list view of the concerned records (like what was already done in the pivot view). This is the default behavior, but it can be deactivated by setting the attribute 'disable_linking' to true in the view. Task 2198461 closes odoo/odoo#47796 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com> Co-authored-by: Aaron Bohy <aab@odoo.com>
Diffstat (limited to 'addons/web/static/src/js/views/graph')
-rw-r--r--addons/web/static/src/js/views/graph/graph_controller.js30
-rw-r--r--addons/web/static/src/js/views/graph/graph_model.js1
-rw-r--r--addons/web/static/src/js/views/graph/graph_renderer.js43
-rw-r--r--addons/web/static/src/js/views/graph/graph_view.js15
4 files changed, 88 insertions, 1 deletions
diff --git a/addons/web/static/src/js/views/graph/graph_controller.js b/addons/web/static/src/js/views/graph/graph_controller.js
index f47bfaeb5bb..eb5fd107081 100644
--- a/addons/web/static/src/js/views/graph/graph_controller.js
+++ b/addons/web/static/src/js/views/graph/graph_controller.js
@@ -23,6 +23,7 @@ class CarretDropdownMenu extends DropdownMenu {
var GraphController = AbstractController.extend({
custom_events: _.extend({}, AbstractController.prototype.custom_events, {
item_selected: '_onItemSelected',
+ open_view: '_onOpenView',
}),
/**
@@ -42,6 +43,9 @@ var GraphController = AbstractController.extend({
// button in the control panel owned by the graph view.
this.isEmbedded = params.isEmbedded;
this.withButtons = params.withButtons;
+ // views to use in the action triggered when the graph is clicked
+ this.views = params.views;
+ this.title = params.title;
// this parameter determines what is the list of fields
// that may be used within the groupby menu available when
@@ -296,6 +300,32 @@ var GraphController = AbstractController.extend({
this.measureMenu.update({ items: this.measures });
}
},
+
+ /**
+ * @private
+ * @param {OdooEvent} ev
+ * @param {Array[]} ev.data.domain
+ */
+ _onOpenView(ev) {
+ ev.stopPropagation();
+ const state = this.model.get();
+ const context = Object.assign({}, state.context);
+ Object.keys(context).forEach(x => {
+ if (x === 'group_by' || x.startsWith('search_default_')) {
+ delete context[x];
+ }
+ });
+ this.do_action({
+ context: context,
+ domain: ev.data.domain,
+ name: this.title,
+ res_model: this.modelName,
+ target: 'current',
+ type: 'ir.actions.act_window',
+ view_mode: 'list',
+ views: this.views,
+ });
+ },
});
return GraphController;
diff --git a/addons/web/static/src/js/views/graph/graph_model.js b/addons/web/static/src/js/views/graph/graph_model.js
index 21614493e3c..e1411324c77 100644
--- a/addons/web/static/src/js/views/graph/graph_model.js
+++ b/addons/web/static/src/js/views/graph/graph_model.js
@@ -244,6 +244,7 @@ return AbstractModel.extend({
self.chart.dataPoints.push({
resId: dataPt[self.chart.groupBy[0]] instanceof Array ? dataPt[self.chart.groupBy[0]][0] : -1,
count: count,
+ domain: dataPt.__domain,
value: value,
labels: labels,
originIndex: originIndex,
diff --git a/addons/web/static/src/js/views/graph/graph_renderer.js b/addons/web/static/src/js/views/graph/graph_renderer.js
index cf6dba6e4c3..35e78690528 100644
--- a/addons/web/static/src/js/views/graph/graph_renderer.js
+++ b/addons/web/static/src/js/views/graph/graph_renderer.js
@@ -68,6 +68,7 @@ return AbstractRenderer.extend({
this.isEmbedded = params.isEmbedded || false;
this.title = params.title || '';
this.fields = params.fields || {};
+ this.disableLinking = params.disableLinking;
this.chart = null;
this.chartId = _.uniqueId('chart');
@@ -134,6 +135,7 @@ return AbstractRenderer.extend({
* @param {Object} tooltipModel see chartjs documentation
*/
_customTooltip: function (tooltipModel) {
+ this.$el.css({ cursor: 'default' });
if (this.$tooltip) {
this.$tooltip.remove();
}
@@ -144,6 +146,10 @@ return AbstractRenderer.extend({
return;
}
+ if (this._isRedirectionEnabled()) {
+ this.$el.css({ cursor: 'pointer' });
+ }
+
const chartArea = this.chart.chartArea;
const chartAreaLeft = chartArea.left;
const chartAreaRight = chartArea.right;
@@ -579,6 +585,17 @@ return AbstractRenderer.extend({
return tooltipOptions;
},
/**
+ * Returns true iff the current graph can be clicked on to redirect to the
+ * list of records.
+ *
+ * @private
+ * @returns {boolean}
+ */
+ _isRedirectionEnabled: function () {
+ return !this.disableLinking &&
+ (this.state.mode === 'bar' || this.state.mode === 'pie');
+ },
+ /**
* Return the first index of the array list where label can be found
* or -1.
*
@@ -642,9 +659,11 @@ return AbstractRenderer.extend({
var newDataset = function (datasetLabel, originIndex) {
var data = new Array(self._getDatasetDataLength(originIndex, labels.length)).fill(0);
+ const domain = new Array(self._getDatasetDataLength(originIndex, labels.length)).fill([]);
return {
label: datasetLabel,
data: data,
+ domain: domain,
originIndex: originIndex,
};
};
@@ -658,6 +677,7 @@ return AbstractRenderer.extend({
}
var labelIndex = dataPt.labelIndex;
acc[datasetLabel].data[labelIndex] = dataPt.value;
+ acc[datasetLabel].domain[labelIndex] = dataPt.domain;
return acc;
},
{}
@@ -682,13 +702,17 @@ return AbstractRenderer.extend({
* @returns {Object} the chart options used for the current mode
*/
_prepareOptions: function (datasetsCount) {
- return {
+ const options = {
maintainAspectRatio: false,
scales: this._getScaleOptions(),
legend: this._getLegendOptions(datasetsCount),
tooltips: this._getTooltipOptions(),
elements: this._getElementOptions(),
};
+ if (this._isRedirectionEnabled()) {
+ options.onClick = this._onGraphClicked.bind(this);
+ }
+ return options;
},
/**
* Determine how to relabel a label according to a given origin.
@@ -988,6 +1012,23 @@ return AbstractRenderer.extend({
//--------------------------------------------------------------------------
/**
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onGraphClicked: function (ev) {
+ const activeElement = this.chart.getElementAtEvent(ev);
+ if (activeElement.length === 0) {
+ return;
+ }
+ const domain = this.chart.data.datasets[activeElement[0]._datasetIndex].domain;
+ if (!domain) {
+ return; // empty dataset
+ }
+ this.trigger_up('open_view', {
+ domain: domain[activeElement[0]._index],
+ });
+ },
+ /**
* If the text of a legend item has been shortened and the user mouse over
* that item (actually the event type is mousemove), a tooltip with the item
* full text is displayed.
diff --git a/addons/web/static/src/js/views/graph/graph_view.js b/addons/web/static/src/js/views/graph/graph_view.js
index 488424536e6..5ae120343c6 100644
--- a/addons/web/static/src/js/views/graph/graph_view.js
+++ b/addons/web/static/src/js/views/graph/graph_view.js
@@ -115,8 +115,23 @@ var GraphView = AbstractView.extend({
this.controllerParams.withButtons = params.withButtons !== false;
this.controllerParams.measures = [...sortedMeasures, countMeasure];
this.controllerParams.groupableFields = groupableFields;
+ this.controllerParams.title = params.title || this.arch.attrs.string || _t("Untitled");
+ // retrieve form and list view ids from the action to open those views
+ // when the graph is clicked
+ function _findView(views, viewType) {
+ const view = views.find(view => {
+ return view.type === viewType;
+ });
+ return [view ? view.viewID : false, viewType];
+ }
+ this.controllerParams.views = [
+ _findView(params.actionViews, 'list'),
+ _findView(params.actionViews, 'form'),
+ ];
+
this.rendererParams.fields = this.fields;
this.rendererParams.title = this.arch.attrs.title; // TODO: use attrs.string instead
+ this.rendererParams.disableLinking = !!JSON.parse(this.arch.attrs.disable_linking || '0');
this.loadParams.mode = this.arch.attrs.type || 'bar';
this.loadParams.measure = measure || '__count__';