summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammed Shekha <msh@odoo.com>2020-04-07 12:32:43 +0000
committerLucas Perais (lpe) <lpe@odoo.com>2020-08-20 12:37:36 +0000
commitce69db55f80777b95015c23dc4b3fa1a6db5daad (patch)
tree66675d808acc37302397eb6b28df6bed20c50479
parent538b7f22cfdd39ce3dea66b36b46ced2de7ceaed (diff)
[IMP] web: set focus to first visible page when active page invisible
Before this commit: if there are modifiers based on field which hides one page and show another and if active page get invisible then instead of first page second page gets focus, consider following example, `<form> <field name="boolean"/> <page name="page1" attrs='{"invisible": [["bar", "=", false]]}'></page> <page name="page2" attrs='{"invisible": [["bar", "=", true]]}'></page> <page name="page3"></page> </form>` Consider the initial boolean value to be true; initially the first page will be visible. But when "bar" is unchecked then page1 will be hidden and page3 will get the focus as page2 is still not visible: it will be after the computaion/application of all modifiers, which is currently done after the rendering of a tab page. Now: the first visible tab is focused on rendering (same as before) but also after all modifiers have been updated. closes odoo/odoo#56209 X-original-commit: 10365572aa3629e9b19d2b29726b351275de9f52 Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com> Co-authored-by: Ipsita Borisagar <ibo@odoo.com> Co-authored-by: Julien Mougenot <jum@odoo.com>
-rw-r--r--addons/web/static/src/js/views/form/form_renderer.js51
-rw-r--r--addons/web/static/tests/views/form_tests.js51
2 files changed, 90 insertions, 12 deletions
diff --git a/addons/web/static/src/js/views/form/form_renderer.js b/addons/web/static/src/js/views/form/form_renderer.js
index 2b91c5820ee..97e3cbd8cb0 100644
--- a/addons/web/static/src/js/views/form/form_renderer.js
+++ b/addons/web/static/src/js/views/form/form_renderer.js
@@ -303,6 +303,28 @@ var FormRenderer = BasicRenderer.extend({
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
+
+ /**
+ * Activates the first visible tab from a given list of tab objects. The
+ * first tab having an "autofocus" attribute set will be focused in
+ * priority.
+ *
+ * @private
+ * @param {Object[]} tabs
+ */
+ _activateFirstVisibleTab(tabs) {
+ const visibleTabs = tabs.filter(
+ (tab) => !tab.$header.hasClass("o_invisible_modifier")
+ );
+ const autofocusTab = visibleTabs.findIndex(
+ (tab) => tab.node.attrs.autofocus === "autofocus"
+ );
+ const tabToFocus = visibleTabs[Math.max(0, autofocusTab)];
+ if (tabToFocus) {
+ tabToFocus.$header.find('.nav-link').addClass('active');
+ tabToFocus.$page.addClass('active');
+ }
+ },
/**
* @override
*/
@@ -932,7 +954,6 @@ var FormRenderer = BasicRenderer.extend({
var self = this;
var $headers = $('<ul class="nav nav-tabs">');
var $pages = $('<div class="tab-content">');
- var autofocusTab = -1;
// renderedTabs is used to aggregate the generated $headers and $pages
// alongside their node, so that their modifiers can be registered once
// all tabs have been rendered, to ensure that the first visible tab
@@ -941,9 +962,6 @@ var FormRenderer = BasicRenderer.extend({
var pageID = _.uniqueId('notebook_page_');
var $header = self._renderTabHeader(child, pageID);
var $page = self._renderTabPage(child, pageID);
- if (autofocusTab === -1 && child.attrs.autofocus === 'autofocus') {
- autofocusTab = index;
- }
self._handleAttributes($header, child);
$headers.append($header);
$pages.append($page);
@@ -953,11 +971,6 @@ var FormRenderer = BasicRenderer.extend({
node: child,
};
});
- if (renderedTabs.length) {
- var tabToFocus = renderedTabs[Math.max(0, autofocusTab)];
- tabToFocus.$header.find('.nav-link').addClass('active');
- tabToFocus.$page.addClass('active');
- }
// register the modifiers for each tab
_.each(renderedTabs, function (tab) {
self._registerModifiers(tab.node, self.state, tab.$header, {
@@ -967,13 +980,12 @@ var FormRenderer = BasicRenderer.extend({
if (modifiers.invisible && $link.hasClass('active')) {
$link.removeClass('active');
tab.$page.removeClass('active');
- var $firstVisibleTab = $headers.find('li:not(.o_invisible_modifier):first() > a');
- $firstVisibleTab.addClass('active');
- $pages.find($firstVisibleTab.attr('href')).addClass('active');
+ self.inactiveNotebooks.push(renderedTabs);
}
},
});
});
+ this._activateFirstVisibleTab(renderedTabs);
var $notebookHeaders = $('<div class="o_notebook_headers">').append($headers);
var $notebook = $('<div class="o_notebook">').append($notebookHeaders, $pages);
$notebook[0].dataset.name = node.attrs.name || '_default_';
@@ -1028,6 +1040,7 @@ var FormRenderer = BasicRenderer.extend({
// render the form and evaluate the modifiers
var defs = [];
this.defs = defs;
+ this.inactiveNotebooks = [];
var $form = this._renderNode(this.arch).addClass(this.className);
delete this.defs;
@@ -1051,6 +1064,20 @@ var FormRenderer = BasicRenderer.extend({
});
},
/**
+ * This method is overridden to activate the first notebook page if the
+ * current active page is invisible due to modifiers. This is done after
+ * all modifiers are applied on all page elements.
+ *
+ * @override
+ */
+ async _updateAllModifiers() {
+ await this._super(...arguments);
+ for (const tabs of this.inactiveNotebooks) {
+ this._activateFirstVisibleTab(tabs);
+ }
+ this.inactiveNotebooks = [];
+ },
+ /**
* Updates the form's $el with new content.
*
* @private
diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js
index 0dd958b6e17..8bbdae1e91f 100644
--- a/addons/web/static/tests/views/form_tests.js
+++ b/addons/web/static/tests/views/form_tests.js
@@ -785,6 +785,57 @@ QUnit.module('Views', {
form.destroy();
});
+ QUnit.test('notebook: pages with invisible modifiers', async function (assert) {
+ assert.expect(10);
+
+ const form = await createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: `<form string="Partners">
+ <sheet>
+ <field name="bar"/>
+ <notebook>
+ <page string="First" attrs='{"invisible": [["bar", "=", false]]}'>
+ <field name="foo"/>
+ </page>
+ <page string="Second" attrs='{"invisible": [["bar", "=", true]]}'>
+ <field name="int_field"/>
+ </page>
+ <page string="Third">
+ <field name="qux"/>
+ </page>
+ </notebook>
+ </sheet>
+ </form>`,
+ res_id: 1,
+ });
+
+ await testUtils.form.clickEdit(form);
+
+ assert.containsOnce(form, ".o_notebook .nav .nav-link.active",
+ "There should be only one active tab"
+ );
+ assert.isVisible(form.$(".o_notebook .nav .nav-item:first"));
+ assert.hasClass(form.$(".o_notebook .nav .nav-link:first"), "active");
+
+ assert.isNotVisible(form.$(".o_notebook .nav .nav-item:eq(1)"));
+ assert.doesNotHaveClass(form.$(".o_notebook .nav .nav-link:eq(1)"), "active");
+
+ await testUtils.dom.click(form.$(".o_field_widget[name=bar] input"));
+
+ assert.containsOnce(form, ".o_notebook .nav .nav-link.active",
+ "There should be only one active tab"
+ );
+ assert.isNotVisible(form.$(".o_notebook .nav .nav-item:first"));
+ assert.doesNotHaveClass(form.$(".o_notebook .nav .nav-link:first"), "active");
+
+ assert.isVisible(form.$(".o_notebook .nav .nav-item:eq(1)"));
+ assert.hasClass(form.$(".o_notebook .nav .nav-link:eq(1)"), "active");
+
+ form.destroy();
+ });
+
QUnit.test('invisible attrs on first notebook page', async function (assert) {
assert.expect(6);