Support tab navigation in dialogs. Fixes #2898

This commit is contained in:
Harshal Dhumal 2018-02-27 11:18:36 +00:00 committed by Dave Page
parent 3be22383b8
commit aa1849c13a
15 changed files with 485 additions and 92 deletions

View File

@ -10,36 +10,51 @@ desired.˝
When using main browser window, the following keyboard shortcuts are available: When using main browser window, the following keyboard shortcuts are available:
+---------------------------+--------------------------------------------------------+ +----------------------------+-------------------------------------------------------+
| Shortcut for all platform | Function | | Shortcut for all platforms | Function |
+===========================+========================================================+ +============================+=======================================================+
| Alt+Shift+F | Open the File menu | | Alt+Shift+F | Open the File menu |
+---------------------------+--------------------------------------------------------+ +----------------------------+-------------------------------------------------------+
| Alt+Shift+O | Open the Object menu | | Alt+Shift+O | Open the Object menu |
+---------------------------+--------------------------------------------------------+ +----------------------------+-------------------------------------------------------+
| Alt+Shift+L | Open the Tools menu | | Alt+Shift+L | Open the Tools menu |
+---------------------------+--------------------------------------------------------+ +----------------------------+-------------------------------------------------------+
| Alt+Shift+H | Open the Help menu | | Alt+Shift+H | Open the Help menu |
+---------------------------+--------------------------------------------------------+ +----------------------------+-------------------------------------------------------+
| Alt+Shift+B | Focus the browser tree | | Alt+Shift+B | Focus the browser tree |
+---------------------------+--------------------------------------------------------+ +----------------------------+-------------------------------------------------------+
| Alt+Shift+[ | Move tabbed panel backward/forward | | Alt+Shift+[ | Move tabbed panel backward |
| Alt+Shift+] | | +----------------------------+-------------------------------------------------------+
+---------------------------+--------------------------------------------------------+ | Alt+Shift+] | Move tabbed panel forward |
| Alt+Shift+Q | Open the Query Tool in the current database | +----------------------------+-------------------------------------------------------+
+---------------------------+--------------------------------------------------------+ | Alt+Shift+Q | Open the Query Tool in the current database |
| Alt+Shift+V | View Data in the selected table/view | +----------------------------+-------------------------------------------------------+
+---------------------------+--------------------------------------------------------+ | Alt+Shift+V | View Data in the selected table/view |
| Alt+Shift+C | Open the context menu | +----------------------------+-------------------------------------------------------+
+---------------------------+--------------------------------------------------------+ | Alt+Shift+C | Open the context menu |
| Alt+Shift+N | Create an object | +----------------------------+-------------------------------------------------------+
+---------------------------+--------------------------------------------------------+ | Alt+Shift+N | Create an object |
| Alt+Shift+E | Edit object properties | +----------------------------+-------------------------------------------------------+
+---------------------------+--------------------------------------------------------+ | Alt+Shift+E | Edit object properties |
| Alt+Shift+D | Delete the object | +----------------------------+-------------------------------------------------------+
+---------------------------+--------------------------------------------------------+ | Alt+Shift+D | Delete the object |
| Alt+Shift+G | Direct debugging | +----------------------------+-------------------------------------------------------+
+---------------------------+--------------------------------------------------------+ | Alt+Shift+G | Direct debugging |
+----------------------------+-------------------------------------------------------+
**Dialog tab shortcuts**
Use the shortcuts below to navigate the tabsets on dialogs:
+----------------------------+-------------------------------------------------------+
| Shortcut for all platforms | Function |
+============================+=======================================================+
| Control+Shift+[ | Dialog tab backward |
+----------------------------+-------------------------------------------------------+
| Control+Shift+] | Dialog tab forward |
+----------------------------+-------------------------------------------------------+
**SQL Editors** **SQL Editors**

View File

@ -433,6 +433,36 @@ class BrowserModule(PgAdminModule):
fields=fields fields=fields
) )
self.preference.register(
'keyboard_shortcuts',
'dialog_tab_forward',
gettext('Dialog tab forward'),
'keyboardshortcut',
{
'alt': False,
'shift': True,
'control': True,
'key': {'key_code': 93, 'char': ']'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'dialog_tab_backward',
gettext('Dialog tab backward'),
'keyboardshortcut',
{
'alt': False,
'shift': True,
'control': True,
'key': {'key_code': 91, 'char': '['}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
def get_exposed_url_endpoints(self): def get_exposed_url_endpoints(self):
""" """
Returns: Returns:

View File

@ -1,7 +1,6 @@
/* eslint-disable */ define(['underscore', 'underscore.string', 'sources/pgadmin', 'jquery', 'mousetrap',
define( 'sources/utils', 'sources/dialog_tab_navigator'],
['underscore', 'underscore.string', 'sources/pgadmin', 'jquery', 'mousetrap'], function(_, S, pgAdmin, $, Mousetrap, commonUtils, dialogTabNavigator) {
function(_, S, pgAdmin, $, Mousetrap) {
'use strict'; 'use strict';
var pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {}; var pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
@ -12,27 +11,26 @@ function(_, S, pgAdmin, $, Mousetrap) {
init: function() { init: function() {
Mousetrap.reset(); Mousetrap.reset();
if (pgBrowser.preferences_cache.length > 0) { if (pgBrowser.preferences_cache.length > 0) {
var getShortcut = this.parseShortcutValue;
this.keyboardShortcut = { this.keyboardShortcut = {
'file_shortcut': getShortcut(pgBrowser.get_preference('browser', 'main_menu_file').value), 'file_shortcut': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'main_menu_file').value),
'object_shortcut': getShortcut(pgBrowser.get_preference('browser', 'main_menu_object').value), 'object_shortcut': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'main_menu_object').value),
'tools_shortcut': getShortcut(pgBrowser.get_preference('browser', 'main_menu_tools').value), 'tools_shortcut': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'main_menu_tools').value),
'help_shortcut': getShortcut(pgBrowser.get_preference('browser', 'main_menu_help').value), 'help_shortcut': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'main_menu_help').value),
'left_tree_shortcut': getShortcut(pgBrowser.get_preference('browser', 'browser_tree').value), 'left_tree_shortcut': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'browser_tree').value),
'tabbed_panel_backward': getShortcut(pgBrowser.get_preference('browser', 'tabbed_panel_backward').value), 'tabbed_panel_backward': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'tabbed_panel_backward').value),
'tabbed_panel_forward': getShortcut(pgBrowser.get_preference('browser', 'tabbed_panel_forward').value), 'tabbed_panel_forward': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'tabbed_panel_forward').value),
'sub_menu_query_tool': getShortcut(pgBrowser.get_preference('browser', 'sub_menu_query_tool').value), 'sub_menu_query_tool': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_query_tool').value),
'sub_menu_view_data': getShortcut(pgBrowser.get_preference('browser', 'sub_menu_view_data').value), 'sub_menu_view_data': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_view_data').value),
'sub_menu_properties': getShortcut(pgBrowser.get_preference('browser', 'sub_menu_properties').value), 'sub_menu_properties': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_properties').value),
'sub_menu_create': getShortcut(pgBrowser.get_preference('browser', 'sub_menu_create').value), 'sub_menu_create': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_create').value),
'sub_menu_delete': getShortcut(pgBrowser.get_preference('browser', 'sub_menu_delete').value), 'sub_menu_delete': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_delete').value),
'context_menu': getShortcut(pgBrowser.get_preference('browser', 'context_menu').value), 'context_menu': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'context_menu').value),
'direct_debugging': getShortcut(pgBrowser.get_preference('browser', 'direct_debugging').value) 'direct_debugging': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'direct_debugging').value),
}; };
this.shortcutMethods = { this.shortcutMethods = {
'bindMainMenu': {'shortcuts': [this.keyboardShortcut.file_shortcut, 'bindMainMenu': {'shortcuts': [this.keyboardShortcut.file_shortcut,
this.keyboardShortcut.object_shortcut, this.keyboardShortcut.tools_shortcut, this.keyboardShortcut.object_shortcut, this.keyboardShortcut.tools_shortcut,
this.keyboardShortcut.help_shortcut]}, // Main menu this.keyboardShortcut.help_shortcut]}, // Main menu
'bindRightPanel': {'shortcuts': [this.keyboardShortcut.tabbed_panel_backward, this.keyboardShortcut.tabbed_panel_forward]}, // Main window panels 'bindRightPanel': {'shortcuts': [this.keyboardShortcut.tabbed_panel_backward, this.keyboardShortcut.tabbed_panel_forward]}, // Main window panels
'bindMainMenuLeft': {'shortcuts': 'left', 'bindElem': '.pg-navbar'}, // Main menu 'bindMainMenuLeft': {'shortcuts': 'left', 'bindElem': '.pg-navbar'}, // Main menu
'bindMainMenuRight': {'shortcuts': 'right', 'bindElem': '.pg-navbar'}, // Main menu 'bindMainMenuRight': {'shortcuts': 'right', 'bindElem': '.pg-navbar'}, // Main menu
@ -44,9 +42,9 @@ function(_, S, pgAdmin, $, Mousetrap) {
'bindSubMenuCreate': {'shortcuts': this.keyboardShortcut.sub_menu_create}, // Sub menu - Create Object, 'bindSubMenuCreate': {'shortcuts': this.keyboardShortcut.sub_menu_create}, // Sub menu - Create Object,
'bindSubMenuDelete': {'shortcuts': this.keyboardShortcut.sub_menu_delete}, // Sub menu - Delete object, 'bindSubMenuDelete': {'shortcuts': this.keyboardShortcut.sub_menu_delete}, // Sub menu - Delete object,
'bindContextMenu': {'shortcuts': this.keyboardShortcut.context_menu}, // Sub menu - Open context menu, 'bindContextMenu': {'shortcuts': this.keyboardShortcut.context_menu}, // Sub menu - Open context menu,
'bindDirectDebugging': {'shortcuts': this.keyboardShortcut.direct_debugging} // Sub menu - Direct Debugging 'bindDirectDebugging': {'shortcuts': this.keyboardShortcut.direct_debugging}, // Sub menu - Direct Debugging
}; };
this.bindShortcuts(); this.bindShortcuts();
} }
}, },
bindShortcuts: function() { bindShortcuts: function() {
@ -71,6 +69,20 @@ function(_, S, pgAdmin, $, Mousetrap) {
attachShortcut: function(shortcut, callback, bindElem) { attachShortcut: function(shortcut, callback, bindElem) {
this._bindWithMousetrap(shortcut, callback, bindElem); this._bindWithMousetrap(shortcut, callback, bindElem);
}, },
attachDialogTabNavigatorShortcut: function(dialogTabNavigator, shortcuts) {
var callback = dialogTabNavigator.on_keyboard_event,
domElem = dialogTabNavigator.dialog.el;
if (domElem) {
Mousetrap(domElem).bind(shortcuts, function() {
callback.apply(dialogTabNavigator, arguments);
}.bind(domElem));
} else {
Mousetrap.bind(shortcuts, function() {
callback.apply(dialogTabNavigator, arguments);
});
}
},
detachShortcut: function(shortcut, bindElem) { detachShortcut: function(shortcut, bindElem) {
if (bindElem) Mousetrap(bindElem).unbind(shortcut); if (bindElem) Mousetrap(bindElem).unbind(shortcut);
else Mousetrap.unbind(shortcut); else Mousetrap.unbind(shortcut);
@ -211,18 +223,18 @@ function(_, S, pgAdmin, $, Mousetrap) {
}, },
bindContextMenu: function(e) { bindContextMenu: function(e) {
var tree = this.getTreeDetails(), var tree = this.getTreeDetails(),
e = window.event, left = $(e.srcElement).find('.aciTreeEntry').position().left + 70,
left = $(e.srcElement).find('.aciTreeEntry').position().left + 70, top = $(e.srcElement).find('.aciTreeEntry').position().top + 70;
top = $(e.srcElement).find('.aciTreeEntry').position().top + 70; e = window.event;
tree.t.blur(tree.i); tree.t.blur(tree.i);
$('#tree').blur(); $('#tree').blur();
// Call context menu and set position // Call context menu and set position
var ctx = tree.i.children().contextMenu({x: left, y:top}); tree.i.children().contextMenu({x: left, y:top});
}, },
bindDirectDebugging: function(e) { bindDirectDebugging: function() {
var tree = this.getTreeDetails(), var tree = this.getTreeDetails(),
type = tree.t.itemData(tree.i)._type; type = tree.t.itemData(tree.i)._type;
if (!tree.d || (type != 'function' && type != 'procedure')) if (!tree.d || (type != 'function' && type != 'procedure'))
return; return;
@ -232,26 +244,23 @@ function(_, S, pgAdmin, $, Mousetrap) {
pgAdmin.Tools.Debugger.get_function_information(pgAdmin.Browser.Nodes[type]); pgAdmin.Tools.Debugger.get_function_information(pgAdmin.Browser.Nodes[type]);
} }
}, },
parseShortcutValue: function(obj) {
var shortcut = "";
if (obj.alt) { shortcut += 'alt+'; }
if (obj.shift) { shortcut += 'shift+'; }
if (obj.control) { shortcut += 'ctrl+'; }
shortcut += String.fromCharCode(obj.key.key_code).toLowerCase();
return shortcut;
},
getTreeDetails: function() { getTreeDetails: function() {
var t = pgAdmin.Browser.tree, var t = pgAdmin.Browser.tree,
i = t.selected().length > 0 ? t.selected() : t.first(), i = t.selected().length > 0 ? t.selected() : t.first(),
d = i && i.length == 1 ? t.itemData(i) : undefined; d = i && i.length == 1 ? t.itemData(i) : undefined;
return { return {
t: t, t: t,
i: i, i: i,
d: d d: d,
} };
} },
getDialogTabNavigator: function(dialog) {
var backward_shortcut = pgBrowser.get_preference('browser', 'dialog_tab_backward').value,
forward_shortcut = pgBrowser.get_preference('browser', 'dialog_tab_forward').value;
return new dialogTabNavigator.dialogTabNavigator(dialog, backward_shortcut, forward_shortcut);
},
}); });
return pgAdmin.keyboardNavigation; return pgAdmin.keyboardNavigation;

View File

@ -1,8 +1,9 @@
define('pgadmin.browser.node', [ define('pgadmin.browser.node', [
'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin', 'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin',
'pgadmin.browser.menu', 'backbone', 'pgadmin.alertifyjs', 'pgadmin.browser.datamodel', 'pgadmin.browser.menu', 'backbone', 'pgadmin.alertifyjs', 'pgadmin.browser.datamodel',
'backform', 'sources/browser/generate_url', 'pgadmin.browser.utils', 'pgadmin.backform', 'backform', 'sources/browser/generate_url', 'sources/utils', 'pgadmin.browser.utils',
], function(gettext, $, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform, generateUrl) { 'pgadmin.backform',
], function(gettext, $, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform, generateUrl, commonUtils) {
var wcDocker = window.wcDocker, var wcDocker = window.wcDocker,
keyCode = { keyCode = {
@ -365,9 +366,8 @@ define('pgadmin.browser.node', [
} }
var setFocusOnEl = function() { var setFocusOnEl = function() {
setTimeout(function() { var container = $(el).find('.tab-content:first > .tab-pane.active:first');
$(el).find('.tab-pane.active:first').find('input:first').focus(); commonUtils.findAndSetFocus(container);
}, 500);
}; };
if (!newModel.isNew()) { if (!newModel.isNew()) {
@ -394,6 +394,8 @@ define('pgadmin.browser.node', [
view.render(); view.render();
setFocusOnEl(); setFocusOnEl();
newModel.startNewSession(); newModel.startNewSession();
// var dialogTabNavigator = pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
}, },
error: function(xhr, error, message) { error: function(xhr, error, message) {
var _label = that && item ? var _label = that && item ?
@ -430,8 +432,11 @@ define('pgadmin.browser.node', [
view.render(); view.render();
setFocusOnEl(); setFocusOnEl();
newModel.startNewSession(); newModel.startNewSession();
// var dialogTabNavigator = pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
} }
} }
return view; return view;
} }

View File

@ -1,7 +1,7 @@
define([ define([
'underscore', 'jquery', 'backbone', 'sources/pgadmin', 'pgadmin.browser', 'underscore', 'jquery', 'backbone', 'sources/pgadmin', 'pgadmin.browser',
'sources/gettext', 'sources/gettext', 'sources/utils',
], function(_, $, Backbone, pgAdmin, pgBrowser, gettext) { ], function(_, $, Backbone, pgAdmin, pgBrowser, gettext, commonUtils) {
var wcDocker = window.wcDocker; var wcDocker = window.wcDocker;
@ -157,7 +157,8 @@ define([
this.currPage = this.collection.at(this.options.curr_page).toJSON(); this.currPage = this.collection.at(this.options.curr_page).toJSON();
}, },
render: function() { render: function() {
var data = this.currPage; var self = this,
data = this.currPage;
/* Check Status of the buttons */ /* Check Status of the buttons */
this.options.disable_next = (this.options.disable_next ? true : this.evalASFunc(this.currPage.disable_next)); this.options.disable_next = (this.options.disable_next ? true : this.evalASFunc(this.currPage.disable_next));
@ -179,6 +180,11 @@ define([
/* OnLoad Callback */ /* OnLoad Callback */
this.onLoad(); this.onLoad();
setTimeout(function() {
var container = $(self.el);
commonUtils.findAndSetFocus(container);
}, 100);
return this; return this;
}, },
nextPage: function() { nextPage: function() {

View File

@ -500,12 +500,12 @@ define([
template: { template: {
'header': _.template([ 'header': _.template([
'<li role="presentation" <%=disabled ? "disabled" : ""%>>', '<li role="presentation" <%=disabled ? "disabled" : ""%>>',
' <a data-toggle="tab" data-tab-index="<%=tabIndex%>" href="#<%=cId%>"', ' <a data-toggle="tab" tabindex="-1" data-tab-index="<%=tabIndex%>" href="#<%=cId%>"',
' id="<%=hId%>" aria-controls="<%=cId%>">', ' id="<%=hId%>" aria-controls="<%=cId%>">',
'<%=label%></a></li>', '<%=label%></a></li>',
].join(' ')), ].join(' ')),
'panel': _.template( 'panel': _.template(
'<div role="tabpanel" class="tab-pane <%=label%> pg-el-sm-12 pg-el-md-12 pg-el-lg-12 pg-el-xs-12 fade" id="<%=cId%>" aria-labelledby="<%=hId%>"></div>' '<div role="tabpanel" tabindex="-1" class="tab-pane <%=label%> pg-el-sm-12 pg-el-md-12 pg-el-lg-12 pg-el-xs-12 fade" id="<%=cId%>" aria-labelledby="<%=hId%>"></div>'
), ),
}, },
render: function() { render: function() {

View File

@ -0,0 +1,143 @@
import $ from 'jquery';
import Mousetrap from 'mousetrap';
import { findAndSetFocus } from './utils';
import { parseShortcutValue } from './utils';
class dialogTabNavigator {
constructor(dialog, backwardShortcut, forwardShortcut) {
this.dialog = dialog;
this.tabSwitching = false;
this.tabs = this.dialog.$el.find('.nav-tabs');
if (this.tabs.length > 0 ) {
this.tabs = this.tabs[0];
}
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));
}
onKeyboardEvent(event, shortcut) {
var currentTabPane = this.dialog.$el
.find('.tab-content:first > .tab-pane.active:first'),
childTabData = this.isActivePaneHasChildTabs(currentTabPane);
if (this.tabSwitching) {
return;
}
this.tabSwitching = true;
if(childTabData) {
var res = this.navigate(shortcut, childTabData.childTab,
childTabData.childTabPane);
if (!res) {
this.navigate(shortcut, this.tabs, currentTabPane);
}
} else {
this.navigate(shortcut, this.tabs, currentTabPane);
}
}
isActivePaneHasChildTabs(currentTabPane) {
var childTab = currentTabPane.find('.nav-tabs:first'),
childTabPane;
if (childTab.length > 0) {
childTabPane = currentTabPane
.find('.tab-content:first > .tab-pane.active:first');
return {
'childTab': childTab,
'childTabPane': childTabPane,
};
}
return null;
}
navigate(shortcut, tabs, tab_pane) {
if(shortcut == this.dialogTabBackward) {
return this.navigateBackward(tabs, tab_pane);
}else if (shortcut == this.dialogTabForward) {
return this.navigateForward(tabs, tab_pane);
}
return false;
}
navigateBackward(tabs, tab_pane) {
var self = this,
nextTabPane,
innerTabContainer,
prevtab = $(tabs).find('li.active').prev('li');
if (prevtab.length > 0) {
prevtab.find('a').tab('show');
nextTabPane = tab_pane.prev();
innerTabContainer = nextTabPane
.find('.tab-content:first > .tab-pane.active:first');
if (innerTabContainer.length > 0) {
findAndSetFocus(innerTabContainer);
} else {
findAndSetFocus(nextTabPane);
}
setTimeout(function() {
self.tabSwitching = false;
}, 200);
return true;
}
this.tabSwitching = false;
return false;
}
navigateForward(tabs, tab_pane) {
var self = this,
nextTabPane,
innerTabContainer,
nexttab = $(tabs).find('li.active').next('li');
if(nexttab.length > 0) {
nexttab.find('a').tab('show');
nextTabPane = tab_pane.next();
innerTabContainer = nextTabPane
.find('.tab-content:first > .tab-pane.active:first');
if (innerTabContainer.length > 0) {
findAndSetFocus(innerTabContainer);
} else {
findAndSetFocus(nextTabPane);
}
setTimeout(function() {
self.tabSwitching = false;
}, 200);
return true;
}
this.tabSwitching = false;
return false;
}
detach() {
Mousetrap(this.dialog.el).unbind(this.dialogTabBackward);
Mousetrap(this.dialog.el).unbind(this.dialogTabForward);
}
}
module.exports = {
dialogTabNavigator: dialogTabNavigator,
};

View File

@ -0,0 +1,38 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////////////////
export function parseShortcutValue(obj) {
var shortcut = '';
if (obj.alt) { shortcut += 'alt+'; }
if (obj.shift) { shortcut += 'shift+'; }
if (obj.control) { shortcut += 'ctrl+'; }
shortcut += String.fromCharCode(obj.key.key_code).toLowerCase();
return shortcut;
}
export function findAndSetFocus(container) {
if (container.length == 0) {
return;
}
setTimeout(function() {
var first_el = container
.find('button.fa-plus:first');
if (first_el.length == 0) {
first_el = container
.find('.pgadmin-controls:first>input:enabled,.CodeMirror-scroll');
}
if(first_el.length > 0) {
first_el[0].focus();
} else {
container[0].focus();
}
}, 200);
}

View File

@ -2,9 +2,10 @@
define([ define([
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'underscore.string', 'pgadmin.alertifyjs', 'backbone', 'pgadmin.backgrid', 'underscore.string', 'pgadmin.alertifyjs', 'backbone', 'pgadmin.backgrid',
'pgadmin.backform', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.browser', 'sources/utils',
], function( ], function(
gettext, url_for, $, _, S, alertify, Backbone, Backgrid, Backform, pgBrowser gettext, url_for, $, _, S, alertify, Backbone, Backgrid, Backform, pgBrowser,
commonUtils
) { ) {
// if module is already initialized, refer to that. // if module is already initialized, refer to that.
@ -696,6 +697,9 @@ define([
this.elements.content.appendChild($container.get(0)); this.elements.content.appendChild($container.get(0));
var container = view.$el.find('.tab-content:first > .tab-pane.active:first');
commonUtils.findAndSetFocus(container);
// Listen to model & if filename is provided then enable Backup button // Listen to model & if filename is provided then enable Backup button
this.view.model.on('change', function() { this.view.model.on('change', function() {
if (!_.isUndefined(this.get('file')) && this.get('file') !== '') { if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
@ -940,6 +944,13 @@ define([
this.elements.content.appendChild($container.get(0)); this.elements.content.appendChild($container.get(0));
if(view) {
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);
}
// Listen to model & if filename is provided then enable Backup button // Listen to model & if filename is provided then enable Backup button
this.view.model.on('change', function() { this.view.model.on('change', function() {
if (!_.isUndefined(this.get('file')) && this.get('file') !== '') { if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {

View File

@ -89,8 +89,10 @@ define([
cell: Backgrid.Extension.SelectRowCell.extend({ cell: Backgrid.Extension.SelectRowCell.extend({
render: function() { render: function() {
// Use the Backform Control's render function // Do not use parent's render function. It set's tabindex to -1 on
Backgrid.Extension.SelectRowCell.prototype.render.apply(this, arguments); // checkboxes.
this.$el.empty().append('<input type="checkbox" />');
this.delegateEvents();
var col = this.column.get('name'); var col = this.column.get('name');
if (this.model && this.model.has(col)) { if (this.model && this.model.has(col)) {

View File

@ -1,9 +1,10 @@
define([ define([
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'pgadmin.alertifyjs', 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'pgadmin.alertifyjs',
'sources/pgadmin', 'pgadmin.browser', 'backbone', 'backgrid', 'backform', 'sources/pgadmin', 'pgadmin.browser', 'backbone', 'backgrid', 'backform',
'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.node.ui', 'sources/utils', 'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.node.ui',
], function( ], function(
gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, Backform gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
Backform, commonUtils
) { ) {
pgAdmin = pgAdmin || window.pgAdmin || {}; pgAdmin = pgAdmin || window.pgAdmin || {};
@ -652,6 +653,12 @@ define([
// Give the dialog initial height & width // Give the dialog initial height & width
this.elements.dialog.style.minHeight = '80%'; this.elements.dialog.style.minHeight = '80%';
this.elements.dialog.style.minWidth = '70%'; this.elements.dialog.style.minWidth = '70%';
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);
}, },
}; };
}); });

View File

@ -1,12 +1,12 @@
define([ define([
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'underscore.string', 'pgadmin.alertifyjs', 'sources/pgadmin', 'pgadmin.browser', 'backbone', 'underscore.string', 'pgadmin.alertifyjs', 'sources/pgadmin', 'pgadmin.browser', 'backbone',
'backgrid', 'backform', 'backgrid', 'backform', 'sources/utils',
'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.backform', 'pgadmin.backgrid',
'pgadmin.browser.node.ui', 'pgadmin.browser.node.ui',
], function( ], function(
gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
Backform Backform, commonUtils
) { ) {
pgAdmin = pgAdmin || window.pgAdmin || {}; pgAdmin = pgAdmin || window.pgAdmin || {};
@ -468,6 +468,10 @@ define([
$(reindex_btn).addClass('active'); $(reindex_btn).addClass('active');
} }
view.$el.attr('tabindex', -1);
var container = view.$el.find('.tab-content:first > .tab-pane.active:first');
commonUtils.findAndSetFocus(container);
this.elements.content.appendChild($container.get(0)); this.elements.content.appendChild($container.get(0));
}, },
}; };

View File

@ -2,9 +2,10 @@
define('tools.restore', [ define('tools.restore', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone', 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'underscore.string', 'pgadmin.alertifyjs', 'pgadmin.browser', 'underscore.string', 'pgadmin.alertifyjs', 'pgadmin.browser',
'pgadmin.backgrid', 'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.backform', 'sources/utils',
], function( ], function(
gettext, url_for, $, _, Backbone, S, alertify, pgBrowser, Backgrid, Backform gettext, url_for, $, _, Backbone, S, alertify, pgBrowser, Backgrid, Backform,
commonUtils
) { ) {
// if module is already initialized, refer to that. // if module is already initialized, refer to that.
@ -572,6 +573,12 @@ define('tools.restore', [
this.elements.content.appendChild($container.get(0)); this.elements.content.appendChild($container.get(0));
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);
// Listen to model & if filename is provided then enable Backup button // Listen to model & if filename is provided then enable Backup button
this.view.model.on('change', function() { this.view.model.on('change', function() {
if (!_.isUndefined(this.get('file')) && this.get('file') !== '') { if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {

View File

@ -0,0 +1,115 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////////////////
import dialogTabNavigator from 'sources/dialog_tab_navigator';
import $ from 'jquery';
import 'bootstrap';
describe('dialogTabNavigator', function () {
let dialog, tabNavigator, backward_shortcut, forward_shortcut;
beforeEach(() => {
let dialogHtml =$('<div tabindex="1" class="backform-tab" role="tabpanel">'+
' <ul class="nav nav-tabs" role="tablist">'+
' <li role="presentation" class="active">'+
' <a data-toggle="tab" tabindex="-1" data-tab-index="1" href="#1" aria-controls="1"> General</a>'+
' </li>'+
' <li role="presentation">'+
' <a data-toggle="tab" tabindex="-1" data-tab-index="5" href="#2" aria-controls="2"> Default Privileges</a>'+
' </li>'+
' <li role="presentation">'+
' <a data-toggle="tab" tabindex="-1" data-tab-index="6" href="#3" aria-controls="3"> SQL</a>'+
' </li>'+
' </ul>'+
' <ul class="tab-content">'+
' <div role="tabpanel" tabindex="-1" class="tab-pane fade collapse in active" id="1">'+
' </div>'+
' <div role="tabpanel" tabindex="-1" class="tab-pane fade collapse" id="2">'+
' <div class="inline-tab-panel" role="tabpanel">'+
' <ul class="nav nav-tabs" role="tablist">'+
' <li role="presentation" class="active">'+
' <a data-toggle="tab" tabindex="-1" data-tab-index="601" href="#11" aria-controls="11"> Tables</a>'+
' </li>'+
' <li role="presentation">'+
' <a data-toggle="tab" tabindex="-1" data-tab-index="602" href="#22" aria-controls="22"> Sequences</a>'+
' </li>'+
' </ul>'+
' <ul class="tab-content">'+
' <div role="tabpanel" tabindex="-1" class="tab-pane fade collapse in active" id="11" >'+
' </div>'+
' <div role="tabpanel" tabindex="-1" class="tab-pane fade collapse" id="22">'+
' </div>'+
' </ul>'+
' </div>'+
' </div>'+
' <div role="tabpanel" tabindex="-1" class="tab-pane fade collapse" id="3">'+
' </div>'+
' </ul>'+
'</div>');
dialog = {};
dialog.el = dialogHtml[0];
dialog.$el = dialogHtml;
backward_shortcut = {
'alt': false,
'shift': true,
'control': true,
'key': {'key_code': 91, 'char': '['}
};
forward_shortcut = {
'alt': false,
'shift': true,
'control': true,
'key': {'key_code': 93, 'char': ']'}
};
tabNavigator = new dialogTabNavigator.dialogTabNavigator(
dialog, backward_shortcut, forward_shortcut);
});
describe('navigate', function () {
beforeEach(() => {
spyOn(tabNavigator, 'navigateBackward').and.callThrough();
spyOn(tabNavigator, 'navigateForward').and.callThrough();
});
it('navigate backward', function () {
tabNavigator.onKeyboardEvent({}, 'shift+ctrl+[');
expect(tabNavigator.navigateBackward).toHaveBeenCalled();
expect(tabNavigator.navigateForward).not.toHaveBeenCalled();
});
it('navigate forward', function () {
tabNavigator.onKeyboardEvent({}, 'shift+ctrl+]');
expect(tabNavigator.navigateForward).toHaveBeenCalled();
expect(tabNavigator.navigateBackward).not.toHaveBeenCalled();
});
it('should not navigate', function () {
tabNavigator.onKeyboardEvent({}, 'shift+ctrl+a');
expect(tabNavigator.navigateForward).not.toHaveBeenCalled();
expect(tabNavigator.navigateBackward).not.toHaveBeenCalled();
});
});
});

View File

@ -126,6 +126,7 @@ var webpackShimConfig = {
'pgadmin': path.join(__dirname, './pgadmin/static/js/pgadmin'), 'pgadmin': path.join(__dirname, './pgadmin/static/js/pgadmin'),
'translations': path.join(__dirname, './pgadmin/tools/templates/js/translations'), 'translations': path.join(__dirname, './pgadmin/tools/templates/js/translations'),
'sources/gettext': path.join(__dirname, './pgadmin/static/js/gettext'), 'sources/gettext': path.join(__dirname, './pgadmin/static/js/gettext'),
'sources/utils': path.join(__dirname, './pgadmin/static/js/utils'),
'babel-polyfill': path.join(__dirname, './node_modules/babel-polyfill/dist/polyfill'), 'babel-polyfill': path.join(__dirname, './node_modules/babel-polyfill/dist/polyfill'),
// Vendor JS // Vendor JS