From e248773826f52ed3297e6af016446b6027a8126e Mon Sep 17 00:00:00 2001 From: Bruno Boi Date: Tue, 29 Jun 2021 13:45:38 +0000 Subject: [PATCH] [FIX] web: various filtering kanban column scenarii BEFORE Various scenarii leads to inconsistencies on the progressbars when a filter is activated: - when dropping in a non matching record - when the view domain is updated (e.g. from some input in the search panel) NOW These scenarii works fine. Taskid-2491196 Taskid-2454209 --- .../views/kanban/kanban_column_progressbar.js | 2 +- .../legacy/js/views/kanban/kanban_model.js | 13 +- .../static/tests/legacy/views/kanban_tests.js | 286 ++++++++++++++++++ 3 files changed, 299 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/legacy/js/views/kanban/kanban_column_progressbar.js b/addons/web/static/src/legacy/js/views/kanban/kanban_column_progressbar.js index abf9a7136e8..bc9ac922bd1 100644 --- a/addons/web/static/src/legacy/js/views/kanban/kanban_column_progressbar.js +++ b/addons/web/static/src/legacy/js/views/kanban/kanban_column_progressbar.js @@ -43,7 +43,7 @@ var KanbanColumnProgressBar = Widget.extend({ this.groupCount = state.groupCount; this.subgroupCounts = state.subgroupCounts; this.totalCounterValue = state.totalCounterValue; - this.activeFilter = state.activeFilter; + this.activeFilter = columnState.activeFilter || state.activeFilter; } // Prepare currency (TODO this should be automatic... use a field ?) diff --git a/addons/web/static/src/legacy/js/views/kanban/kanban_model.js b/addons/web/static/src/legacy/js/views/kanban/kanban_model.js index 3fdde47ea34..a6d2703fead 100644 --- a/addons/web/static/src/legacy/js/views/kanban/kanban_model.js +++ b/addons/web/static/src/legacy/js/views/kanban/kanban_model.js @@ -167,6 +167,8 @@ var KanbanModel = BasicModel.extend({ } else { result.isGroupedByM2ONoColumn = false; } + // Keep activeFilter + result.activeFilter = dp.activeFilter; // Add progressBarValues, loadMoreCount and loadMoreOffset key let loadMoreCount = result.count - result.data.length; let loadMoreOffset = result.data.length; @@ -264,6 +266,11 @@ var KanbanModel = BasicModel.extend({ if (index >= 0) { old_group.data.splice(index, 1); old_group.count--; + if (!old_group.activeFilter || old_group.activeFilter.value === record.data[parent.progressBar.field]) { + // Here, the record leaving the old group matches its domain, + // so we must decrease the domainCount too. + old_group.domainCount--; + } old_group.res_ids = _.without(old_group.res_ids, resID); self._updateParentResIDs(old_group); break; @@ -355,6 +362,10 @@ var KanbanModel = BasicModel.extend({ if (params.progressBar) { dataPoint.progressBar = params.progressBar; } + // In Kanban view, we sometimes drag into a group records that are + // outside of its domain. Here we make sure to remember the initial + // domain count. This is useful for e.g. progressbars computations. + dataPoint.domainCount = dataPoint.count; return dataPoint; }, /** @@ -403,7 +414,7 @@ var KanbanModel = BasicModel.extend({ // Compute records count for progressbar field values // not specified in the progressbar attributes const counts = Object.assign({ - __false: group.count - valuesCountTotal + __false: group.domainCount - valuesCountTotal }, valuesCount); group.progressBarValues = Object.assign({ diff --git a/addons/web/static/tests/legacy/views/kanban_tests.js b/addons/web/static/tests/legacy/views/kanban_tests.js index 697330e7e9a..a8cc0afc07b 100644 --- a/addons/web/static/tests/legacy/views/kanban_tests.js +++ b/addons/web/static/tests/legacy/views/kanban_tests.js @@ -7803,6 +7803,292 @@ QUnit.module('Views', { kanban.destroy(); }); + + QUnit.test("progressbar filter state is kept unchanged when domain is updated (records still in group)", async function (assert) { + assert.expect(16); + + const kanban = await createView({ + View: KanbanView, + model: "partner", + data: this.data, + arch: ` + + + + + + +
+ + +
+
+
+
+ `, + }); + + // Check that we have 2 columns and check their progressbar's state + assert.containsN(kanban.el, ".o_kanban_group", 2); + assert.containsNone(kanban.el, ".o_kanban_group.o_kanban_group_show"); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_column_title")].map(el => el.innerText), + ["false", "true"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["0 yop", "1 blip", "0 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["1 yop", "1 blip", "1 __false"], + ); + + // Apply an active filter + await testUtils.dom.click(kanban.el.querySelector(".o_kanban_group:nth-child(2) .progress-bar[data-filter=yop]")); + assert.containsOnce(kanban.el, ".o_kanban_group.o_kanban_group_show"); + assert.strictEqual(kanban.el.querySelector(".o_kanban_group.o_kanban_group_show .o_column_title").innerText, "true"); + + // Add searchdomain to something restricting progressbars' values (records still in filtered group) + await kanban.update({ domain: [["foo", "=", "yop"]] }); + + // Check that we have now 1 column only and check its progressbar's state + assert.containsOnce(kanban.el, ".o_kanban_group"); + assert.containsOnce(kanban.el, ".o_kanban_group.o_kanban_group_show"); + assert.strictEqual(kanban.el.querySelector(".o_column_title").innerText, "true"); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["1 yop", "0 blip", "0 __false"], + ); + + // Undo searchdomain + await kanban.update({ domain: [] }); + + // Check that we have 2 columns back and check their progressbar's state + assert.containsN(kanban.el, ".o_kanban_group", 2); + assert.containsOnce(kanban.el, ".o_kanban_group.o_kanban_group_show"); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_column_title")].map(el => el.innerText), + ["false", "true"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["0 yop", "1 blip", "0 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["1 yop", "1 blip", "1 __false"], + ); + + kanban.destroy(); + }); + + QUnit.test("progressbar filter state is kept unchanged when domain is updated (emptying group)", async function (assert) { + assert.expect(25); + + const kanban = await createView({ + View: KanbanView, + model: "partner", + data: this.data, + arch: ` + + + + + + +
+ + +
+
+
+
+ `, + }); + + // Check that we have 2 columns, check their progressbar's state and check records + assert.containsN(kanban.el, ".o_kanban_group", 2); + assert.containsNone(kanban.el, ".o_kanban_group.o_kanban_group_show"); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_column_title")].map(el => el.innerText), + ["false", "true"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["0 yop", "1 blip", "0 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_record")].map(el => el.innerText), + ["4 blip"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["1 yop", "1 blip", "1 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_record")].map(el => el.innerText), + ["1 yop", "2 blip", "3 gnap"], + ); + + // Apply an active filter + await testUtils.dom.click(kanban.el.querySelector(".o_kanban_group:nth-child(2) .progress-bar[data-filter=yop]")); + assert.containsOnce(kanban.el, ".o_kanban_group.o_kanban_group_show"); + assert.strictEqual(kanban.el.querySelector(".o_kanban_group.o_kanban_group_show .o_column_title").innerText, "true"); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group.o_kanban_group_show .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["1 yop", "1 blip", "1 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group.o_kanban_group_show .o_kanban_record")].map(el => el.innerText), + ["1 yop"], + ); + + // Add searchdomain to something restricting progressbars' values + emptying the filtered group + await kanban.update({ domain: [["foo", "=", "blip"]] }); + + // Check that we still have 2 columns, check their progressbar's state and check records + assert.containsN(kanban.el, ".o_kanban_group", 2); + assert.containsNone(kanban.el, ".o_kanban_group.o_kanban_group_show"); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_column_title")].map(el => el.innerText), + ["false", "true"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["0 yop", "1 blip", "0 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_record")].map(el => el.innerText), + ["4 blip"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["0 yop", "1 blip", "0 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_record")].map(el => el.innerText), + [], + ); + + // Undo searchdomain + await kanban.update({ domain: [] }); + + // Check that we still have 2 columns and check their progressbar's state + assert.containsN(kanban.el, ".o_kanban_group", 2); + assert.containsOnce(kanban.el, ".o_kanban_group.o_kanban_group_show"); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_column_title")].map(el => el.innerText), + ["false", "true"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["0 yop", "1 blip", "0 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_record")].map(el => el.innerText), + ["4 blip"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["1 yop", "1 blip", "1 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_record")].map(el => el.innerText), + ["1 yop"], + ); + + kanban.destroy(); + }); + + QUnit.test("filtered column keeps consistent counters when dropping in a non-matching record", async function (assert) { + assert.expect(19); + + const kanban = await createView({ + View: KanbanView, + model: "partner", + data: this.data, + arch: ` + + + + + + +
+ + +
+
+
+
+ `, + }); + + // Check that we have 2 columns, check their progressbar's state, and check records + assert.containsN(kanban.el, ".o_kanban_group", 2); + assert.containsNone(kanban.el, ".o_kanban_group.o_kanban_group_show"); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_column_title")].map(el => el.innerText), + ["false", "true"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["0 yop", "1 blip", "0 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_record")].map(el => el.innerText), + ["4 blip"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["1 yop", "1 blip", "1 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_record")].map(el => el.innerText), + ["1 yop", "2 blip", "3 gnap"], + ); + + // Apply an active filter + await testUtils.dom.click(kanban.el.querySelector(".o_kanban_group:nth-child(2) .progress-bar[data-filter=yop]")); + assert.hasClass(kanban.el.querySelector(".o_kanban_group:nth-child(2)"), "o_kanban_group_show"); + assert.containsOnce(kanban.el, ".o_kanban_group.o_kanban_group_show"); + assert.strictEqual(kanban.el.querySelector(".o_kanban_group.o_kanban_group_show .o_column_title").innerText, "true"); + assert.containsOnce(kanban.el, ".o_kanban_group.o_kanban_group_show .o_kanban_record"); + assert.strictEqual(kanban.el.querySelector(".o_kanban_group.o_kanban_group_show .o_kanban_record").innerText, "1 yop"); + + // Drop in the non-matching record from first column + await testUtils.dom.dragAndDrop( + kanban.el.querySelector(".o_kanban_group:nth-child(1) .o_kanban_record"), + kanban.el.querySelector(".o_kanban_group.o_kanban_group_show") + ); + + // Check that we have 2 columns, check their progressbar's state, and check records + assert.containsN(kanban.el, ".o_kanban_group", 2); + assert.containsOnce(kanban.el, ".o_kanban_group.o_kanban_group_show"); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_column_title")].map(el => el.innerText), + ["false", "true"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["0 yop", "0 blip", "0 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(1) .o_kanban_record")].map(el => el.innerText), + [], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_counter .progress-bar")].map(el => el.dataset.originalTitle), + ["1 yop", "2 blip", "1 __false"], + ); + assert.deepEqual( + [...kanban.el.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_record")].map(el => el.innerText), + ["1 yop", "4 blip"], + ); + + kanban.destroy(); + }); }); });