From 015958dc1730296e216a035f3b12b92df2dbd6c8 Mon Sep 17 00:00:00 2001 From: Martin Geubelle Date: Wed, 13 Mar 2019 16:40:08 +0000 Subject: [PATCH] [IMP] web,base: put buttons in list group headers It is now possible to put buttons in the list view group headers. When the view is grouped by a many2one field, those buttons appear next to the header title when the group is opened. The buttons are specified in the views in a tag in the list arch, with the following structure: -
+
/ diff --git a/addons/web/static/tests/helpers/mock_server.js b/addons/web/static/tests/helpers/mock_server.js index d2060de9f59..0eb54bd3e0b 100644 --- a/addons/web/static/tests/helpers/mock_server.js +++ b/addons/web/static/tests/helpers/mock_server.js @@ -203,6 +203,7 @@ var MockServer = Class.extend({ var modifiersNames = ['invisible', 'readonly', 'required']; var onchanges = this.data[model].onchanges || {}; var fieldNodes = {}; + var groupbyNodes = {}; var doc; if (typeof arch === 'string') { @@ -220,6 +221,7 @@ var MockServer = Class.extend({ var modifiers = {}; var isField = (node.tagName === 'field'); + var isGroupby = (node.tagName === 'groupby'); if (isField) { var fieldName = node.getAttribute('name'); @@ -251,6 +253,10 @@ var MockServer = Class.extend({ modifiers[attr] = defaultValue; } }); + } else if (isGroupby && !node._isProcessed) { + var groupbyName = node.getAttribute('name'); + fieldNodes[groupbyName] = node; + groupbyNodes[groupbyName] = node; } // 'transfer_node_to_modifiers' simulation @@ -290,6 +296,10 @@ var MockServer = Class.extend({ node.setAttribute('modifiers', JSON.stringify(modifiers)); } + if (isGroupby && !node._isProcessed) { + return false; + } + return !isField; }); @@ -317,6 +327,21 @@ var MockServer = Class.extend({ node.setAttribute('on_change', "1"); } }); + _.each(groupbyNodes, function (node, name) { + var field = fields[name]; + if (field.type !== 'many2one') { + throw new Error('groupby can only target many2one'); + } + field.views = {}; + relModel = field.relation; + relFields = $.extend(true, {}, self.data[relModel].fields); + node._isProcessed = true; + // postprocess simulation + field.views.groupby = self._fieldsViewGet(node, relModel, relFields, context); + node.childNodes.forEach(function (child) { + node.removeChild(child); + }); + }); var xmlSerializer = new XMLSerializer(); var processedArch = xmlSerializer.serializeToString(doc); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index fbed65758b5..cb6a1ef805d 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -355,7 +355,7 @@ QUnit.module('Views', { }); QUnit.test('basic grouped list rendering 1 col without selector', async function (assert) { - assert.expect(1); + assert.expect(2); var list = await createView({ View: ListView, @@ -367,12 +367,14 @@ QUnit.module('Views', { }); assert.strictEqual(list.$('.o_group_header:first').children().length, 1, - "group header should have exactly 1 column"); + "group header should have exactly 1 column"); + assert.strictEqual(list.$('.o_group_header:first th').attr('colspan'), "1", + "the header should span the whole table"); list.destroy(); }); QUnit.test('basic grouped list rendering 1 col with selector', async function (assert) { - assert.expect(1); + assert.expect(2); var list = await createView({ View: ListView, @@ -385,11 +387,13 @@ QUnit.module('Views', { assert.strictEqual(list.$('.o_group_header:first').children().length, 1, "group header should have exactly 1 column"); + assert.strictEqual(list.$('.o_group_header:first th').attr('colspan'), "2", + "the header should span the whole table"); list.destroy(); }); - QUnit.test('basic grouped list rendering 2 col without selector', async function (assert) { - assert.expect(1); + QUnit.test('basic grouped list rendering 2 cols without selector', async function (assert) { + assert.expect(2); var list = await createView({ View: ListView, @@ -400,13 +404,15 @@ QUnit.module('Views', { hasSelectors: false, }); - assert.strictEqual(list.$('.o_group_header:first').children().length, 2, - "group header should have exactly 2 column"); + assert.strictEqual(list.$('.o_group_header:first').children().length, 1, + "group header should have exactly 1 column"); + assert.strictEqual(list.$('.o_group_header:first th').attr('colspan'), "2", + "the header should span the whole table"); list.destroy(); }); QUnit.test('basic grouped list rendering 2 col with selector', async function (assert) { - assert.expect(1); + assert.expect(2); var list = await createView({ View: ListView, @@ -417,8 +423,40 @@ QUnit.module('Views', { hasSelectors: true, }); - assert.strictEqual(list.$('.o_group_header:first').children().length, 2, - "group header should have exactly 2 column"); + assert.strictEqual(list.$('.o_group_header:first').children().length, 1, + "group header should have exactly 1 column"); + assert.strictEqual(list.$('.o_group_header:first th').attr('colspan'), "3", + "the header should span the whole table"); + list.destroy(); + }); + + QUnit.test('basic grouped list rendering 7 cols with aggregates and selector', async function (assert) { + assert.expect(4); + + var list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '', + groupBy: ['bar'], + }); + + assert.strictEqual(list.$('.o_group_header:first').children().length, 5, + "group header should have exactly 5 columns (one before first aggregate, one after last aggregate, and all in between"); + assert.strictEqual(list.$('.o_group_header:first th').attr('colspan'), "3", + "header name should span on the two first fields + selector (colspan 3)"); + assert.containsN(list, '.o_group_header:first td', 3, + "there should be 3 tds (aggregates + fields in between)"); + assert.strictEqual(list.$('.o_group_header:first th:last').attr('colspan'), "2", + "header last cell should span on the two last fields (to give space for the pager) (colspan 2)"); list.destroy(); }); @@ -1092,7 +1130,6 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('properly apply onchange in simple case', async function (assert) { assert.expect(2); @@ -1721,6 +1758,159 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('groupby node with a button', async function (assert) { + assert.expect(14); + + var list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '' + + '' + + '' + + '