diff options
| author | Mathieu Duckerts-Antoine <dam@odoo.com> | 2020-08-10 14:27:58 +0000 |
|---|---|---|
| committer | Mathieu Duckerts-Antoine <dam@odoo.com> | 2020-08-12 08:52:20 +0000 |
| commit | ae58021255274d3733e58ee8b020ca275aaa430d (patch) | |
| tree | 926009fc46bb86c608d76044a3a4de52119555a2 | |
| parent | fd6182443f68af3b6418195b13a4ab6c40599572 (diff) | |
[IMP] web: sample data in graph view
A previous commit has paved the way for displaying sample data in the
reporting views. The present commit brings the required changes to have
sample data in graph views.
Task ID: 2282196
Co-authored-by: Aaron Bohy <aab@odoo.com>
Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
| -rw-r--r-- | addons/web/static/src/js/views/graph/graph_model.js | 9 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/graph/graph_renderer.js | 79 | ||||
| -rw-r--r-- | addons/web/static/src/xml/base.xml | 12 | ||||
| -rw-r--r-- | addons/web/static/tests/views/graph_tests.js | 145 | ||||
| -rw-r--r-- | odoo/addons/base/rng/graph_view.rng | 1 |
5 files changed, 169 insertions, 77 deletions
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 8679fafe4ff..b1bcddb47c2 100644 --- a/addons/web/static/src/js/views/graph/graph_model.js +++ b/addons/web/static/src/js/views/graph/graph_model.js @@ -35,7 +35,7 @@ return AbstractModel.extend({ * @returns {Object} */ __get: function () { - return _.extend({}, this.chart); + return Object.assign({ isSample: this.isSampleModel }, this.chart); }, /** * Initial loading. @@ -165,7 +165,12 @@ return AbstractModel.extend({ this.chart.comparisonFieldIndex = -1; } }, - + /** + * @override + */ + _isEmpty() { + return this.chart.dataPoints.length === 0; + }, /** * Fetch and process graph data. It is basically a(some) read_group(s) * with correct fields for each domain. We have to do some light processing 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 52608e7b9cd..5cbe3f75324 100644 --- a/addons/web/static/src/js/views/graph/graph_renderer.js +++ b/addons/web/static/src/js/views/graph/graph_renderer.js @@ -54,6 +54,7 @@ var MAX_LEGEND_LENGTH = 4 * (Math.max(1, config.device.size_class)); return AbstractRenderer.extend({ className: "o_graph_renderer", + sampleDataTargets: ['.o_graph_canvas_container'], /** * @override * @param {Widget} parent @@ -762,9 +763,30 @@ return AbstractRenderer.extend({ } var dataPoints = this._filterDataPoints(); dataPoints = this._sortDataPoints(dataPoints); - if (!dataPoints.length && this.state.mode !== 'pie') { - this.$el.append(qweb.render('View.NoContentHelper')); - } else if (this.isInDOM) { + if (this.isInDOM) { + this._renderTitle(); + + // detect if some pathologies are still present after the filtering + if (this.state.mode === 'pie') { + const someNegative = dataPoints.some(dataPt => dataPt.value < 0); + const somePositive = dataPoints.some(dataPt => dataPt.value > 0); + if (someNegative && somePositive) { + const context = { + title: _t("Invalid data"), + description: [ + _t("Pie chart cannot mix positive and negative numbers. "), + _t("Try to change your domain to only display positive results") + ].join("") + }; + this._renderNoContentHelper(context); + return; + } + } + + if (this.state.isSample && !this.isEmbedded) { + this._renderNoContentHelper(); + } + // only render the graph if the widget is already in the DOM (this // happens typically after an update), otherwise, it will be // rendered when the widget will be attached to the DOM (see @@ -785,8 +807,6 @@ return AbstractRenderer.extend({ } else if (this.state.mode === 'pie') { this._renderPieChart(dataPoints); } - - this._renderTitle(); } }, /** @@ -876,6 +896,25 @@ return AbstractRenderer.extend({ }); }, /** + * @private + */ + _renderNoContentHelper: function (context) { + let templateName; + if (!context && this.noContentHelp) { + templateName = "web.ActionHelper"; + context = { noContentHelp: this.noContentHelp }; + } else { + templateName = "web.NoContentHelper"; + } + const template = document.createElement('template'); + // FIXME: retrieve owl qweb instance via the env set on Component s.t. + // it also works in the tests (importing 'web.env' wouldn't). This won't + // be necessary as soon as this rendering will be written in owl. + const OwlQweb = owl.Component.env.qweb; + template.innerHTML = OwlQweb.renderToString(templateName, context); + this.el.append(template.content.firstChild); + }, + /** * create pie chart * * @private @@ -883,38 +922,10 @@ return AbstractRenderer.extend({ */ _renderPieChart: function (dataPoints) { var self = this; - - // try to see if some pathologies are still present after the filtering - var allNegative = true; - var someNegative = false; - var allZero = true; - dataPoints.forEach(function (datapt) { - allNegative = allNegative && (datapt.value < 0); - someNegative = someNegative || (datapt.value < 0); - allZero = allZero && (datapt.value === 0); - }); - if (someNegative && !allNegative) { - this.$el.empty(); - this.$el.append(qweb.render('View.NoContentHelper', { - title: _t("Invalid data"), - description: _t("Pie chart cannot mix positive and negative numbers. " + - "Try to change your domain to only display positive results"), - })); - return; - } - if (allZero && !this.isEmbedded && this.state.origins.length === 1) { - this.$el.empty(); - this.$el.append(qweb.render('View.NoContentHelper', { - title: _t("Invalid data"), - description: _t("Pie chart cannot display all zero numbers.. " + - "Try to change your domain to display positive results"), - })); - return; - } - // prepare data var data = {}; var colors = []; + const allZero = dataPoints.every(dataPt => dataPt.value === 0); if (allZero) { // add fake data to display a pie chart with a grey zone associated // with every origin diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 12eff26e6ac..27cc0fdc4f9 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -803,17 +803,9 @@ </div> </div> -<div t-name="View.NoContentHelper" class="o_view_nocontent" role="alert"> +<div t-name="web.ActionHelper" class="o_view_nocontent" owl="1"> <div class="o_nocontent_help"> - <p class="o_view_nocontent_empty_folder"> - <t t-if="title" t-esc="title"/> - <t t-else="1">No data to display</t> - </p> - <p> - <t t-if="description" t-esc="description"/> - <t t-else="1">Try to add some records, or make sure that there is no - active filter in the search bar.</t> - </p> + <t t-raw="noContentHelp"/> </div> </div> diff --git a/addons/web/static/tests/views/graph_tests.js b/addons/web/static/tests/views/graph_tests.js index 68c28952877..253c98d706a 100644 --- a/addons/web/static/tests/views/graph_tests.js +++ b/addons/web/static/tests/views/graph_tests.js @@ -338,43 +338,53 @@ QUnit.module('Views', { }); QUnit.test('no content helper (bar chart)', async function (assert) { - assert.expect(2); + assert.expect(3); this.data.foo.records = []; var graph = await createView({ View: GraphView, model: "foo", data: this.data, - arch: '<graph string="Gloups">' + - '<field name="product_id"/>' + - '</graph>', + arch: ` + <graph string="Gloups"> + <field name="product_id"/> + </graph>`, + viewOptions: { + action: { + help: '<p class="abc">This helper should not be displayed in graph views</p>' + } + }, }); - assert.containsNone(graph, 'div.o_graph_canvas_container canvas', - "should not contain a div with a canvas element"); - assert.containsOnce(graph, 'div.o_view_nocontent', - "should display the no content helper"); + assert.containsOnce(graph, 'div.o_graph_canvas_container canvas'); + assert.containsNone(graph, 'div.o_view_nocontent'); + assert.containsNone(graph, '.abc'); graph.destroy(); }); QUnit.test('no content helper (pie chart)', async function (assert) { - assert.expect(2); + assert.expect(3); this.data.foo.records = []; var graph = await createView({ View: GraphView, model: "foo", data: this.data, - arch: '<graph type="pie">' + - '<field name="product_id"/>' + - '</graph>', + arch: ` + <graph type="pie"> + <field name="product_id"/> + </graph>`, + viewOptions: { + action: { + help: '<p class="abc">This helper should not be displayed in graph views</p>' + } + }, }); - assert.containsNone(graph, 'div.o_graph_canvas_container canvas', - "should not contain a div with a canvas element"); - assert.containsOnce(graph, 'div.o_view_nocontent', - "should display the no content helper"); + assert.containsOnce(graph, 'div.o_graph_canvas_container canvas'); + assert.containsNone(graph, 'div.o_view_nocontent'); + assert.containsNone(graph, '.abc'); graph.destroy(); }); @@ -413,27 +423,33 @@ QUnit.module('Views', { }); QUnit.test('no content helper after update', async function (assert) { - assert.expect(4); + assert.expect(6); var graph = await createView({ View: GraphView, model: "foo", data: this.data, - arch: '<graph string="Gloups">' + - '<field name="product_id"/>' + - '</graph>', + arch: ` + <graph string="Gloups"> + <field name="product_id"/> + </graph>`, + viewOptions: { + action: { + help: '<p class="abc">This helper should not be displayed in graph views</p>' + } + }, }); - assert.containsOnce(graph, 'div.o_graph_canvas_container canvas', - "should contain a div with a canvas element"); - assert.containsNone(graph, 'div.o_view_nocontent', - "should not display the no content helper"); + assert.containsOnce(graph, 'div.o_graph_canvas_container canvas'); + assert.containsNone(graph, 'div.o_view_nocontent'); + assert.containsNone(graph, '.abc'); + + await testUtils.graph.reload(graph, {domain: [['product_id', '<', 0]]}); + + assert.containsOnce(graph, 'div.o_graph_canvas_container canvas'); + assert.containsNone(graph, 'div.o_view_nocontent'); + assert.containsNone(graph, '.abc'); - await testUtils.graph.reload(graph, {domain: [['product_id', '=', 4]]}); - assert.containsNone(graph, 'div.o_graph_canvas_container canvas', - "should not contain a div with a canvas element"); - assert.containsOnce(graph, 'div.o_view_nocontent', - "should display the no content helper"); graph.destroy(); }); @@ -1259,7 +1275,6 @@ QUnit.module('Views', { assert.checkDatasets(graph, 'data', { data: [4, 3, 1] }); graph.destroy(); - }); QUnit.test('graph view sort by measure for grouped data', async function (assert) { @@ -1301,7 +1316,6 @@ QUnit.module('Views', { assert.checkDatasets(graph, 'data', [{ data: [3, 0, 0] }, { data: [1, 3, 1] }]); graph.destroy(); - }); QUnit.test('graph view sort by measure for multiple grouped data', async function (assert) { @@ -1356,6 +1370,75 @@ QUnit.module('Views', { graph.destroy(); }); + QUnit.test('empty graph view with sample data', async function (assert) { + assert.expect(8); + + const graph = await createView({ + View: GraphView, + model: "foo", + data: this.data, + arch: ` + <graph sample="1"> + <field name="product_id"/> + <field name="date"/> + </graph>`, + domain: [['id', '<', 0]], + viewOptions: { + action: { + help: '<p class="abc">click to add a foo</p>' + } + }, + }); + + assert.hasClass(graph.el, 'o_view_sample_data'); + assert.containsOnce(graph, '.o_view_nocontent'); + assert.containsOnce(graph, '.o_graph_canvas_container canvas'); + assert.hasClass(graph.$('.o_graph_canvas_container'), 'o_sample_data_disabled'); + + await graph.reload({ domain: [] }); + + assert.doesNotHaveClass(graph.el, 'o_view_sample_data'); + assert.containsNone(graph, '.o_view_nocontent'); + assert.containsOnce(graph, '.o_graph_canvas_container canvas'); + assert.doesNotHaveClass(graph.$('.o_graph_canvas_container'), 'o_sample_data_disabled'); + + graph.destroy(); + }); + + QUnit.test('non empty graph view with sample data', async function (assert) { + assert.expect(8); + + const graph = await createView({ + View: GraphView, + model: "foo", + data: this.data, + arch: ` + <graph sample="1"> + <field name="product_id"/> + <field name="date"/> + </graph>`, + viewOptions: { + action: { + help: '<p class="abc">click to add a foo</p>' + } + }, + }) + + assert.doesNotHaveClass(graph.el, 'o_view_sample_data'); + assert.containsNone(graph, '.o_view_nocontent'); + assert.containsOnce(graph, '.o_graph_canvas_container canvas'); + assert.doesNotHaveClass(graph.$('.o_graph_canvas_container'), 'o_sample_data_disabled'); + + await graph.reload({ domain: [['id', '<', 0]] }); + + assert.doesNotHaveClass(graph.el, 'o_view_sample_data'); + assert.containsOnce(graph, '.o_graph_canvas_container canvas'); + assert.doesNotHaveClass(graph.$('.o_graph_canvas_container'), 'o_sample_data_disabled'); + assert.containsNone(graph, '.o_view_nocontent'); + + graph.destroy(); + }); + QUnit.module('GraphView: comparison mode', { beforeEach: async function () { this.data.foo.records[0].date = '2016-12-15'; diff --git a/odoo/addons/base/rng/graph_view.rng b/odoo/addons/base/rng/graph_view.rng index 32639b30d9c..47f547f0c13 100644 --- a/odoo/addons/base/rng/graph_view.rng +++ b/odoo/addons/base/rng/graph_view.rng @@ -25,6 +25,7 @@ <rng:optional><rng:attribute name="orientation"/></rng:optional> <rng:optional><rng:attribute name="interval"/></rng:optional> <rng:optional><rng:attribute name="disable_linking"/></rng:optional> + <rng:optional><rng:attribute name="sample"/></rng:optional> <rng:zeroOrMore> <rng:ref name="field"/> </rng:zeroOrMore> |
