From f731ab730bb74be12d5bf5e8658de04992fcb4fe Mon Sep 17 00:00:00 2001 From: Harshal Dhumal Date: Tue, 22 Jan 2019 16:28:32 +0530 Subject: [PATCH] Fixed keyboard navigation for dialog tabs. Fixes #3862 --- docs/en_US/release_notes_4_2.rst | 1 + web/pgadmin/browser/static/js/keyboard.js | 4 +- web/pgadmin/browser/static/js/node.js | 21 ++-- web/pgadmin/static/js/dialog_tab_navigator.js | 22 ++-- .../import_export/static/js/import_export.js | 5 +- .../javascript/dialog_tab_navigator_spec.js | 102 ++++++++++++++++-- 6 files changed, 124 insertions(+), 31 deletions(-) diff --git a/docs/en_US/release_notes_4_2.rst b/docs/en_US/release_notes_4_2.rst index 7b6cd5382..3f88e6e00 100644 --- a/docs/en_US/release_notes_4_2.rst +++ b/docs/en_US/release_notes_4_2.rst @@ -27,6 +27,7 @@ Bug fixes | `Bug #3840 `_ - Ensure that file format combo box value should be retained when hidden files checkbox is toggled. | `Bug #3846 `_ - Proper SQL should be generated when create procedure with custom type arguments. | `Bug #3858 `_ - Drop-down should be closed when click on any other toolbar button. +| `Bug #3862 `_ - Fixed keyboard navigation for dialog tabs. | `Bug #3871 `_ - Fixed alignment of tree arrow icons for Internet Explorer. | `Bug #3891 `_ - Correct order of Save and Cancel button for json/jsonb editing. | `Bug #3897 `_ - Data should be updated properly for FTS Configurations, FTS Dictionaries, FTS Parsers and FTS Templates. \ No newline at end of file diff --git a/web/pgadmin/browser/static/js/keyboard.js b/web/pgadmin/browser/static/js/keyboard.js index 79266bf5e..4d9b6ae72 100644 --- a/web/pgadmin/browser/static/js/keyboard.js +++ b/web/pgadmin/browser/static/js/keyboard.js @@ -349,11 +349,11 @@ _.extend(pgBrowser.keyboardNavigation, { d: selectedTreeNodeData, }; }, - getDialogTabNavigator: function(dialog) { + getDialogTabNavigator: function(dialogContainer) { const backward_shortcut = pgBrowser.get_preference('browser', 'dialog_tab_backward').value; const forward_shortcut = pgBrowser.get_preference('browser', 'dialog_tab_forward').value; - return new dialogTabNavigator.dialogTabNavigator(dialog, backward_shortcut, forward_shortcut); + return new dialogTabNavigator.dialogTabNavigator(dialogContainer, backward_shortcut, forward_shortcut); }, }); diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js index f5cb8cd02..30605ddc6 100644 --- a/web/pgadmin/browser/static/js/node.js +++ b/web/pgadmin/browser/static/js/node.js @@ -412,8 +412,6 @@ define('pgadmin.browser.node', [ view.render(); setFocusOnEl(); newModel.startNewSession(); - // var dialogTabNavigator = pgBrowser.keyboardNavigation.getDialogTabNavigator(view); - pgBrowser.keyboardNavigation.getDialogTabNavigator(view); }, error: function(xhr, error, message) { var _label = that && item ? @@ -450,8 +448,6 @@ define('pgadmin.browser.node', [ view.render(); setFocusOnEl(); newModel.startNewSession(); - // var dialogTabNavigator = pgBrowser.keyboardNavigation.getDialogTabNavigator(view); - pgBrowser.keyboardNavigation.getDialogTabNavigator(view); } } @@ -1083,7 +1079,7 @@ define('pgadmin.browser.node', [ // All buttons will be created within a single // div area. var btnGroup = - $('
').addClass( + $('
').addClass( 'pg-prop-btn-group' ), // Template used for creating a button @@ -1200,7 +1196,6 @@ define('pgadmin.browser.node', [ }); }, }); - createButtons(buttons, 'header', 'pg-prop-btn-group-above'); } j.append(content); @@ -1392,7 +1387,7 @@ define('pgadmin.browser.node', [ ); // Create proper buttons - createButtons([{ + let btn_grp = createButtons([{ label: '', type: 'help', tooltip: gettext('SQL help for this object type.'), @@ -1458,6 +1453,18 @@ define('pgadmin.browser.node', [ }); }, }], 'footer', 'pg-prop-btn-group-below'); + + btn_grp.on('keydown', 'button', function(event) { + if (event.keyCode == 9 && $(this).nextAll('button:not([disabled])').length == 0) { + // set focus back to first editable input element of current active tab once we cycle through all enabled buttons. + commonUtils.findAndSetFocus(view.$el.find('.tab-content div.active')); + return false; + } + }); + + setTimeout(function() { + pgBrowser.keyboardNavigation.getDialogTabNavigator(panel.pgElContainer); + }, 200); } // Create status bar. diff --git a/web/pgadmin/static/js/dialog_tab_navigator.js b/web/pgadmin/static/js/dialog_tab_navigator.js index 19b204556..44721725d 100644 --- a/web/pgadmin/static/js/dialog_tab_navigator.js +++ b/web/pgadmin/static/js/dialog_tab_navigator.js @@ -13,13 +13,13 @@ import { findAndSetFocus } from './utils'; import { parseShortcutValue } from './utils'; class dialogTabNavigator { - constructor(dialog, backwardShortcut, forwardShortcut) { + constructor(dialogContainer, backwardShortcut, forwardShortcut) { - this.dialog = dialog; + this.dialogContainer = dialogContainer; this.tabSwitching = false; - this.tabs = this.dialog.$el.find('.nav-tabs'); + this.tabs = this.dialogContainer.find('.nav-tabs'); if (this.tabs.length > 0 ) { this.tabs = this.tabs[0]; @@ -28,13 +28,13 @@ class dialogTabNavigator { this.dialogTabBackward = parseShortcutValue(backwardShortcut); this.dialogTabForward = parseShortcutValue(forwardShortcut); - Mousetrap(this.dialog.el).bind(this.dialogTabBackward, this.onKeyboardEvent.bind(this)); - Mousetrap(this.dialog.el).bind(this.dialogTabForward, this.onKeyboardEvent.bind(this)); + Mousetrap(this.dialogContainer[0]).bind(this.dialogTabBackward, this.onKeyboardEvent.bind(this)); + Mousetrap(this.dialogContainer[0]).bind(this.dialogTabForward, this.onKeyboardEvent.bind(this)); } onKeyboardEvent(event, shortcut) { - var currentTabPane = this.dialog.$el + var currentTabPane = this.dialogContainer .find('.tab-content:first > .tab-pane.active:first'), childTabData = this.isActivePaneHasChildTabs(currentTabPane); @@ -86,7 +86,7 @@ class dialogTabNavigator { var self = this, nextTabPane, innerTabContainer, - prevtab = $(tabs).find('li.active').prev('li'); + prevtab = $(tabs).find('li').has('a.active').prev('li'); if (prevtab.length > 0) { prevtab.find('a').tab('show'); @@ -116,7 +116,7 @@ class dialogTabNavigator { var self = this, nextTabPane, innerTabContainer, - nexttab = $(tabs).find('li.active').next('li'); + nexttab = $(tabs).find('li').has('a.active').next('li'); if(nexttab.length > 0) { nexttab.find('a').tab('show'); @@ -142,11 +142,11 @@ class dialogTabNavigator { } detach() { - Mousetrap(this.dialog.el).unbind(this.dialogTabBackward); - Mousetrap(this.dialog.el).unbind(this.dialogTabForward); + Mousetrap(this.dialogContainer[0]).unbind(this.dialogTabBackward); + Mousetrap(this.dialogContainer[0]).unbind(this.dialogTabForward); } } module.exports = { dialogTabNavigator: dialogTabNavigator, -}; \ No newline at end of file +}; diff --git a/web/pgadmin/tools/import_export/static/js/import_export.js b/web/pgadmin/tools/import_export/static/js/import_export.js index e3957c2fe..53c788bac 100644 --- a/web/pgadmin/tools/import_export/static/js/import_export.js +++ b/web/pgadmin/tools/import_export/static/js/import_export.js @@ -657,10 +657,11 @@ Backform, commonUtils, supportedNodes }); view.$el.attr('tabindex', -1); - // var dialogTabNavigator = pgBrowser.keyboardNavigation.getDialogTabNavigator(view); - pgBrowser.keyboardNavigation.getDialogTabNavigator(view); var container = view.$el.find('.tab-content:first > .tab-pane.active:first'); commonUtils.findAndSetFocus(container); + setTimeout(function() { + pgBrowser.keyboardNavigation.getDialogTabNavigator($(self.elements.dialog)); + }, 200); }, }; }); diff --git a/web/regression/javascript/dialog_tab_navigator_spec.js b/web/regression/javascript/dialog_tab_navigator_spec.js index f355e8848..3c6301a49 100644 --- a/web/regression/javascript/dialog_tab_navigator_spec.js +++ b/web/regression/javascript/dialog_tab_navigator_spec.js @@ -14,10 +14,10 @@ describe('dialogTabNavigator', function () { let dialog, tabNavigator, backward_shortcut, forward_shortcut; beforeEach(() => { - let dialogHtml =$('
'+ + dialog = $('
'+ ' '+ '
'); - dialog = {}; - - dialog.el = dialogHtml[0]; - dialog.$el = dialogHtml; - backward_shortcut = { 'alt': false, 'shift': true, @@ -112,4 +107,93 @@ describe('dialogTabNavigator', function () { }); -}); \ No newline at end of file + + describe('navigateForward from fist tab to second tab', function () { + var navigateForwardResult; + beforeEach(() => { + spyOn(tabNavigator, 'navigateForward').and.callThrough(); + + navigateForwardResult = tabNavigator.navigateForward( + dialog.find('ul.nav-tabs:first'), + dialog.find('div#1') + ); + }); + + it('should return true', function () { + + expect(navigateForwardResult).toEqual(true); + + }); + + }); + + + describe('navigateForward from last tab', function () { + var navigateForwardResult; + beforeEach(() => { + + // set second tab active + dialog.find('ul.nav-tabs li a.active').removeClass('active'); + + dialog.find('ul.nav-tabs li a[href="#3"]').addClass('active'); + + spyOn(tabNavigator, 'navigateForward').and.callThrough(); + + navigateForwardResult = tabNavigator.navigateForward( + dialog.find('ul.nav-tabs:first'), + dialog.find('div#1') + ); + }); + + it('should return false', function () { + + expect(navigateForwardResult).toEqual(false); + + }); + + }); + + describe('navigateBackward from second tab to first tab', function () { + var navigateBackwardResult; + beforeEach(() => { + // set second tab active + dialog.find('ul.nav-tabs li a.active').removeClass('active'); + + dialog.find('ul.nav-tabs li a[href="#2"]').addClass('active'); + + spyOn(tabNavigator, 'navigateBackward').and.callThrough(); + + navigateBackwardResult = tabNavigator.navigateBackward( + dialog.find('ul.nav-tabs:first'), + dialog.find('div#1') + ); + }); + + it('should return true', function () { + + expect(navigateBackwardResult).toEqual(true); + + }); + + }); + + describe('navigateBackward from first tab', function () { + var navigateBackwardResult; + beforeEach(() => { + spyOn(tabNavigator, 'navigateBackward').and.callThrough(); + + navigateBackwardResult = tabNavigator.navigateBackward( + dialog.find('ul.nav-tabs:first'), + dialog.find('div#1') + ); + }); + + it('should return false', function () { + + expect(navigateBackwardResult).toEqual(false); + + }); + + }); + +});