diff options
7 files changed, 44 insertions, 10 deletions
diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js index 07a0109e9a7..cb83301c14d 100644 --- a/addons/web/static/src/js/views/abstract_controller.js +++ b/addons/web/static/src/js/views/abstract_controller.js @@ -559,15 +559,19 @@ var AbstractController = mvc.Controller.extend(ActionMixin, { ev.data.callback(state || {}); }, /** - * Called mainly from the control panel when the focus should be given to - * the controller + * Called either from the control panel to focus the controller + * or from the view to focus the search bar * * @private * @param {OdooEvent} ev */ _onNavigationMove: function (ev) { switch (ev.data.direction) { - case 'down' : + case 'up': + ev.stopPropagation(); + this._controlPanel.focusSearchBar(); + break; + case 'down': ev.stopPropagation(); this.giveFocus(); break; @@ -629,7 +633,6 @@ var AbstractController = mvc.Controller.extend(ActionMixin, { _onSwitchView: function (ev) { ev.data.controllerID = this.controllerID; }, - }); return AbstractController; diff --git a/addons/web/static/src/js/views/control_panel/control_panel_controller.js b/addons/web/static/src/js/views/control_panel/control_panel_controller.js index dc8fce9f5d1..3c23e681631 100644 --- a/addons/web/static/src/js/views/control_panel/control_panel_controller.js +++ b/addons/web/static/src/js/views/control_panel/control_panel_controller.js @@ -55,6 +55,14 @@ var ControlPanelController = mvc.Controller.extend({ return this.model.exportState(); }, /** + * Called by the abstract controller to give focus to the searchbar + */ + focusSearchBar: function () { + if (this.renderer.searchBar) { + this.renderer.searchBar.focus(); + } + }, + /** * Compute the search related values that will be used to fetch data. * * @returns {Object} object with keys 'context', 'domain', 'groupBy' diff --git a/addons/web/static/src/js/views/control_panel/search/search_bar.js b/addons/web/static/src/js/views/control_panel/search/search_bar.js index 6248489922c..d4efab663d4 100644 --- a/addons/web/static/src/js/views/control_panel/search/search_bar.js +++ b/addons/web/static/src/js/views/control_panel/search/search_bar.js @@ -221,6 +221,14 @@ var SearchBar = Widget.extend({ this._focusFollowing(); e.preventDefault(); break; + case $.ui.keyCode.DOWN: + // if the searchbar dropdown is closed, try to focus the renderer + const $dropdown = this.$('.o_searchview_autocomplete:visible'); + if (!$dropdown.length) { + this.trigger_up('navigation_move', { direction: 'down' }); + e.preventDefault(); + } + break; case $.ui.keyCode.BACKSPACE: if (this.$input.val() === '') { this.trigger_up('facet_removed'); diff --git a/addons/web/static/src/js/views/kanban/kanban_renderer.js b/addons/web/static/src/js/views/kanban/kanban_renderer.js index 30feb56cb2c..8f6d7b628f6 100644 --- a/addons/web/static/src/js/views/kanban/kanban_renderer.js +++ b/addons/web/static/src/js/views/kanban/kanban_renderer.js @@ -262,13 +262,17 @@ var KanbanRenderer = BasicRenderer.extend({ } }, /** + * Tries to give focus to the previous card, and returns true if successful + * * @private * @param {DOMElement} currentColumn + * @returns {boolean} */ _focusOnPreviousCard: function (currentCardElement) { var previousCard = currentCardElement.previousElementSibling; if (previousCard && previousCard.classList.contains("o_kanban_record")) { //previous element might be column title previousCard.focus(); + return true; } }, /** @@ -611,7 +615,10 @@ var KanbanRenderer = BasicRenderer.extend({ e.preventDefault(); break; case $.ui.keyCode.UP: - this._focusOnPreviousCard(e.currentTarget); + const previousFocused = this._focusOnPreviousCard(e.currentTarget); + if (!previousFocused) { + this.trigger_up('navigation_move', { direction: 'up' }); + } e.stopPropagation(); e.preventDefault(); break; diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js index 65bae3eea41..71f20878e88 100644 --- a/addons/web/static/src/js/views/list/list_editable_renderer.js +++ b/addons/web/static/src/js/views/list/list_editable_renderer.js @@ -1155,6 +1155,10 @@ ListRenderer.include({ */ _onNavigationMove: function (ev) { var self = this; + // Don't stop the propagation when navigating up while not editing any row + if (this.currentRow === null && ev.data.direction === 'up') { + return; + } ev.stopPropagation(); // stop the event, the action is done by this renderer switch (ev.data.direction) { case 'previous': diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js index 2ddf21b1ef8..5e54b626573 100644 --- a/addons/web/static/src/js/views/list/list_renderer.js +++ b/addons/web/static/src/js/views/list/list_renderer.js @@ -1108,6 +1108,9 @@ var ListRenderer = BasicRenderer.extend({ $cell.closest('tbody').addClass('o_keyboard_navigation'); colIndex = this.currentColIndex || $cell.index(); $futureCell = this._findConnectedCell($cell, 'prev', colIndex); + if (!$futureCell) { + this.trigger_up('navigation_move', { direction: 'up' }); + } break; case $.ui.keyCode.DOWN: ev.preventDefault(); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index 4c1ff4c10ae..48c0502b689 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -5883,14 +5883,15 @@ QUnit.module('Views', { }, }); - assert.ok(document.activeElement.classList.contains('o_searchview_input'), 'default focus should be in search view'); - // switch focus to the create button in tests while it works on live - $('.o_list_button_add').focus(); - assert.ok(document.activeElement.classList.contains('o_list_button_add'), - 'focus should now be in create button'); + assert.ok(document.activeElement.classList.contains('o_searchview_input'), + 'default focus should be in search view'); await testUtils.fields.triggerKeydown($(document.activeElement), 'down'); assert.strictEqual(document.activeElement.tagName, 'INPUT', 'focus should now be on the record selector'); + await testUtils.fields.triggerKeydown($(document.activeElement), 'up'); + assert.ok(document.activeElement.classList.contains('o_searchview_input'), + 'focus should have come back to the search view'); + await testUtils.fields.triggerKeydown($(document.activeElement), 'down'); await testUtils.fields.triggerKeydown($(document.activeElement), 'down'); assert.strictEqual(document.activeElement.tagName, 'INPUT', 'focus should now be in first row input'); |
