[FIX] web: properly handle tab navigation in readonly

The tab navigation was only working in edit mode. Entering a readonly form
view with input fields was causing a traceback.

As the default browser behaviour has been overriden with custom navigation,
we need to specify which widget can be focused (e.g.: url, many2one, etc.).
This commit is contained in:
Martin Geubelle
2017-06-19 16:47:34 +02:00
parent 21fca784dc
commit c5f8f35747
4 changed files with 89 additions and 6 deletions
@@ -285,11 +285,15 @@ var InputField = DebouncedField.extend({
*/
_onNavigationMove: function (ev) {
this._super.apply(this, arguments);
var input = this.$input[0];
var selecting = (input.selectionEnd !== input.selectionStart);
if ((ev.data.direction === "left" && (selecting || input.selectionStart !== 0))
|| (ev.data.direction === "right" && (selecting || input.selectionStart !== input.value.length))) {
ev.stopPropagation();
// the following code only makes sense in edit mode, with an input
if (this.mode === 'edit') {
var input = this.$input[0];
var selecting = (input.selectionEnd !== input.selectionStart);
if ((ev.data.direction === "left" && (selecting || input.selectionStart !== 0))
|| (ev.data.direction === "right" && (selecting || input.selectionStart !== input.value.length))) {
ev.stopPropagation();
}
}
},
});
@@ -814,6 +818,19 @@ var FieldEmail = InputField.extend({
this.tagName = this.mode === 'readonly' ? 'a' : 'input';
},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* Returns the associated link.
*
* @override
*/
getFocusableElement: function () {
return this.mode === 'readonly' && this.$el || $();
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
@@ -849,6 +866,17 @@ var FieldPhone = FieldEmail.extend({
this.tagName = 'span';
}
},
/**
* Returns the associated link only if there is one.
*
* @override
*/
getFocusableElement: function () {
if (this._canCall()) {
return this._super.apply(this, arguments);
}
return $();
},
//--------------------------------------------------------------------------
// Private
@@ -902,6 +930,19 @@ var UrlWidget = InputField.extend({
this.tagName = this.mode === 'readonly' ? 'a' : 'input';
},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* Returns the associated link.
*
* @override
*/
getFocusableElement: function () {
return this.mode === 'readonly' && this.$el || $();
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
@@ -124,7 +124,7 @@ var FieldMany2One = AbstractField.extend({
* @returns {jQuery}
*/
getFocusableElement: function () {
return this.$input || $();
return this.mode === 'edit' && this.$input || this.$el;
},
/**
* TODO
@@ -828,6 +828,7 @@ var FormRenderer = BasicRenderer.extend({
*/
_onNavigationMove: function (ev) {
ev.stopPropagation();
var index;
if (ev.data.direction === "next") {
index = this.allFieldWidgets[this.state.id].indexOf(ev.data.target);
@@ -3278,6 +3278,47 @@ QUnit.module('Views', {
form.destroy();
});
QUnit.test('navigation with tab key in readonly form view', function (assert) {
assert.expect(3);
this.data.partner.records[1].product_id = 37;
var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form string="Partners">' +
'<sheet>' +
'<group>' +
'<field name="trululu"/>' +
'<field name="foo"/>' +
'<field name="product_id"/>' +
'<field name="foo" widget="phone"/>' +
'<field name="display_name" widget="url"/>' +
'</group>' +
'</sheet>' +
'</form>',
res_id: 2,
});
// focus first field, trigger tab
form.$('[name="trululu"]').focus();
form.$('[name="trululu"]').trigger($.Event('keydown', {which: $.ui.keyCode.TAB}));
assert.strictEqual(form.$('[name="product_id"]')[0], document.activeElement,
"product_id should be focused");
form.$('[name="product_id"]').trigger($.Event('keydown', {which: $.ui.keyCode.TAB}));
assert.strictEqual(form.$('[name="display_name"]')[0], document.activeElement,
"display_name should be focused (emails are focusable but phone aren't)");
// simulate shift+tab on active element
$(document.activeElement).trigger($.Event('keydown', {which: $.ui.keyCode.TAB, shiftKey: true}));
$(document.activeElement).trigger($.Event('keydown', {which: $.ui.keyCode.TAB, shiftKey: true}));
assert.strictEqual(document.activeElement, form.$('[name="trululu"]')[0],
"first many2one should be focused");
form.destroy();
});
QUnit.test('skip invisible fields when navigating with TAB', function (assert) {
assert.expect(1);