public inbox for [email protected]
help / color / mirror / Atom feedFrom: Aditya Toshniwal <[email protected]>
To: pgadmin-hackers <[email protected]>
Subject: [pgAdmin][RM4195] Tab navigation is broken in Query tool (Accessibility)
Date: Tue, 21 May 2019 14:30:30 +0530
Message-ID: <CAM9w-_=e8+iiEP0+pTv0htRHhUjupfK6N9dFS1wQNG6MBCq4Vw@mail.gmail.com> (raw)
Hi Hackers,
Attached is the patch to fix the accessibility issue in query tool.
The query tool can be completely restructured unlike previously where query
tool used to stick at top and other tabs in the bottom.
- I have added a new shortcut to switch between frames if in query tool the
tab positions are changed. On switching the frames, the frame will blink to
show it is focussed.
- In the frame, you can switch tab same as previous. The tabs in SQL editor
are cyclic now, which means if you execute shortcut to go right on the last
tab then it will cycle to first tab.
- I have also changed the default shortcut to switch between tabs from
Shift+Ctrl+ArrowKeys to Shift+Ctrl+[ and ] similar to what we have in
browser tabs.
- Similar changes done for debugger also.
Kindly review.
--
Thanks and Regards,
Aditya Toshniwal
Software Engineer | EnterpriseDB India | Pune
"Don't Complain about Heat, Plant a TREE"
Attachments:
[application/octet-stream] RM4195.patch (20.4K, 3-RM4195.patch)
download | inline diff:
diff --git a/web/pgadmin/static/js/keyboard_shortcuts.js b/web/pgadmin/static/js/keyboard_shortcuts.js
index 017529b7..be1bdc93 100644
--- a/web/pgadmin/static/js/keyboard_shortcuts.js
+++ b/web/pgadmin/static/js/keyboard_shortcuts.js
@@ -9,6 +9,7 @@
import $ from 'jquery';
import gettext from 'sources/gettext';
+import { getMod } from 'sources/utils';
const PERIOD_KEY = 190,
FWD_SLASH_KEY = 191,
@@ -116,9 +117,44 @@ function validateShortcutKeys(user_defined_shortcut, event) {
user_defined_shortcut.key.key_code == keyCode;
}
+// Finds the desired panel on which user wants to navigate to
+function focusDockerPanel(docker, op) {
+ if(!docker || !docker._focusFrame) {
+ return;
+ }
+
+ let focus_frame = docker._focusFrame,
+ focus_id = 0,
+ flash = false;
+
+ // Mod is used to cycle the op
+ if (op == 'switch') {
+ let i = 0, total_frames = docker._frameList.length;
+
+ for(i = 0; i < total_frames; i++) {
+ if(focus_frame === docker._frameList[i]) break;
+ }
+ focus_frame = docker._frameList[getMod(i+1,total_frames)];
+ focus_id = focus_frame._curTab;
+ flash = true;
+ } else if (op == 'left') {
+ focus_id = getMod(focus_frame._curTab-1, focus_frame._panelList.length);
+ flash = false;
+ } else if (op == 'right') {
+ focus_id = getMod(focus_frame._curTab+1, focus_frame._panelList.length);
+ flash = false;
+ }
+
+ let focus_panel = focus_frame._panelList[focus_id];
+
+ focus_panel.$container.find('*[tabindex]:not([tabindex="-1"])').trigger('focus');
+ focus_panel.focus(flash);
+ return focus_panel._type;
+}
+
/* Debugger: Keyboard Shortcuts handling */
-function keyboardShortcutsDebugger($el, event, preferences) {
- let panel_id, panel_content, $input;
+function keyboardShortcutsDebugger($el, event, preferences, docker) {
+ let panel_type = '', panel_content, $input;
if(this.validateShortcutKeys(preferences.edit_grid_values, event)) {
this._stopEventPropagation(event);
@@ -132,54 +168,32 @@ function keyboardShortcutsDebugger($el, event, preferences) {
}
} else if(this.validateShortcutKeys(preferences.move_next, event)) {
this._stopEventPropagation(event);
- panel_id = this.getInnerPanel($el, 'right');
+ panel_type = this.focusDockerPanel(docker, 'right');
} else if(this.validateShortcutKeys(preferences.move_previous, event)) {
this._stopEventPropagation(event);
- panel_id = this.getInnerPanel($el, 'left');
- }
- return panel_id;
-}
-
-// Finds the desired panel on which user wants to navigate to
-function getInnerPanel($el, direction) {
- if(!$el || !$el.length)
- return false;
-
- let total_panels = $el.find('.wcPanelTab');
- // If no panels found OR if single panel
- if (!total_panels.length || total_panels.length == 1)
- return false;
-
- let active_panel = $(total_panels).filter('.wcPanelTabActive'),
- id = parseInt($(active_panel).attr('id')),
- fist_panel = 0,
- last_panel = total_panels.length - 1;
-
- // Find desired panel
- if (direction == 'left') {
- if(id > fist_panel)
- id--;
- } else {
- if (id < last_panel)
- id++;
+ panel_type = this.focusDockerPanel(docker, 'left');
+ } else if(this.validateShortcutKeys(preferences.switch_panel, event)) {
+ this._stopEventPropagation(event);
+ panel_type = this.focusDockerPanel(docker, 'switch');
}
- return id;
+ return panel_type;
}
/* Query tool: Keyboard Shortcuts handling */
function keyboardShortcutsQueryTool(
- sqlEditorController, queryToolActions, event
+ sqlEditorController, queryToolActions, event, docker
) {
if (sqlEditorController.isQueryRunning()) {
return;
}
- let keyCode = event.which || event.keyCode, panel_id;
+ let keyCode = event.which || event.keyCode, panel_type = '';
let executeKeys = sqlEditorController.preferences.execute_query;
let explainKeys = sqlEditorController.preferences.explain_query;
let explainAnalyzeKeys = sqlEditorController.preferences.explain_analyze_query;
let downloadCsvKeys = sqlEditorController.preferences.download_csv;
- let nextPanelKeys = sqlEditorController.preferences.move_next;
- let previousPanelKeys = sqlEditorController.preferences.move_previous;
+ let nextTabKeys = sqlEditorController.preferences.move_next;
+ let previousTabKeys = sqlEditorController.preferences.move_previous;
+ let switchPanelKeys = sqlEditorController.preferences.switch_panel;
let toggleCaseKeys = sqlEditorController.preferences.toggle_case;
let commitKeys = sqlEditorController.preferences.commit_transaction;
let rollbackKeys = sqlEditorController.preferences.rollback_transaction;
@@ -236,12 +250,15 @@ function keyboardShortcutsQueryTool(
&& $(event.target).closest('.dropdown-submenu').length > 0) {
$(event.target).closest('.dropdown-submenu').find('.dropdown-menu').removeClass('show');
}
- } else if(this.validateShortcutKeys(nextPanelKeys, event)) {
+ } else if(this.validateShortcutKeys(nextTabKeys, event)) {
+ this._stopEventPropagation(event);
+ panel_type = this.focusDockerPanel(docker, 'right');
+ } else if(this.validateShortcutKeys(previousTabKeys, event)) {
this._stopEventPropagation(event);
- panel_id = this.getInnerPanel(sqlEditorController.container, 'right');
- } else if(this.validateShortcutKeys(previousPanelKeys, event)) {
+ panel_type = this.focusDockerPanel(docker, 'left');
+ } else if(this.validateShortcutKeys(switchPanelKeys, event)) {
this._stopEventPropagation(event);
- panel_id = this.getInnerPanel(sqlEditorController.container, 'left');
+ panel_type = this.focusDockerPanel(docker, 'switch');
} else if(keyCode === UP_KEY || keyCode === DOWN_KEY) {
/*Apply only for dropdown*/
if($(event.target).closest('.dropdown-menu').length > 0) {
@@ -293,13 +310,13 @@ function keyboardShortcutsQueryTool(
}
}
- return panel_id;
+ return panel_type;
}
export {
keyboardShortcutsDebugger as processEventDebugger,
keyboardShortcutsQueryTool as processEventQueryTool,
- getInnerPanel, validateShortcutKeys,
+ focusDockerPanel, validateShortcutKeys,
_stopEventPropagation, isMac, isKeyCtrlAlt, isKeyAltShift, isKeyCtrlShift,
isKeyCtrlAltShift, isAltShiftBoth, isCtrlShiftBoth, isCtrlAltBoth,
shortcut_key, shortcut_title, shortcut_accesskey_title,
diff --git a/web/pgadmin/static/js/utils.js b/web/pgadmin/static/js/utils.js
index 6b1ed9ad..1c58a9eb 100644
--- a/web/pgadmin/static/js/utils.js
+++ b/web/pgadmin/static/js/utils.js
@@ -79,3 +79,7 @@ export function getGCD(inp_arr) {
return result;
}
+
+export function getMod(no, divisor) {
+ return ((no % divisor) + divisor) % divisor;
+}
diff --git a/web/pgadmin/tools/debugger/__init__.py b/web/pgadmin/tools/debugger/__init__.py
index 44d55da8..a2c592e0 100644
--- a/web/pgadmin/tools/debugger/__init__.py
+++ b/web/pgadmin/tools/debugger/__init__.py
@@ -184,8 +184,8 @@ class DebuggerModule(PgAdminModule):
'shift': True,
'control': False,
'key': {
- 'key_code': 37,
- 'char': 'ArrowLeft'
+ 'key_code': 91,
+ 'char': '['
}
},
category_label=gettext('Keyboard shortcuts'),
@@ -202,8 +202,26 @@ class DebuggerModule(PgAdminModule):
'shift': True,
'control': False,
'key': {
- 'key_code': 39,
- 'char': 'ArrowRight'
+ 'key_code': 93,
+ 'char': ']'
+ }
+ },
+ category_label=gettext('Keyboard shortcuts'),
+ fields=shortcut_fields
+ )
+
+ self.preference.register(
+ 'keyboard_shortcuts',
+ 'switch_panel',
+ gettext('Switch Panel'),
+ 'keyboardshortcut',
+ {
+ 'alt': True,
+ 'shift': True,
+ 'control': False,
+ 'key': {
+ 'key_code': 9,
+ 'char': 'Tab'
}
},
category_label=gettext('Keyboard shortcuts'),
diff --git a/web/pgadmin/tools/debugger/static/js/direct.js b/web/pgadmin/tools/debugger/static/js/direct.js
index d0bcf3ee..5c649b4b 100644
--- a/web/pgadmin/tools/debugger/static/js/direct.js
+++ b/web/pgadmin/tools/debugger/static/js/direct.js
@@ -1510,17 +1510,17 @@ define([
controller.Step_into(pgTools.DirectDebug.trans_id);
},
keyAction: function (event) {
- var $el = this.$el, panel_id, actual_panel,
- self = this;
+ let panel_type='';
- panel_id = keyboardShortcuts.processEventDebugger(
- $el, event, self.preferences
+ panel_type = keyboardShortcuts.processEventDebugger(
+ this.$el, event, this.preferences, pgTools.DirectDebug.docker
);
- // Panel navigation
- if(!_.isUndefined(panel_id) && !_.isNull(panel_id)) {
- actual_panel = panel_id + 1;
- pgTools.DirectDebug.docker.findPanels()[actual_panel].focus();
+
+ if(!_.isNull(panel_type) && !_.isUndefined(panel_type) && panel_type != '') {
+ setTimeout(function() {
+ pgBrowser.Events.trigger(`pgadmin:debugger:${panel_type}:focus`);
+ }, 100);
}
},
});
@@ -1853,6 +1853,10 @@ define([
}
});
+ pgBrowser.Events.on('pgadmin:debugger:code:focus', ()=>{
+ self.editor.focus();
+ });
+
// On loading the docker, register the callbacks
var onLoad = function() {
self.docker.finishLoading(100);
@@ -1903,6 +1907,16 @@ define([
// Create the toolbar view for debugging the function
this.toolbarView = new DebuggerToolbarView();
+ /* wcDocker focuses on window always, and all our shortcuts are
+ * bind to editor-panel. So when we use wcDocker focus, editor-panel
+ * loses focus and events don't work.
+ */
+ $(window).on('keydown', (e)=>{
+ if(self.toolbarView.keyAction) {
+ self.toolbarView.keyAction(e);
+ }
+ });
+
/* Cache may take time to load for the first time
* Keep trying till available
*/
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index fcc31cf3..6af14bc3 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -192,8 +192,8 @@ define('tools.querytool', [
'filename': 'css',
}),
theme: 'webcabin.overrides.css',
- });
-
+ }
+ );
// Create the panels
var sql_panel = new pgAdmin.Browser.Panel({
@@ -213,7 +213,7 @@ define('tools.querytool', [
isCloseable: false,
isPrivate: true,
extraClasses: 'hide-vertical-scrollbar',
- content: '<div id ="datagrid" class="sql-editor-grid-container text-12" tabindex: "0"></div>',
+ content: '<div id ="datagrid" class="sql-editor-grid-container text-12" tabindex="0"></div>',
});
var explain = new pgAdmin.Browser.Panel({
@@ -223,7 +223,7 @@ define('tools.querytool', [
height: '100%',
isCloseable: false,
isPrivate: true,
- content: '<div class="sql-editor-explain" tabindex: "0"></div>',
+ content: '<div class="sql-editor-explain" tabindex="0"></div>',
});
var messages = new pgAdmin.Browser.Panel({
@@ -233,7 +233,7 @@ define('tools.querytool', [
height: '100%',
isCloseable: false,
isPrivate: true,
- content: '<div class="sql-editor-message" tabindex: "0"></div>',
+ content: '<div class="sql-editor-message" tabindex= "0"></div>',
});
var history = new pgAdmin.Browser.Panel({
@@ -243,7 +243,7 @@ define('tools.querytool', [
height: '33%',
isCloseable: false,
isPrivate: true,
- content: '<div id ="history_grid" class="sql-editor-history-container" tabindex: "0"></div>',
+ content: '<div id ="history_grid" class="sql-editor-history-container" tabindex= "0"></div>',
});
var scratch = new pgAdmin.Browser.Panel({
@@ -253,7 +253,7 @@ define('tools.querytool', [
height: '33%',
isCloseable: true,
isPrivate: false,
- content: '<div class="sql-scratch" tabindex: "0"><textarea wrap="off"></textarea></div>',
+ content: '<div class="sql-scratch"><textarea wrap="off" tabindex= "0"></textarea></div>',
});
var notifications = new pgAdmin.Browser.Panel({
@@ -263,7 +263,7 @@ define('tools.querytool', [
height: '100%',
isCloseable: false,
isPrivate: true,
- content: '<div id ="notification_grid" class="sql-editor-notifications" tabindex: "0"></div>',
+ content: '<div id ="notification_grid" class="sql-editor-notifications" tabindex= "0"></div>',
});
var geometry_viewer = new pgAdmin.Browser.Panel({
@@ -273,7 +273,7 @@ define('tools.querytool', [
height: '100%',
isCloseable: true,
isPrivate: true,
- content: '<div id ="geometry_viewer_panel" class="sql-editor-geometry-viewer" tabindex: "0"></div>',
+ content: '<div id ="geometry_viewer_panel" class="sql-editor-geometry-viewer" tabindex= "0"></div>',
});
// Load all the created panels
@@ -313,8 +313,8 @@ define('tools.querytool', [
self.render_history_grid();
queryToolNotifications.renderNotificationsGrid(self.notifications_panel);
- var text_container = $('<textarea id="sql_query_tool" tabindex: "-1"></textarea>');
- var output_container = $('<div id="output-panel" tabindex: "0"></div>').append(text_container);
+ var text_container = $('<textarea id="sql_query_tool" tabindex="-1"></textarea>');
+ var output_container = $('<div id="output-panel" tabindex="0"></div>').append(text_container);
self.sql_panel_obj.$container.find('.pg-panel-content').append(output_container);
self.query_tool_obj = CodeMirror.fromTextArea(text_container.get(0), {
@@ -338,6 +338,10 @@ define('tools.querytool', [
scrollbarStyle: 'simple',
});
+ pgBrowser.Events.on('pgadmin:query_tool:sql_panel:focus', ()=>{
+ self.query_tool_obj.focus();
+ });
+
if (!self.preferences.new_browser_tab) {
// Listen on the panel closed event and notify user to save modifications.
_.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) {
@@ -1848,23 +1852,19 @@ define('tools.querytool', [
},
keyAction: function(event) {
- var panel_id, self = this;
- panel_id = keyboardShortcuts.processEventQueryTool(
- this.handler, queryToolActions, event
+ var panel_type='';
+
+ panel_type = keyboardShortcuts.processEventQueryTool(
+ this.handler, queryToolActions, event, this.docker
);
- // If it return panel id then focus it
- if(!_.isNull(panel_id) && !_.isUndefined(panel_id)) {
- // Returned panel index, by incrementing it by 1 we will get actual panel
- panel_id++;
- this.docker.findPanels()[panel_id].focus();
- // We set focus on history tab so we need to set the focus on
- // editor explicitly
- if(panel_id == 3) {
- setTimeout(function() { self.query_tool_obj.focus(); }, 100);
- }
+ if(!_.isNull(panel_type) && !_.isUndefined(panel_type) && panel_type != '') {
+ setTimeout(function() {
+ pgBrowser.Events.trigger(`pgadmin:query_tool:${panel_type}:focus`);
+ }, 100);
}
},
+
// Callback function for the commit button click.
on_commit_transaction: function() {
queryToolActions.executeCommit(this.handler);
@@ -2116,6 +2116,16 @@ define('tools.querytool', [
// Render the header
self.gridView.render();
+ /* wcDocker focuses on window always, and all our shortcuts are
+ * bind to editor-panel. So when we use wcDocker focus, editor-panel
+ * loses focus and events don't work.
+ */
+ $(window).on('keydown', (e)=>{
+ if(self.gridView.keyAction) {
+ self.gridView.keyAction(e);
+ }
+ });
+
if (self.is_query_tool) {
// Fetch the SQL for Scripts (eg: CREATE/UPDATE/DELETE/SELECT)
// Call AJAX only if script type url is present
diff --git a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
index 8fbc88ab..15c5d870 100644
--- a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
+++ b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
@@ -354,8 +354,8 @@ def RegisterQueryToolPreferences(self):
'shift': True,
'control': False,
'key': {
- 'key_code': 37,
- 'char': 'ArrowLeft'
+ 'key_code': 91,
+ 'char': '['
}
},
category_label=gettext('Keyboard shortcuts'),
@@ -372,8 +372,26 @@ def RegisterQueryToolPreferences(self):
'shift': True,
'control': False,
'key': {
- 'key_code': 39,
- 'char': 'ArrowRight'
+ 'key_code': 93,
+ 'char': ']'
+ }
+ },
+ category_label=gettext('Keyboard shortcuts'),
+ fields=shortcut_fields
+ )
+
+ self.preference.register(
+ 'keyboard_shortcuts',
+ 'switch_panel',
+ gettext('Switch Panel'),
+ 'keyboardshortcut',
+ {
+ 'alt': True,
+ 'shift': True,
+ 'control': False,
+ 'key': {
+ 'key_code': 9,
+ 'char': 'Tab'
}
},
category_label=gettext('Keyboard shortcuts'),
diff --git a/web/regression/javascript/common_keyboard_shortcuts_spec.js b/web/regression/javascript/common_keyboard_shortcuts_spec.js
index abe5bac5..a74f3ce5 100644
--- a/web/regression/javascript/common_keyboard_shortcuts_spec.js
+++ b/web/regression/javascript/common_keyboard_shortcuts_spec.js
@@ -8,6 +8,7 @@
//////////////////////////////////////////////////////////////////////////
import * as keyboardShortcuts from 'sources/keyboard_shortcuts';
+import $ from 'jquery';
describe('the keyboard shortcuts', () => {
const F1_KEY = 112;
@@ -45,9 +46,30 @@ describe('the keyboard shortcuts', () => {
});
describe('when user wants to goto next panel', function () {
-
- it('returns panel id', function () {
- expect(keyboardShortcuts.getInnerPanel(debuggerElementSpy, 'right')).toEqual(false);
+ let dockerSpy = {
+ '_focusFrame': {
+ '_curTab': 0,
+ '_panelList': [
+ {$container: $('<b/>'), '_type': 'type1', 'focus': function() {return true;}},
+ {$container: $('<b/>'), '_type': 'type2', 'focus': function() {return true;}},
+ ],
+ },
+ };
+ it('right key', function () {
+ dockerSpy._focusFrame._curTab = 0;
+ expect(keyboardShortcuts.focusDockerPanel(dockerSpy, 'right')).toEqual('type2');
+ });
+ it('left key', function () {
+ dockerSpy._focusFrame._curTab = 1;
+ expect(keyboardShortcuts.focusDockerPanel(dockerSpy, 'left')).toEqual('type1');
+ });
+ it('left key cycle', function () {
+ dockerSpy._focusFrame._curTab = 0;
+ expect(keyboardShortcuts.focusDockerPanel(dockerSpy, 'left')).toEqual('type2');
+ });
+ it('right key cycle', function () {
+ dockerSpy._focusFrame._curTab = 1;
+ expect(keyboardShortcuts.focusDockerPanel(dockerSpy, 'left')).toEqual('type1');
});
});
diff --git a/web/regression/javascript/pgadmin_utils_spec.js b/web/regression/javascript/pgadmin_utils_spec.js
index 39fe87df..3f72946f 100644
--- a/web/regression/javascript/pgadmin_utils_spec.js
+++ b/web/regression/javascript/pgadmin_utils_spec.js
@@ -7,7 +7,7 @@
//
//////////////////////////////////////////////////////////////
-import { getEpoch, getGCD } from 'sources/utils';
+import { getEpoch, getGCD, getMod } from 'sources/utils';
describe('getEpoch', function () {
it('should return non zero', function () {
@@ -33,3 +33,22 @@ describe('getGCD', function () {
expect(getGCD(nos)).toEqual(3);
});
});
+
+describe('getMod', function () {
+ it('complete divisible', function () {
+ expect(getMod(5,5)).toEqual(0);
+ });
+
+ it('incomplete divisible less divisor', function () {
+ expect(getMod(7,5)).toEqual(2);
+ });
+
+ it('incomplete divisible greater divisor', function () {
+ expect(getMod(5,7)).toEqual(5);
+ });
+
+ it('negative number', function () {
+ expect(getMod(-7,5)).toEqual(3);
+ });
+});
+
view thread (11+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [pgAdmin][RM4195] Tab navigation is broken in Query tool (Accessibility)
In-Reply-To: <CAM9w-_=e8+iiEP0+pTv0htRHhUjupfK6N9dFS1wQNG6MBCq4Vw@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox