Add keyboard navigation in Query tool module via Tab/Shift-Tab key. Fixes #2896

Note:
1) Once the keyboard shortcut infrastructure is ready we will add generic shortcut to focus out from CodeMirror editor and set foucs to next element, Right now there is no way of doing this, For testing purpose you can manually focus out from CodeMirror and click on data output panel to continue navigate using Tab key.
2) As of now inner panel's are not getting focused on Tab/Shift-Tab keys but once RM#2895 patch gets committed it will start working automatically as it's inherited code which will add tabindex tag automatically on each newly created wcDocker panel.
This commit is contained in:
Murtuza Zabuawala 2018-01-25 12:27:13 +00:00 committed by Dave Page
parent 5cea5f8485
commit 97760d65c2
11 changed files with 228 additions and 199 deletions

View File

@ -4,6 +4,7 @@ Keyboard Shortcuts
Keyboard shortcuts are provided in pgAdmin to allow easy access to specific functions.
**Desktop Runtime**
When running in the Desktop Runtime, the following keyboard shortcuts are available:
@ -26,6 +27,7 @@ When running in the Desktop Runtime, the following keyboard shortcuts are availa
| Ctrl+0 | Cmd+0 | Reset the zoom level |
+--------------------------+----------------+---------------------------------------+
**SQL Editors**
When using the syntax-highlighting SQL editors, the following shortcuts are available:
@ -61,6 +63,9 @@ When using the syntax-highlighting SQL editors, the following shortcuts are avai
+--------------------------+------------------+-------------------------------------+
| Shift+Tab | Shift+Tab | Un-indent selected text |
+--------------------------+------------------+-------------------------------------+
| <accesskey> + y | <accesskey> + y | Copy SQL on history panel |
+--------------------------+------------------+-------------------------------------+
**Query Tool**
@ -89,35 +94,49 @@ When using the Query Tool, the following shortcuts are available:
+--------------------------+--------------------+-----------------------------------+
| Ctrl+Shift+F | Cmd+Shift+F | Replace |
+--------------------------+--------------------+-----------------------------------+
| <accesskey> + y | <accesskey> + y | Copy SQL on history panel |
+--------------------------+--------------------+-----------------------------------+
**Debugger**
When using the Debugger, the following shortcuts are available:
+--------------------------+--------------------+-----------------------------------+
| Shortcut (Windows/Linux) | Shortcut (Mac) | Function |
+==========================+====================+===================================+
| <accesskey> + i | <accesskey> + i | Step in |
+--------------------------+--------------------+-----------------------------------+
| <accesskey> + o | <accesskey> + o | Step over |
+--------------------------+--------------------+-----------------------------------+
| <accesskey> + c | <accesskey> + c | Continue/Restart |
+--------------------------+--------------------+-----------------------------------+
| <accesskey> + t | <accesskey> + t | Toggle breakpoint |
+--------------------------+--------------------+-----------------------------------+
| <accesskey> + x | <accesskey> + x | Clear all breakpoints |
+--------------------------+--------------------+-----------------------------------+
| <accesskey> + s | <accesskey> + s | Stop |
+--------------------------+--------------------+-----------------------------------+
| Alt + Shift + g | Alt + Shift + g | Enter or Edit values in Grid |
+--------------------------+--------------------+-----------------------------------+
**Inner panel navigation**
When using the Query Tool and Debugger, the following shortcuts are available for inner panel navigation:
+--------------------------+---------------------------+------------------------------+
| Shortcut (Windows/Linux) | Shortcut (Mac) | Function |
+==========================+===========================+==============================+
| <accesskey> + i | <accesskey> + i | Step in |
+--------------------------+---------------------------+------------------------------+
| <accesskey> + o | <accesskey> + o | Step over |
+--------------------------+---------------------------+------------------------------+
| <accesskey> + c | <accesskey> + c | Continue/Restart |
+--------------------------+---------------------------+------------------------------+
| <accesskey> + t | <accesskey> + t | Toggle breakpoint |
+--------------------------+---------------------------+------------------------------+
| <accesskey> + x | <accesskey> + x | Clear all breakpoints |
+--------------------------+---------------------------+------------------------------+
| <accesskey> + s | <accesskey> + s | Stop |
+--------------------------+---------------------------+------------------------------+
| Alt + Shift + Right Arrow| Alt + Shift + Right Arrow | Move to next inner panel |
+--------------------------+---------------------------+------------------------------+
| Alt + Shift + Left Arrow | Alt + Shift + Left Arrow | Move to previous inner panel |
+--------------------------+---------------------------+------------------------------+
| Alt + Shift + g | Alt + Shift + g | Enter or Edit values in Grid |
+--------------------------+---------------------------+------------------------------+
.. note:: <accesskey> is browser and platform dependant. The following table lists the default access keys for supported browsers.
+-------------------+------------+------------+------------+
| | Windows | Linux | Mac |
+===================+============+============+============+

View File

@ -762,6 +762,7 @@ define('pgadmin.misc.explain', [
zoomInBtn = $('<button></button>', {
class: 'btn pg-explain-zoom-btn badge',
title: 'Zoom in',
tabindex: 0,
}).appendTo(zoomArea).append(
$('<i></i>', {
class: 'fa fa-search-plus',
@ -769,6 +770,7 @@ define('pgadmin.misc.explain', [
zoomToNormal = $('<button></button>', {
class: 'btn pg-explain-zoom-btn badge',
title: 'Zoom to original',
tabindex: 0,
}).appendTo(zoomArea).append(
$('<i></i>', {
class: 'fa fa-arrows-alt',
@ -776,6 +778,7 @@ define('pgadmin.misc.explain', [
zoomOutBtn = $('<button></button>', {
class: 'btn pg-explain-zoom-btn badge',
title: 'Zoom out',
tabindex: 0,
}).appendTo(zoomArea).append(
$('<i></i>', {
class: 'fa fa-search-minus',

View File

@ -1,8 +1,14 @@
import $ from 'jquery';
// Debugger
const EDIT_KEY = 71, // Key: G -> Grid values
LEFT_ARROW_KEY = 37,
RIGHT_ARROW_KEY = 39;
RIGHT_ARROW_KEY = 39,
F5_KEY = 116,
F7_KEY = 118,
F8_KEY = 119,
PERIOD_KEY = 190,
FWD_SLASH_KEY = 191;
function isMac() {
return window.navigator.platform.search('Mac') != -1;
@ -103,8 +109,62 @@ function getInnerPanel($el, direction) {
return id;
}
/* Query tool: Keyboard Shortcuts handling */
function keyboardShortcutsQueryTool(sqlEditorController, queryToolActions, event) {
if (sqlEditorController.isQueryRunning()) {
return;
}
let keyCode = event.which || event.keyCode, panel_id;
if (keyCode === F5_KEY) {
event.preventDefault();
queryToolActions.executeQuery(sqlEditorController);
} else if (event.shiftKey && keyCode === F7_KEY) {
this._stopEventPropagation(event);
queryToolActions.explainAnalyze(sqlEditorController);
} else if (keyCode === F7_KEY) {
this._stopEventPropagation(event);
queryToolActions.explain(sqlEditorController);
} else if (keyCode === F8_KEY) {
event.preventDefault();
queryToolActions.download(sqlEditorController);
} else if ((
(this.isMac() && event.metaKey) ||
(!this.isMac() && event.ctrlKey)
) && !event.altKey && event.shiftKey && keyCode === FWD_SLASH_KEY) {
this._stopEventPropagation(event);
queryToolActions.commentBlockCode(sqlEditorController);
} else if ((
(this.isMac() && !this.isKeyCtrlAltShift(event) && event.metaKey) ||
(!this.isMac() && !this.isKeyAltShift(event) && event.ctrlKey)
) && keyCode === FWD_SLASH_KEY) {
this._stopEventPropagation(event);
queryToolActions.commentLineCode(sqlEditorController);
} else if ((
(this.isMac() && !this.isKeyCtrlAltShift(event) && event.metaKey) ||
(!this.isMac() && !this.isKeyAltShift(event) && event.ctrlKey)
) && keyCode === PERIOD_KEY) {
this._stopEventPropagation(event);
queryToolActions.uncommentLineCode(sqlEditorController);
} else if (this.isAltShiftBoth(event) && keyCode === LEFT_ARROW_KEY) {
// Goto previous side panel
this._stopEventPropagation(event);
panel_id = this.getInnerPanel(
sqlEditorController.container, 'left'
);
} else if (this.isAltShiftBoth(event) && keyCode === RIGHT_ARROW_KEY) {
// Goto next side panel
this._stopEventPropagation(event);
panel_id = this.getInnerPanel(
sqlEditorController.container, 'right'
);
}
return panel_id;
}
module.exports = {
processEventDebugger: keyboardShortcutsDebugger,
processEventQueryTool: keyboardShortcutsQueryTool,
getInnerPanel: getInnerPanel,
// misc functions
_stopEventPropagation: _stopEventPropagation,

View File

@ -1,81 +0,0 @@
const F5_KEY = 116,
F7_KEY = 118,
F8_KEY = 119,
PERIOD_KEY = 190,
FWD_SLASH_KEY = 191;
function keyboardShortcuts(sqlEditorController, queryToolActions, event) {
if (sqlEditorController.isQueryRunning()) {
return;
}
let keyCode = event.which || event.keyCode;
if (keyCode === F5_KEY) {
event.preventDefault();
queryToolActions.executeQuery(sqlEditorController);
} else if (event.shiftKey && keyCode === F7_KEY) {
_stopEventPropagation();
queryToolActions.explainAnalyze(sqlEditorController);
} else if (keyCode === F7_KEY) {
_stopEventPropagation();
queryToolActions.explain(sqlEditorController);
} else if (keyCode === F8_KEY) {
event.preventDefault();
queryToolActions.download(sqlEditorController);
} else if ((
(this.isMac() && event.metaKey) ||
(!this.isMac() && event.ctrlKey)
) && !event.altKey && event.shiftKey && keyCode === FWD_SLASH_KEY) {
_stopEventPropagation();
queryToolActions.commentBlockCode(sqlEditorController);
} else if ((
(this.isMac() && !this.isKeyCtrlAltShift(event) && event.metaKey) ||
(!this.isMac() && !this.isKeyAltShift(event) && event.ctrlKey)
) && keyCode === FWD_SLASH_KEY) {
_stopEventPropagation();
queryToolActions.commentLineCode(sqlEditorController);
} else if ((
(this.isMac() && !this.isKeyCtrlAltShift(event) && event.metaKey) ||
(!this.isMac() && !this.isKeyAltShift(event) && event.ctrlKey)
) && keyCode === PERIOD_KEY) {
_stopEventPropagation();
queryToolActions.uncommentLineCode(sqlEditorController);
}
function _stopEventPropagation() {
event.cancelBubble = true;
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
}
}
function isMac() {
return window.navigator.platform.search('Mac') != -1;
}
function isKeyCtrlAlt(event) {
return event.ctrlKey || event.altKey;
}
function isKeyAltShift(event) {
return event.altKey || event.shiftKey;
}
function isKeyCtrlShift(event) {
return event.ctrlKey || event.shiftKey;
}
function isKeyCtrlAltShift(event) {
return event.ctrlKey || event.altKey || event.shiftKey;
}
module.exports = {
processEvent: keyboardShortcuts,
isMac: isMac,
isKeyCtrlAlt: isKeyCtrlAlt,
isKeyAltShift: isKeyAltShift,
isKeyCtrlShift: isKeyCtrlShift,
isKeyCtrlAltShift: isKeyCtrlAltShift,
};

View File

@ -54,10 +54,13 @@ export default class HistoryDetailQuery extends React.Component {
return (
<div id="history-detail-query">
<button className={this.copyButtonClass()}
tabIndex={0}
accessKey={'y'}
onClick={this.copyAllHandler}>{this.copyButtonText()}</button>
<CodeMirror
value={this.props.historyEntry.query}
options={{
tabindex: -1,
mode: 'text/x-pgsql',
readOnly: true,
}}

View File

@ -307,6 +307,7 @@ define('pgadmin.datagrid', [
// Apply CodeMirror to filter text area.
this.filter_obj = CodeMirror.fromTextArea($sql_filter.get(0), {
tabindex: 0,
lineNumbers: true,
mode: 'text/x-pgsql',
extraKeys: pgBrowser.editor_shortcut_keys,
@ -594,4 +595,4 @@ define('pgadmin.datagrid', [
};
return pgAdmin.DataGrid;
});
});

View File

@ -1,5 +1,5 @@
<div class="filter-textarea">
<textarea id="sql_filter" rows="5"></textarea>
<textarea id="sql_filter" rows="5" tabindex="0"></textarea>
<style>
.filter-textarea .CodeMirror-scroll {
min-height: 120px;

View File

@ -17,7 +17,6 @@
width:{% if display_connection_status -%} calc(100% - 43px)
{% else %} 100% {%- endif %};
}
</style>
<div id="main-editor_panel">
<div id="fetching_data" class="wcLoadingIconContainer sql-editor-busy-fetching hide">
@ -29,24 +28,25 @@
<div id="btn-toolbar" class="pg-prop-btn-group bg-gray-2 border-gray-3" role="toolbar" aria-label="">
<div class="btn-group" role="group" aria-label="">
<button id="btn-load-file" type="button" class="btn btn-default btn-load-file"
title="{{ _('Open File') }}" accesskey="o">
title="{{ _('Open File') }}" accesskey="o" tabindex="0">
<i class="fa fa-folder-open-o" aria-hidden="true"></i>
</button>
<button id="btn-save" type="button" class="btn btn-default" title="{{ _('Save') }}" disabled>
<i class="fa fa-floppy-o" aria-hidden="true"></i>
<i class="fa fa-floppy-o" aria-hidden="true" tabindex="0"></i>
</button>
<button id="btn-file-menu-dropdown" type="button" class="btn btn-default dropdown-toggle"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" disabled accesskey="s">
<span class="caret"></span> <span class="sr-only">Toggle Dropdown</span>
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" disabled accesskey="s"
tabindex="0">
<span class="caret"></span> <span class="sr-only">{{ _('Toggle Dropdown') }}</span>
</button>
<ul class="dropdown-menu">
<li>
<a id="btn-file-menu-save" href="#">
<a id="btn-file-menu-save" href="#" tabindex="0">
<span>{{ _('Save') }}</span>
</a>
</li>
<li>
<a id="btn-file-menu-save-as" href="#">
<a id="btn-file-menu-save-as" href="#" tabindex="0">
<span>{{ _('Save As') }}</span>
</a>
</li>
@ -54,15 +54,15 @@
</div>
<div class="btn-group" role="group" aria-label="">
<button id="btn-find" type="button" class="btn btn-default" title="{{ _('Find (Ctrl/Cmd+F)') }}">
<i class="fa fa-search" aria-hidden="true"></i>
<i class="fa fa-search" aria-hidden="true" tabindex="0"></i>
</button>
<button id="btn-find-menu-dropdown" type="button" class="btn btn-default dropdown-toggle"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" accesskey="f">
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" accesskey="f" tabindex="0">
<span class="caret"></span> <span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li>
<a id="btn-find-menu-find" href="#">
<a id="btn-find-menu-find" href="#" tabindex="0">
<span> {{ _('Find') }}{% if client_platform == 'macos' -%}
{{ _(' (Cmd+F)') }}
{% else %}
@ -70,7 +70,7 @@
</a>
</li>
<li>
<a id="btn-find-menu-find-next" href="#">
<a id="btn-find-menu-find-next" href="#" tabindex="0">
<span> {{ _('Find next') }}{% if client_platform == 'macos' -%}
{{ _(' (Cmd+G)') }}
{% else %}
@ -78,7 +78,7 @@
</a>
</li>
<li>
<a id="btn-find-menu-find-previous" href="#">
<a id="btn-find-menu-find-previous" href="#" tabindex="0">
<span> {{ _('Find previous') }}{% if client_platform == 'macos' -%}
{{ _(' (Cmd+Shift+G)') }}
{% else %}
@ -86,13 +86,13 @@
</a>
</li>
<li>
<a id="btn-find-menu-find-persistent" href="#">
<a id="btn-find-menu-find-persistent" href="#" tabindex="0">
<span>{{ _('Persistent find') }}</span>
</a>
</li>
<li class="divider"></li>
<li>
<a id="btn-find-menu-replace" href="#">
<a id="btn-find-menu-replace" href="#" tabindex="0">
<span> {{ _('Replace') }}{% if client_platform == 'macos' -%}
{{ _(' (Cmd+Shift+F)') }}
{% else %}
@ -100,13 +100,13 @@
</a>
</li>
<li>
<a id="btn-find-menu-replace-all" href="#">
<a id="btn-find-menu-replace-all" href="#" tabindex="0">
<span>{{ _('Replace all') }}</span>
</a>
</li>
<li class="divider"></li>
<li>
<a id="btn-find-menu-jump" href="#">
<a id="btn-find-menu-jump" href="#" tabindex="0">
<span>{{ _('Jump (Alt+G)') }}</span>
</a>
</li>
@ -114,48 +114,48 @@
</div>
<div class="btn-group" role="group" aria-label="">
<button id="btn-copy-row" type="button" class="btn btn-default"
title="{{ _('Copy') }}" disabled accesskey="c">
title="{{ _('Copy') }}" disabled accesskey="c" tabindex="0">
<i class="fa fa-files-o" aria-hidden="true"></i>
</button>
<button id="btn-paste-row" type="button" class="btn btn-default"
title="{{ _('Paste Row') }}" disabled accesskey="p">
title="{{ _('Paste Row') }}" disabled accesskey="p" tabindex="0">
<i class="fa fa-clipboard" aria-hidden="true"></i>
</button>
</div>
<div class="btn-group" role="group" aria-label="">
<button id="btn-delete-row" type="button" class="btn btn-default"
title="{{ _('Delete Row(s)') }}" disabled accesskey="d">
title="{{ _('Delete Row(s)') }}" disabled accesskey="d" tabindex="0">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
<div class="btn-group" role="group" aria-label="">
<button id="btn-edit-dropdown" type="button" class="btn btn-default dropdown-toggle"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
title="{{ _('Edit') }}" accesskey="e">
title="{{ _('Edit') }}" accesskey="e" tabindex="0">
<i class="fa fa-pencil-square-o" aria-hidden="true"></i>
<span class="caret"></span> <span class="sr-only">Toggle Dropdown</span>
<span class="caret"></span> <span class="sr-only">{{ _('Toggle Dropdown') }}</span>
</button>
<ul class="dropdown-menu dropdown-menu">
<ul class="dropdown-menu">
<li>
<a id="btn-indent-code" href="#">
<a id="btn-indent-code" href="#" tabindex="0">
<span> {{ _('Indent Selection (Tab)') }} </span>
</a>
<a id="btn-unindent-code" href="#">
<a id="btn-unindent-code" href="#" tabindex="0">
<span> {{ _('Unindent Selection (Shift+Tab)') }} </span>
</a>
<a id="btn-comment-line" href="#">
<a id="btn-comment-line" href="#" tabindex="0">
<span> {{ _('Inline Comment Selection') }}{% if client_platform == 'macos' -%}
{{ _(' (Cmd+/)') }}
{% else %}
{{ _(' (Ctrl+/)') }}{%- endif %}</span>
</a>
<a id="btn-uncomment-line" href="#">
<a id="btn-uncomment-line" href="#" tabindex="0">
<span> {{ _('Inline Uncomment Selection') }}{% if client_platform == 'macos' -%}
{{ _(' (Cmd+.)') }}
{% else %}
{{ _(' (Ctrl+.)') }}{%- endif %}</span>
</a>
<a id="btn-toggle-comment-block" href="#">
<a id="btn-toggle-comment-block" href="#" tabindex="0">
<span> {{ _('Block Comment/Uncomment Selection') }}{% if client_platform == 'macos' -%}
{{ _(' (Shift+Cmd+/)') }}
{% else %}
@ -166,79 +166,79 @@
</div>
<div class="btn-group" role="group" aria-label="">
<button id="btn-filter" type="button" class="btn btn-default"
title="{{ _('Filter') }}" disabled accesskey="i">
title="{{ _('Filter') }}" disabled accesskey="i" tabindex="0">
<i class="fa fa-filter" aria-hidden="true"></i>
</button>
<button id="btn-filter-dropdown" type="button" class="btn btn-default dropdown-toggle"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" disabled>
<span class="caret"></span> <span class="sr-only">Toggle Dropdown</span>
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" disabled tabindex="0">
<span class="caret"></span> <span class="sr-only">{{ _('Toggle Dropdown') }}</span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li>
<a id="btn-filter-menu" href="#">{{ _('Filter') }}</a>
<a id="btn-remove-filter" href="#">{{ _('Remove Filter') }}</a>
<a id="btn-include-filter" href="#">{{ _('By Selection') }}</a>
<a id="btn-exclude-filter" href="#">{{ _('Exclude Selection') }}</a>
<a id="btn-filter-menu" href="#" tabindex="0">{{ _('Filter') }}</a>
<a id="btn-remove-filter" href="#" tabindex="0">{{ _('Remove Filter') }}</a>
<a id="btn-include-filter" href="#" tabindex="0">{{ _('By Selection') }}</a>
<a id="btn-exclude-filter" href="#" tabindex="0">{{ _('Exclude Selection') }}</a>
</li>
</ul>
</div>
<div class="btn-group" role="group" aria-label="">
<select class="limit" style="height: 30px; width: 90px;" disabled accesskey="r">
<option value="-1">No limit</option>
<option value="1000">1000 rows</option>
<option value="500">500 rows</option>
<option value="100">100 rows</option>
<select class="limit" style="height: 30px; width: 90px;" disabled accesskey="r" tabindex="0">
<option value="-1">{{ _('No limit') }}</option>
<option value="1000">{{ _('1000 rows') }}</option>
<option value="500">{{ _('500 rows') }}</option>
<option value="100">{{ _('100 rows') }}</option>
</select>
</div>
<div class="btn-group" role="group" aria-label="">
<button id="btn-flash" type="button" class="btn btn-default" style="width: 40px;"
title="{{ _('Execute/Refresh (F5)') }}">
title="{{ _('Execute/Refresh (F5)') }}" tabindex="0">
<i class="fa fa-bolt" aria-hidden="true"></i>
</button>
<button id="btn-query-dropdown" type="button" class="btn btn-default dropdown-toggle"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" accesskey="x">
<span class="caret"></span> <span class="sr-only">Toggle Dropdown</span>
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" accesskey="x" tabindex="0">
<span class="caret"></span> <span class="sr-only">{{ _('Toggle Dropdown') }}</span>
</button>
<ul class="dropdown-menu dropdown-menu">
<ul class="dropdown-menu" role="menu">
<li>
<a id="btn-flash-menu" href="#">
<a id="btn-flash-menu" href="#" tabindex="0">
<span>{{ _('Execute/Refresh (F5)') }}</span>
</a>
</li>
<li>
<a id="btn-explain" href="#">
<a id="btn-explain" href="#" tabindex="0">
<span>{{ _('Explain (F7)') }}</span>
</a>
</li>
<li>
<a id="btn-explain-analyze" href="#">
<a id="btn-explain-analyze" href="#" tabindex="0">
<span>{{ _('Explain Analyze (Shift+F7)') }}</span>
</a>
</li>
<li class="divider"></li>
<li class="dropdown-submenu dropdown-submenu">
<a href="#">{{ _('Explain Options') }}</a>
<li class="dropdown-submenu">
<a href="#" tabindex="0">{{ _('Explain Options') }}</a>
<ul class="dropdown-menu">
<li>
<a id="btn-explain-verbose" href="#" class="noclose">
<a id="btn-explain-verbose" href="#" class="noclose" tabindex="0">
<i class="explain-verbose fa fa-check visibility-hidden" aria-hidden="true"></i>
<span> {{ _('Verbose') }} </span>
</a>
</li>
<li>
<a id="btn-explain-costs" href="#" class="noclose">
<a id="btn-explain-costs" href="#" class="noclose" tabindex="0">
<i class="explain-costs fa fa-check visibility-hidden" aria-hidden="true"></i>
<span> {{ _('Costs') }} </span>
</a>
</li>
<li>
<a id="btn-explain-buffers" href="#" class="noclose">
<a id="btn-explain-buffers" href="#" class="noclose" tabindex="0">
<i class="explain-buffers fa fa-check visibility-hidden" aria-hidden="true"></i>
<span> {{ _('Buffers') }} </span>
</a>
</li>
<li>
<a id="btn-explain-timing" href="#" class="noclose">
<a id="btn-explain-timing" href="#" class="noclose" tabindex="0">
<i class="explain-timing fa fa-check visibility-hidden" aria-hidden="true"></i>
<span> {{ _('Timing') }} </span>
</a>
@ -247,36 +247,36 @@
</li>
<li class="divider"></li>
<li>
<a id="btn-auto-commit" href="#">
<a id="btn-auto-commit" href="#" tabindex="0">
<i class="auto-commit fa fa-check visibility-hidden" aria-hidden="true"></i>
<span> {{ _('Auto commit?') }} </span>
</a>
</li>
<li>
<a id="btn-auto-rollback" href="#">
<a id="btn-auto-rollback" href="#" tabindex="0">
<i class="auto-rollback fa fa-check visibility-hidden" aria-hidden="true"></i>
<span> {{ _('Auto rollback?') }} </span>
</a>
</li>
</ul>
<button id="btn-cancel-query" type="button" class="btn btn-default" title="{{ _('Cancel query') }}"
disabled accesskey="q">
disabled accesskey="q" tabindex="0">
<i class="fa fa-stop" aria-hidden="true"></i>
</button>
</div>
<div class="btn-group" role="group" aria-label="">
<button id="btn-clear-dropdown" type="button" class="btn btn-default dropdown-toggle"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
title="{{ _('Clear') }}" accesskey="l">
title="{{ _('Clear') }}" accesskey="l" tabindex="0">
<i class="fa fa-eraser" aria-hidden="true"></i>
<span class="caret"></span> <span class="sr-only">Toggle Dropdown</span>
<span class="caret"></span> <span class="sr-only">{{ _('Toggle Dropdown') }}</span>
</button>
<ul class="dropdown-menu dropdown-menu">
<ul class="dropdown-menu">
<li>
<a id="btn-clear" href="#">
<a id="btn-clear" href="#" tabindex="0">
<span> {{ _('Clear Query Window') }} </span>
</a>
<a id="btn-clear-history" href="#">
<a id="btn-clear-history" href="#" tabindex="0">
<span> {{ _('Clear History') }} </span>
</a>
</li>
@ -284,7 +284,7 @@
</div>
<div class="btn-group" role="group" aria-label="">
<button id="btn-download" type="button" class="btn btn-default"
title="{{ _('Download as CSV (F8)') }}" accesskey="v">
title="{{ _('Download as CSV (F8)') }}" accesskey="v" tabindex="0">
<i class="fa fa-download" aria-hidden="true"></i>
</button>
</div>
@ -298,7 +298,7 @@
data-toggle="popover" data-placement="bottom"
data-content=""
data-panel-visible="visible"
accesskey="t">
accesskey="t" tabindex="0">
<i class="fa-custom fa-query-tool-disconnected" aria-hidden="true"
title="{{ _('Connection status (click for details) (<accesskey>+T)') }}">
</i>
@ -315,17 +315,17 @@
<textarea id="sql_filter" rows="5"></textarea>
</div>
<div class="btn-group">
<button id="btn-cancel" type="button" class="btn btn-danger" title="{{ _('Cancel') }}">
<button id="btn-cancel" type="button" class="btn btn-danger" title="{{ _('Cancel') }}" tabindex="0">
<i class="fa fa-times" aria-hidden="true"></i> {{ _('Cancel') }}
</button>
</div>
<div class="btn-group">
<button id="btn-apply" type="button" class="btn btn-primary" title="{{ _('Apply') }}">
<button id="btn-apply" type="button" class="btn btn-primary" title="{{ _('Apply') }}" tabindex="0">
<i class="fa fa-check" aria-hidden="true"></i> {{ _('Apply') }}
</button>
</div>
</div>
<div id="editor-panel"></div>
<div id="editor-panel" tabindex="0"></div>
<iframe id="download-csv" style="display:none"></iframe>
</div>
</div>

View File

@ -572,3 +572,8 @@ input.editor-checkbox:focus {
.connection_status .fa-query-tool-disconnected {
content: url('../img/disconnect.svg');
}
.sql-editor .dropdown-toggle:focus,
.sql-editor .copy-all:focus {
outline: 5px auto -webkit-focus-ring-color;
}

View File

@ -15,7 +15,7 @@ define('tools.querytool', [
'sources/history/index.js',
'sources/../jsx/history/query_history',
'react', 'react-dom',
'sources/sqleditor/keyboard_shortcuts',
'sources/keyboard_shortcuts',
'sources/sqleditor/query_tool_actions',
'sources/../bundle/slickgrid',
'pgadmin.file_manager',
@ -110,6 +110,7 @@ define('tools.querytool', [
self.checkConnectionStatus();
self.filter_obj = CodeMirror.fromTextArea(filter.get(0), {
tabindex: '0',
lineNumbers: true,
mode: self.handler.server_type === 'gpdb' ? 'text/x-gpsql' : 'text/x-pgsql',
foldOptions: {
@ -158,6 +159,8 @@ define('tools.querytool', [
theme: 'webcabin.overrides.css',
});
self.docker = main_docker;
var sql_panel = new pgAdmin.Browser.Panel({
name: 'sql_panel',
title: false,
@ -170,11 +173,12 @@ define('tools.querytool', [
sql_panel.load(main_docker);
var sql_panel_obj = main_docker.addPanel('sql_panel', wcDocker.DOCK.TOP);
var text_container = $('<textarea id="sql_query_tool"></textarea>');
var output_container = $('<div id="output-panel"></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);
sql_panel_obj.$container.find('.pg-panel-content').append(output_container);
self.query_tool_obj = CodeMirror.fromTextArea(text_container.get(0), {
tabindex: '0',
lineNumbers: true,
styleSelectedText: true,
mode: self.handler.server_type === 'gpdb' ? 'text/x-gpsql' : 'text/x-pgsql',
@ -218,7 +222,7 @@ define('tools.querytool', [
height: '100%',
isCloseable: false,
isPrivate: true,
content: '<div id ="datagrid" class="sql-editor-grid-container text-12"></div>',
content: '<div id ="datagrid" class="sql-editor-grid-container text-12" tabindex: "0"></div>',
});
var explain = new pgAdmin.Browser.Panel({
@ -228,7 +232,7 @@ define('tools.querytool', [
height: '100%',
isCloseable: false,
isPrivate: true,
content: '<div class="sql-editor-explain"></div>',
content: '<div class="sql-editor-explain" tabindex: "0"></div>',
});
var messages = new pgAdmin.Browser.Panel({
@ -238,7 +242,7 @@ define('tools.querytool', [
height: '100%',
isCloseable: false,
isPrivate: true,
content: '<div class="sql-editor-message"></div>',
content: '<div class="sql-editor-message" tabindex: "0"></div>',
});
var history = new pgAdmin.Browser.Panel({
@ -248,7 +252,7 @@ define('tools.querytool', [
height: '100%',
isCloseable: false,
isPrivate: true,
content: '<div id ="history_grid" class="sql-editor-history-container"></div>',
content: '<div id ="history_grid" class="sql-editor-history-container" tabindex: "0"></div>',
});
// Load all the created panels
@ -1540,8 +1544,8 @@ define('tools.querytool', [
return true;
}
).set('labels', {
ok: 'Yes',
cancel: 'No',
ok: gettext('Yes'),
cancel: gettext('No'),
});
} else {
self.query_tool_obj.setValue('');
@ -1569,8 +1573,8 @@ define('tools.querytool', [
return true;
}
).set('labels', {
ok: 'Yes',
cancel: 'No',
ok: gettext('Yes'),
cancel: gettext('No'),
});
},
@ -1698,7 +1702,22 @@ define('tools.querytool', [
},
keyAction: function(event) {
keyboardShortcuts.processEvent(this.handler, queryToolActions, event);
var panel_id, self = this;
panel_id = keyboardShortcuts.processEventQueryTool(
this.handler, queryToolActions, event
);
// 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);
}
}
},
});
@ -1827,8 +1846,8 @@ define('tools.querytool', [
return true;
}
).set('labels', {
ok: 'Yes',
cancel: 'No',
ok: gettext('Yes'),
cancel: gettext('No'),
});
} else {
self._run_query();

View File

@ -7,7 +7,7 @@
//
//////////////////////////////////////////////////////////////////////////
import keyboardShortcuts from 'sources/sqleditor/keyboard_shortcuts';
import keyboardShortcuts from 'sources/keyboard_shortcuts';
import {queryToolActions} from 'sources/sqleditor/query_tool_actions';
describe('the keyboard shortcuts', () => {
@ -57,7 +57,7 @@ describe('the keyboard shortcuts', () => {
beforeEach(() => {
event.which = F1_KEY;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('should allow event to propagate', () => {
@ -69,7 +69,7 @@ describe('the keyboard shortcuts', () => {
describe('when there is no query already running', () => {
beforeEach(() => {
event.keyCode = F5_KEY;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('should execute the query', () => {
@ -86,7 +86,7 @@ describe('the keyboard shortcuts', () => {
event.keyCode = F5_KEY;
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
expect(queryToolActionsSpy.executeQuery).not.toHaveBeenCalled();
});
@ -97,7 +97,7 @@ describe('the keyboard shortcuts', () => {
describe('when there is not a query already running', () => {
beforeEach(() => {
event.which = F7_KEY;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('should explain the query plan', () => {
@ -112,7 +112,7 @@ describe('the keyboard shortcuts', () => {
event.keyCode = F7_KEY;
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
expect(queryToolActionsSpy.explain).not.toHaveBeenCalled();
});
@ -124,7 +124,7 @@ describe('the keyboard shortcuts', () => {
beforeEach(() => {
event.shiftKey = true;
event.which = F7_KEY;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('should analyze explain the query plan', () => {
@ -140,7 +140,7 @@ describe('the keyboard shortcuts', () => {
event.which = F7_KEY;
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
expect(queryToolActionsSpy.explainAnalyze).not.toHaveBeenCalled();
});
@ -151,7 +151,7 @@ describe('the keyboard shortcuts', () => {
describe('when there is not a query already running', () => {
beforeEach(() => {
event.which = F8_KEY;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('should download the query results as a CSV', () => {
@ -168,7 +168,7 @@ describe('the keyboard shortcuts', () => {
event.keyCode = F8_KEY;
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
expect(queryToolActionsSpy.download).not.toHaveBeenCalled();
});
@ -181,7 +181,7 @@ describe('the keyboard shortcuts', () => {
beforeEach(() => {
macKeysSetup();
event.which = FWD_SLASH_KEY;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('should comment the line', () => {
@ -195,7 +195,7 @@ describe('the keyboard shortcuts', () => {
beforeEach(() => {
windowsKeysSetup();
event.which = FWD_SLASH_KEY;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('should comment the line', () => {
@ -217,7 +217,7 @@ describe('the keyboard shortcuts', () => {
});
it('does nothing', () => {
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
expect(queryToolActionsSpy.commentLineCode).not.toHaveBeenCalled();
});
@ -230,7 +230,7 @@ describe('the keyboard shortcuts', () => {
});
it('does nothing', () => {
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
expect(queryToolActionsSpy.commentLineCode).not.toHaveBeenCalled();
});
@ -244,7 +244,7 @@ describe('the keyboard shortcuts', () => {
beforeEach(() => {
macKeysSetup();
event.which = PERIOD_KEY;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('should uncomment the line', () => {
@ -257,7 +257,7 @@ describe('the keyboard shortcuts', () => {
beforeEach(() => {
windowsKeysSetup();
event.which = PERIOD_KEY;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('should uncomment the line', () => {
@ -279,7 +279,7 @@ describe('the keyboard shortcuts', () => {
});
it('does nothing', () => {
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
expect(queryToolActionsSpy.uncommentLineCode).not.toHaveBeenCalled();
});
});
@ -290,7 +290,7 @@ describe('the keyboard shortcuts', () => {
});
it('does nothing', () => {
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
expect(queryToolActionsSpy.uncommentLineCode).not.toHaveBeenCalled();
});
@ -305,7 +305,7 @@ describe('the keyboard shortcuts', () => {
macKeysSetup();
event.which = FWD_SLASH_KEY;
event.shiftKey = true;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('should comment out the block selection', () => {
@ -322,7 +322,7 @@ describe('the keyboard shortcuts', () => {
windowsKeysSetup();
event.which = FWD_SLASH_KEY;
event.shiftKey = true;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('should comment out the block selection', () => {
@ -342,7 +342,7 @@ describe('the keyboard shortcuts', () => {
macKeysSetup();
event.which = FWD_SLASH_KEY;
event.shiftKey = true;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('does nothing', () => {
expect(queryToolActionsSpy.commentBlockCode).not.toHaveBeenCalled();
@ -353,7 +353,7 @@ describe('the keyboard shortcuts', () => {
windowsKeysSetup();
event.which = FWD_SLASH_KEY;
event.shiftKey = true;
keyboardShortcuts.processEvent(sqlEditorControllerSpy, queryToolActionsSpy, event);
keyboardShortcuts.processEventQueryTool(sqlEditorControllerSpy, queryToolActionsSpy, event);
});
it('does nothing', () => {
expect(queryToolActionsSpy.commentBlockCode).not.toHaveBeenCalled();