Move shouldSuppress() method from mixin to service
Change-Id: Iffd0fa8bd09a435d4fa25a167e9ad75bb2c2da00
diff --git a/package.json b/package.json
index ebfadc8..a47ba9f 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,6 @@
"eslintfix": "npm run safe_bazelisk run polygerrit-ui/app:lint_bin -- -- --fix $(pwd)/polygerrit-ui/app",
"test:debug": "npm run compile:local && npm run safe_bazelisk run //polygerrit-ui:karma_bin -- -- start $(pwd)/polygerrit-ui/karma.conf.js --root '.ts-out/polygerrit-ui/app/' --browsers ChromeDev --no-single-run --test-files",
"test:single": "npm run compile:local && npm run safe_bazelisk run //polygerrit-ui:karma_bin -- -- start $(pwd)/polygerrit-ui/karma.conf.js --root '.ts-out/polygerrit-ui/app/' --test-files",
- "test:single:nocompile": "npm run safe_bazelisk run //polygerrit-ui:karma_bin -- -- start $(pwd)/polygerrit-ui/karma.conf.js --root '.ts-out/polygerrit-ui/app/' --test-files",
"polylint": "npm run safe_bazelisk test //polygerrit-ui/app:polylint_test",
"polylint:dev": "rm -rf ./polygerrit-ui/app/tmpl_out && npm run safe_bazelisk build //polygerrit-ui/app:template_test_tar && mkdir ./polygerrit-ui/app/tmpl_out && tar -xf bazel-bin/polygerrit-ui/app/template_test_tar.tar -C ./polygerrit-ui/app/tmpl_out"
},
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
index a2a46e0..bedaed9 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
@@ -157,6 +157,8 @@
private readonly restApiService = appContext.restApiService;
+ private readonly shortcuts = appContext.shortcutsService;
+
override keyboardShortcuts() {
return {
[Shortcut.CURSOR_NEXT_CHANGE]: '_nextChange',
@@ -407,7 +409,7 @@
}
_nextChange(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -419,7 +421,7 @@
}
_prevChange(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -431,7 +433,7 @@
}
_openChange(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -442,7 +444,7 @@
_nextPage(e: CustomKeyboardEvent) {
if (
- this.shouldSuppressKeyboardShortcut(e) ||
+ this.shortcuts.shouldSuppress(e) ||
(this.modifierPressed(e) && !isShiftPressed(e))
) {
return;
@@ -454,7 +456,7 @@
_prevPage(e: CustomKeyboardEvent) {
if (
- this.shouldSuppressKeyboardShortcut(e) ||
+ this.shortcuts.shouldSuppress(e) ||
(this.modifierPressed(e) && !isShiftPressed(e))
) {
return;
@@ -470,7 +472,7 @@
}
_toggleChangeReviewed(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -489,7 +491,7 @@
}
_refreshChangeList(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) {
+ if (this.shortcuts.shouldSuppress(e)) {
return;
}
@@ -498,7 +500,7 @@
}
_toggleChangeStar(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index 0400030..650b6d8 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -751,7 +751,7 @@
}
_handleToggleDiffMode(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -1495,7 +1495,7 @@
}
_handleOpenReplyDialog(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
this._getLoggedIn().then(isLoggedIn => {
@@ -1510,7 +1510,7 @@
}
_handleOpenDownloadDialogShortcut(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -1519,7 +1519,7 @@
}
_handleEditTopic(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -1528,7 +1528,7 @@
}
_handleOpenSubmitDialog(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || !this._submitEnabled) {
+ if (this.shortcuts.shouldSuppress(e) || !this._submitEnabled) {
return;
}
@@ -1537,7 +1537,7 @@
}
_handleToggleAttentionSet(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) {
+ if (this.shortcuts.shouldSuppress(e)) {
return;
}
if (!this._change || !this._account?._account_id) return;
@@ -1579,7 +1579,7 @@
}
_handleDiffAgainstBase(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) {
+ if (this.shortcuts.shouldSuppress(e)) {
return;
}
assertIsDefined(this._change, '_change');
@@ -1593,7 +1593,7 @@
}
_handleDiffBaseAgainstLeft(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) {
+ if (this.shortcuts.shouldSuppress(e)) {
return;
}
assertIsDefined(this._change, '_change');
@@ -1607,7 +1607,7 @@
}
_handleDiffAgainstLatest(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) {
+ if (this.shortcuts.shouldSuppress(e)) {
return;
}
assertIsDefined(this._change, '_change');
@@ -1626,7 +1626,7 @@
}
_handleDiffRightAgainstLatest(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) {
+ if (this.shortcuts.shouldSuppress(e)) {
return;
}
assertIsDefined(this._change, '_change');
@@ -1645,7 +1645,7 @@
}
_handleDiffBaseAgainstLatest(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) {
+ if (this.shortcuts.shouldSuppress(e)) {
return;
}
assertIsDefined(this._change, '_change');
@@ -1663,7 +1663,7 @@
}
_handleRefreshChange(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) {
+ if (this.shortcuts.shouldSuppress(e)) {
return;
}
e.preventDefault();
@@ -1671,7 +1671,7 @@
}
_handleToggleChangeStar(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
e.preventDefault();
@@ -1679,7 +1679,7 @@
}
_handleUpToDashboard(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -1688,7 +1688,7 @@
}
_handleExpandAllMessages(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -1699,7 +1699,7 @@
}
_handleCollapseAllMessages(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -1710,7 +1710,7 @@
}
_handleOpenDiffPrefsShortcut(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
index 74d3cfc..de24b9e 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
@@ -400,7 +400,6 @@
patchNum: 3 as RevisionPatchSetNum,
basePatchNum: 1 as BasePatchSetNum,
};
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
element._handleDiffAgainstBase(new CustomEvent('') as CustomKeyboardEvent);
assert(navigateToChangeStub.called);
const args = navigateToChangeStub.getCall(0).args;
@@ -417,7 +416,6 @@
basePatchNum: 1 as BasePatchSetNum,
patchNum: 3 as RevisionPatchSetNum,
};
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
element._handleDiffAgainstLatest(
new CustomEvent('') as CustomKeyboardEvent
);
@@ -437,7 +435,6 @@
patchNum: 3 as RevisionPatchSetNum,
basePatchNum: 1 as BasePatchSetNum,
};
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
element._handleDiffBaseAgainstLeft(
new CustomEvent('') as CustomKeyboardEvent
);
@@ -456,7 +453,6 @@
basePatchNum: 1 as BasePatchSetNum,
patchNum: 3 as RevisionPatchSetNum,
};
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
element._handleDiffRightAgainstLatest(
new CustomEvent('') as CustomKeyboardEvent
);
@@ -475,7 +471,6 @@
basePatchNum: 1 as BasePatchSetNum,
patchNum: 3 as RevisionPatchSetNum,
};
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
element._handleDiffBaseAgainstLatest(
new CustomEvent('') as CustomKeyboardEvent
);
@@ -501,7 +496,6 @@
basePatchNum: 1 as BasePatchSetNum,
patchNum: 3 as RevisionPatchSetNum,
};
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
assert.isNotOk(element._change.attention_set);
await element._getLoggedIn();
@@ -829,7 +823,6 @@
});
test('m should toggle diff mode', () => {
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
const setModeStub = sinon.stub(
element.$.fileListHeader,
'setDiffViewMode'
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index 40a2e1f..1df8f7f 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -356,6 +356,8 @@
private diffCursor = new GrDiffCursor();
+ private readonly shortcuts = appContext.shortcutsService;
+
constructor() {
super();
this.fileCursor.scrollMode = ScrollMode.KEEP_VISIBLE;
@@ -887,7 +889,7 @@
}
_handleLeftPane(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this._noDiffsExpanded()) {
+ if (this.shortcuts.shouldSuppress(e) || this._noDiffsExpanded()) {
return;
}
@@ -896,7 +898,7 @@
}
_handleRightPane(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this._noDiffsExpanded()) {
+ if (this.shortcuts.shouldSuppress(e) || this._noDiffsExpanded()) {
return;
}
@@ -906,7 +908,7 @@
_handleToggleInlineDiff(e: CustomKeyboardEvent) {
if (
- this.shouldSuppressKeyboardShortcut(e) ||
+ this.shortcuts.shouldSuppress(e) ||
this.modifierPressed(e) ||
e.detail?.keyboardEvent?.repeat ||
this.fileCursor.index === -1
@@ -919,10 +921,7 @@
}
_handleToggleAllInlineDiffs(e: CustomKeyboardEvent) {
- if (
- this.shouldSuppressKeyboardShortcut(e) ||
- e.detail?.keyboardEvent?.repeat
- ) {
+ if (this.shortcuts.shouldSuppress(e) || e.detail?.keyboardEvent?.repeat) {
return;
}
@@ -931,7 +930,7 @@
}
_handleToggleHideAllCommentThreads(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -940,7 +939,7 @@
}
_handleCursorNext(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -960,7 +959,7 @@
}
_handleCursorPrev(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -980,7 +979,7 @@
}
_handleNewComment(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
e.preventDefault();
@@ -990,7 +989,7 @@
_handleOpenLastFile(e: CustomKeyboardEvent) {
// Check for meta key to avoid overriding native chrome shortcut.
- if (this.shouldSuppressKeyboardShortcut(e) || getKeyboardEvent(e).metaKey) {
+ if (this.shortcuts.shouldSuppress(e) || getKeyboardEvent(e).metaKey) {
return;
}
@@ -1000,7 +999,7 @@
_handleOpenFirstFile(e: CustomKeyboardEvent) {
// Check for meta key to avoid overriding native chrome shortcut.
- if (this.shouldSuppressKeyboardShortcut(e) || getKeyboardEvent(e).metaKey) {
+ if (this.shortcuts.shouldSuppress(e) || getKeyboardEvent(e).metaKey) {
return;
}
@@ -1009,7 +1008,7 @@
}
_handleOpenFile(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
e.preventDefault();
@@ -1024,7 +1023,7 @@
_handleNextChunk(e: CustomKeyboardEvent) {
if (
- this.shouldSuppressKeyboardShortcut(e) ||
+ this.shortcuts.shouldSuppress(e) ||
(this.modifierPressed(e) && !isShiftPressed(e)) ||
this._noDiffsExpanded()
) {
@@ -1041,7 +1040,7 @@
_handlePrevChunk(e: CustomKeyboardEvent) {
if (
- this.shouldSuppressKeyboardShortcut(e) ||
+ this.shortcuts.shouldSuppress(e) ||
(this.modifierPressed(e) && !isShiftPressed(e)) ||
this._noDiffsExpanded()
) {
@@ -1057,7 +1056,7 @@
}
_handleToggleFileReviewed(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
@@ -1069,7 +1068,7 @@
}
_handleToggleLeftPane(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) {
+ if (this.shortcuts.shouldSuppress(e)) {
return;
}
@@ -1546,7 +1545,7 @@
}
_handleEscKey(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
return;
}
e.preventDefault();
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
index 85e6f25..be15716 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
@@ -597,8 +597,6 @@
let interact;
setup(() => {
- sinon.stub(element, 'shouldSuppressKeyboardShortcut')
- .returns(false);
sinon.stub(element, 'modifierPressed').returns(false);
const openCursorStub = sinon.stub(element, '_openCursorFile');
const openSelectedStub = sinon.stub(element, '_openSelectedFile');
@@ -1707,8 +1705,6 @@
});
test('_displayLine', () => {
- sinon.stub(element, 'shouldSuppressKeyboardShortcut')
- .callsFake(() => false);
sinon.stub(element, 'modifierPressed')
.callsFake(() => false);
element.filesExpanded = FilesExpandedState.ALL;
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
index 240bb22..18a619a 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
@@ -199,6 +199,8 @@
private readonly restApiService = appContext.restApiService;
+ private readonly shortcuts = appContext.shortcutsService;
+
constructor() {
super();
this.query = (input: string) => this._getSearchSuggestions(input);
@@ -399,7 +401,7 @@
_handleSearch(e: CustomKeyboardEvent) {
const keyboardEvent = getKeyboardEvent(e);
if (
- this.shouldSuppressKeyboardShortcut(e) ||
+ this.shortcuts.shouldSuppress(e) ||
(this.modifierPressed(e) && !keyboardEvent.shiftKey)
) {
return;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
index 978d98f..3b83fd8 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
@@ -522,7 +522,7 @@
}
_handleToggleFileReviewed(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (this.modifierPressed(e)) return;
e.preventDefault();
@@ -530,7 +530,7 @@
}
_handleEscKey(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (this.modifierPressed(e)) return;
e.preventDefault();
@@ -538,21 +538,21 @@
}
_handleLeftPane(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
e.preventDefault();
this.cursor.moveLeft();
}
_handleRightPane(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
e.preventDefault();
this.cursor.moveRight();
}
_handlePrevLineOrFileWithComments(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (
e.detail.keyboardEvent?.shiftKey &&
@@ -572,7 +572,7 @@
}
_handleVisibleLine(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
e.preventDefault();
this.cursor.moveToVisibleArea();
@@ -583,7 +583,7 @@
}
_handleNextLineOrFileWithComments(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (
e.detail.keyboardEvent?.shiftKey &&
@@ -642,7 +642,7 @@
}
_handleNewComment(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (this.modifierPressed(e)) return;
e.preventDefault();
@@ -651,7 +651,7 @@
}
_handlePrevFile(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
// Check for meta key to avoid overriding native chrome shortcut.
if (getKeyboardEvent(e).metaKey) return;
if (!this._path) return;
@@ -662,7 +662,7 @@
}
_handleNextFile(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
// Check for meta key to avoid overriding native chrome shortcut.
if (getKeyboardEvent(e).metaKey) return;
if (!this._path) return;
@@ -673,7 +673,7 @@
}
_handleNextChunkOrCommentThread(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
e.preventDefault();
if (e.detail.keyboardEvent?.shiftKey) {
@@ -734,7 +734,7 @@
}
_handlePrevChunkOrCommentThread(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
e.preventDefault();
if (e.detail.keyboardEvent?.shiftKey) {
@@ -750,7 +750,7 @@
// Similar to gr-change-view._handleOpenReplyDialog
_handleOpenReplyDialog(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (this.modifierPressed(e)) return;
this._getLoggedIn().then(isLoggedIn => {
if (!isLoggedIn) {
@@ -765,7 +765,7 @@
}
_handleToggleLeftPane(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (!e.detail.keyboardEvent?.shiftKey) return;
e.preventDefault();
@@ -773,7 +773,7 @@
}
_handleOpenDownloadDialog(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (this.modifierPressed(e)) return;
this.set('changeViewState.showDownloadDialog', true);
@@ -782,7 +782,7 @@
}
_handleUpToChange(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (this.modifierPressed(e)) return;
e.preventDefault();
@@ -790,7 +790,7 @@
}
_handleCommaKey(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (this.modifierPressed(e)) return;
if (this._diffPrefsDisabled) return;
@@ -799,7 +799,7 @@
}
_handleToggleDiffMode(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (this.modifierPressed(e)) return;
e.preventDefault();
@@ -1696,27 +1696,27 @@
}
_handleToggleBlame(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (this.modifierPressed(e)) return;
this._toggleBlame();
}
_handleToggleHideAllCommentThreads(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (this.modifierPressed(e)) return;
toggleClass(this, 'hideComments');
}
_handleOpenFileList(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (this.modifierPressed(e)) return;
this.$.dropdown.open();
}
_handleDiffAgainstBase(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (!this._change) return;
if (!this._path) return;
if (!this._patchRange) return;
@@ -1733,7 +1733,7 @@
}
_handleDiffBaseAgainstLeft(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (!this._change) return;
if (!this._path) return;
if (!this._patchRange) return;
@@ -1754,7 +1754,7 @@
}
_handleDiffAgainstLatest(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (!this._change) return;
if (!this._path) return;
if (!this._patchRange) return;
@@ -1774,7 +1774,7 @@
}
_handleDiffRightAgainstLatest(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (!this._change) return;
if (!this._path) return;
if (!this._patchRange) return;
@@ -1793,7 +1793,7 @@
}
_handleDiffBaseAgainstLatest(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
if (!this._change) return;
if (!this._path) return;
if (!this._patchRange) return;
@@ -1831,7 +1831,7 @@
}
_handleToggleAllDiffContext(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
this.$.diffHost.toggleAllContext();
}
@@ -1841,7 +1841,7 @@
}
_handleNextUnreviewedFile(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
this._setReviewed(true);
this.navigateToUnreviewedFile('next');
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
index 9aca719..a1ec6e2 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
@@ -536,7 +536,6 @@
basePatchNum: 5,
patchNum: 10,
};
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
element._handleDiffAgainstBase(new CustomEvent(''));
const args = diffNavStub.getCall(0).args;
@@ -553,7 +552,6 @@
basePatchNum: 5,
patchNum: 10,
};
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
element._handleDiffAgainstLatest(new CustomEvent(''));
const args = diffNavStub.getCall(0).args;
@@ -571,7 +569,6 @@
basePatchNum: 1,
};
element.params = {};
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
element._handleDiffBaseAgainstLeft(new CustomEvent(''));
assert(diffNavStub.called);
@@ -594,7 +591,6 @@
sinon.stub(element, '_paramsChanged');
element.params = {commentLink: true, view: GerritView.DIFF};
element._focusLineNum = 10;
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
element._handleDiffBaseAgainstLeft(new CustomEvent(''));
assert(diffNavStub.called);
@@ -613,7 +609,6 @@
basePatchNum: 1,
patchNum: 3,
};
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
element._handleDiffRightAgainstLatest(new CustomEvent(''));
assert(diffNavStub.called);
@@ -631,7 +626,6 @@
basePatchNum: 1,
patchNum: 3,
};
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
element._handleDiffBaseAgainstLatest(new CustomEvent(''));
assert(diffNavStub.called);
@@ -1483,7 +1477,6 @@
});
test('_handleToggleDiffMode', () => {
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
const e = {preventDefault: () => {}};
// Initial state.
assert.equal(element._getDiffViewMode(), 'SIDE_BY_SIDE');
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index 3bbcd5b..aa5562d 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -214,6 +214,8 @@
private readonly restApiService = appContext.restApiService;
+ private readonly shortcuts = appContext.shortcutsService;
+
override keyboardShortcuts() {
return {
[Shortcut.OPEN_SHORTCUT_HELP_DIALOG]: '_showKeyboardShortcuts',
@@ -501,6 +503,7 @@
}
_showKeyboardShortcuts(e: CustomKeyboardEvent) {
+ if (this.shortcuts.shouldSuppress(e)) return;
// same shortcut should close the dialog if pressed again
// when dialog is open
this.loadKeyboardShortcutsDialog = true;
@@ -513,9 +516,6 @@
keyboardShortcuts.cancel();
return;
}
- if (this.shouldSuppressKeyboardShortcut(e)) {
- return;
- }
keyboardShortcuts.open();
this._footerHeaderAriaHidden = true;
this._mainAriaHidden = true;
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
index fe4a66a..db4c7e0 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
@@ -226,6 +226,8 @@
readonly restApiService = appContext.restApiService;
+ private readonly shortcuts = appContext.shortcutsService;
+
constructor() {
super();
this.addEventListener('comment-update', e =>
@@ -495,7 +497,7 @@
}
_handleEKey(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) {
+ if (this.shortcuts.shouldSuppress(e)) {
return;
}
diff --git a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
index 2f249e7..d11d71e 100644
--- a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
+++ b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
@@ -15,7 +15,6 @@
* limitations under the License.
*/
import {IronA11yKeysBehavior} from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior';
-import {dom, EventApi} from '@polymer/polymer/lib/legacy/polymer.dom';
import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class';
import {property} from '@polymer/decorators';
import {PolymerElement} from '@polymer/polymer';
@@ -82,12 +81,6 @@
ShortcutSection = ShortcutSection;
- private _disableKeyboardShortcuts = false;
-
- private readonly restApiService = appContext.restApiService;
-
- private readonly reporting = appContext.reportingService;
-
private readonly shortcuts = appContext.shortcutsService;
/** Used to disable shortcuts when the element is not visible. */
@@ -118,48 +111,6 @@
);
}
- shouldSuppressKeyboardShortcut(event: CustomKeyboardEvent) {
- if (this._disableKeyboardShortcuts) return true;
- const e = getKeyboardEvent(event);
- // TODO(TS): maybe override the EventApi, narrow it down to Element always
- const target = (dom(e) as EventApi).rootTarget as Element;
- const tagName = target.tagName;
- const type = target.getAttribute('type');
- if (
- // Suppress shortcuts on <input> and <textarea>, but not on
- // checkboxes, because we want to enable workflows like 'click
- // mark-reviewed and then press ] to go to the next file'.
- (tagName === 'INPUT' && type !== 'checkbox') ||
- tagName === 'TEXTAREA' ||
- // Suppress shortcuts if the key is 'enter'
- // and target is an anchor or button or paper-tab.
- (e.keyCode === 13 &&
- (tagName === 'A' || tagName === 'BUTTON' || tagName === 'PAPER-TAB'))
- ) {
- return true;
- }
- for (let i = 0; e.path && i < e.path.length; i++) {
- // TODO(TS): narrow this down to Element from EventTarget first
- if ((e.path[i] as Element).tagName === 'GR-OVERLAY') {
- return true;
- }
- }
-
- // eg: {key: "k:keydown", ..., from: "gr-diff-view"}
- let key = `${(e as unknown as KeyboardEvent).key}:${e.type}`;
- if (this._inGoKeyMode()) key = 'g+' + key;
- if (this.inVKeyMode()) key = 'v+' + key;
- if (e.shiftKey) key = 'shift+' + key;
- if (e.ctrlKey) key = 'ctrl+' + key;
- if (e.metaKey) key = 'meta+' + key;
- if (e.altKey) key = 'alt+' + key;
- this.reporting.reportInteraction('shortcut-triggered', {
- key,
- from: this.nodeName ?? 'unknown',
- });
- return false;
- }
-
// Alias for getKeyboardEvent.
getKeyboardEvent(e: CustomKeyboardEvent) {
return getKeyboardEvent(e);
@@ -191,11 +142,6 @@
override connectedCallback() {
super.connectedCallback();
- this.restApiService.getPreferences().then(prefs => {
- if (prefs?.disable_keyboard_shortcuts) {
- this._disableKeyboardShortcuts = true;
- }
- });
this.createVisibilityObserver();
this.enableBindings();
}
@@ -292,7 +238,7 @@
}
_handleVKeyDown(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
this._shortcut_v_key_last_pressed = Date.now();
}
@@ -313,7 +259,7 @@
if (
!this.inVKeyMode() ||
!this._shortcut_v_table.has(e.detail.key) ||
- this.shouldSuppressKeyboardShortcut(e)
+ this.shortcuts.shouldSuppress(e)
) {
return;
}
@@ -327,7 +273,7 @@
}
_handleGoKeyDown(e: CustomKeyboardEvent) {
- if (this.shouldSuppressKeyboardShortcut(e)) return;
+ if (this.shortcuts.shouldSuppress(e)) return;
this._shortcut_go_key_last_pressed = Date.now();
}
@@ -350,7 +296,7 @@
if (
!this._inGoKeyMode() ||
!this._shortcut_go_table.has(e.detail.key) ||
- this.shouldSuppressKeyboardShortcut(e)
+ this.shortcuts.shouldSuppress(e)
) {
return;
}
@@ -394,7 +340,6 @@
/** The interface corresponding to KeyboardShortcutMixin */
export interface KeyboardShortcutMixinInterface {
keyboardShortcuts(): {[key: string]: string | null};
- shouldSuppressKeyboardShortcut(event: CustomKeyboardEvent): boolean;
modifierPressed(event: CustomKeyboardEvent): boolean;
}
diff --git a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin_test.ts b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin_test.ts
index 6350bf9..babe44a 100644
--- a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin_test.ts
+++ b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin_test.ts
@@ -16,11 +16,8 @@
*/
import '../../test/common-test-setup-karma';
import {KeyboardShortcutMixin} from './keyboard-shortcut-mixin';
-import {html} from '@polymer/polymer/lib/utils/html-tag';
import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {mockPromise, queryAndAssert} from '../../test/test-utils';
import '../../elements/shared/gr-overlay/gr-overlay';
-import {GrOverlay} from '../../elements/shared/gr-overlay/gr-overlay';
import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
import {CustomKeyboardEvent} from '../../types/events';
@@ -56,104 +53,14 @@
const basicFixture = fixtureFromElement('keyboard-shortcut-mixin-test-element');
-const withinOverlayFixture = fixtureFromTemplate(html`
- <gr-overlay>
- <keyboard-shortcut-mixin-test-element>
- </keyboard-shortcut-mixin-test-element>
- </gr-overlay>
-`);
-
suite('keyboard-shortcut-mixin tests', () => {
let element: GrKeyboardShortcutMixinTestElement;
- let overlay: GrOverlay;
setup(async () => {
element = basicFixture.instantiate();
- overlay = withinOverlayFixture.instantiate() as GrOverlay;
await flush();
});
- test('doesn’t block kb shortcuts for non-allowed els', async () => {
- const divEl = document.createElement('div');
- element.appendChild(divEl);
- const promise = mockPromise();
- element._handleKey = e => {
- assert.isFalse(element.shouldSuppressKeyboardShortcut(e));
- promise.resolve();
- };
- MockInteractions.keyDownOn(divEl, 75, null, 'k');
- await promise;
- });
-
- test('blocks kb shortcuts for input els', async () => {
- const inputEl = document.createElement('input');
- element.appendChild(inputEl);
- const promise = mockPromise();
- element._handleKey = e => {
- assert.isTrue(element.shouldSuppressKeyboardShortcut(e));
- promise.resolve();
- };
- MockInteractions.keyDownOn(inputEl, 75, null, 'k');
- await promise;
- });
-
- test('doesn’t block kb shortcuts for checkboxes', async () => {
- const inputEl = document.createElement('input');
- inputEl.setAttribute('type', 'checkbox');
- element.appendChild(inputEl);
- const promise = mockPromise();
- element._handleKey = e => {
- assert.isFalse(element.shouldSuppressKeyboardShortcut(e));
- promise.resolve();
- };
- MockInteractions.keyDownOn(inputEl, 75, null, 'k');
- await promise;
- });
-
- test('blocks kb shortcuts for textarea els', async () => {
- const textareaEl = document.createElement('textarea');
- element.appendChild(textareaEl);
- const promise = mockPromise();
- element._handleKey = e => {
- assert.isTrue(element.shouldSuppressKeyboardShortcut(e));
- promise.resolve();
- };
- MockInteractions.keyDownOn(textareaEl, 75, null, 'k');
- await promise;
- });
-
- test('blocks kb shortcuts for anything in a gr-overlay', async () => {
- const divEl = document.createElement('div');
- const element = queryAndAssert<GrKeyboardShortcutMixinTestElement>(
- overlay,
- 'keyboard-shortcut-mixin-test-element'
- );
- element.appendChild(divEl);
- const promise = mockPromise();
- element._handleKey = e => {
- assert.isTrue(element.shouldSuppressKeyboardShortcut(e));
- promise.resolve();
- };
- MockInteractions.keyDownOn(divEl, 75, null, 'k');
- await promise;
- });
-
- test('blocks enter shortcut on an anchor', async () => {
- const anchorEl = document.createElement('a');
- const element = queryAndAssert<GrKeyboardShortcutMixinTestElement>(
- overlay,
- 'keyboard-shortcut-mixin-test-element'
- );
- element.appendChild(anchorEl);
- const promise = mockPromise();
- element._handleKey = e => {
- assert.isTrue(element.shouldSuppressKeyboardShortcut(e));
- promise.resolve();
- };
- MockInteractions.keyDownOn(anchorEl, 13, null, 'enter');
- await promise;
- });
-
test('modifierPressed returns accurate values', () => {
const spy = sinon.spy(element, 'modifierPressed');
element._handleKey = e => {
@@ -189,7 +96,6 @@
detail: {key: 'a'},
preventDefault: () => {},
} as CustomKeyboardEvent;
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
element._shortcut_go_key_last_pressed = 9000;
element._handleGoAction(e);
assert.isTrue(handlerStub.calledOnce);
@@ -201,7 +107,6 @@
detail: {key: 'a'},
preventDefault: () => {},
} as CustomKeyboardEvent;
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
element._shortcut_go_key_last_pressed = null;
element._handleGoAction(e);
assert.isFalse(handlerStub.called);
@@ -212,29 +117,16 @@
detail: {key: 'a'},
preventDefault: () => {},
} as CustomKeyboardEvent;
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
element._shortcut_go_key_last_pressed = 3000;
element._handleGoAction(e);
assert.isFalse(handlerStub.called);
});
- test('should suppress', () => {
- const e = {
- detail: {key: 'a'},
- preventDefault: () => {},
- } as CustomKeyboardEvent;
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(true);
- element._shortcut_go_key_last_pressed = 9000;
- element._handleGoAction(e);
- assert.isFalse(handlerStub.called);
- });
-
test('unrecognized key', () => {
const e = {
detail: {key: 'f'},
preventDefault: () => {},
} as CustomKeyboardEvent;
- sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
element._shortcut_go_key_last_pressed = 9000;
element._handleGoAction(e);
assert.isFalse(handlerStub.called);
diff --git a/polygerrit-ui/app/services/app-context-init.ts b/polygerrit-ui/app/services/app-context-init.ts
index 597776d..3a6f7c5 100644
--- a/polygerrit-ui/app/services/app-context-init.ts
+++ b/polygerrit-ui/app/services/app-context-init.ts
@@ -83,6 +83,6 @@
storageService: () => new GrStorageService(),
configService: () => new ConfigService(),
userService: () => new UserService(appContext.restApiService),
- shortcutsService: () => new ShortcutsService(),
+ shortcutsService: () => new ShortcutsService(appContext.reportingService),
});
}
diff --git a/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts b/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
index 3cf46bd..c3aa06e 100644
--- a/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
+++ b/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
@@ -21,6 +21,10 @@
ShortcutSection,
SPECIAL_SHORTCUT,
} from './shortcuts-config';
+import {disableShortcuts$} from '../user/user-model';
+import {CustomKeyboardEvent} from '../../types/events';
+import {getKeyboardEvent, isElementTarget} from '../../utils/dom-util';
+import {ReportingService} from '../gr-reporting/gr-reporting';
export type SectionView = Array<{binding: string[][]; text: string}>;
@@ -35,25 +39,84 @@
* Shortcuts service, holds all hosts, bindings and listeners.
*/
export class ShortcutsService {
+ /**
+ * Keeps track of the components that are currently active such that we can
+ * show a shortcut help dialog that only shows the shortcuts that are
+ * currently relevant.
+ */
private readonly activeHosts = new Map<unknown, Map<string, string>>();
+ /** Static map built in the constructor by iterating over the config. */
private readonly bindings = new Map<Shortcut, string[]>();
private readonly listeners = new Set<ShortcutListener>();
- constructor() {
+ /** Keeps track of the corresponding user preference. */
+ private shortcutsDisabled = false;
+
+ constructor(readonly reporting?: ReportingService) {
for (const section of config.keys()) {
const items = config.get(section) ?? [];
for (const item of items) {
this.bindings.set(item.shortcut, item.bindings);
}
}
+ disableShortcuts$.subscribe(x => (this.shortcutsDisabled = x));
}
public _testOnly_isEmpty() {
return this.activeHosts.size === 0 && this.listeners.size === 0;
}
+ shouldSuppress(event: CustomKeyboardEvent) {
+ if (this.shortcutsDisabled) return true;
+ const e = getKeyboardEvent(event);
+
+ // Note that when you listen on document, then `e.currentTarget` will be the
+ // document and `e.target` will be `<gr-app>` due to shadow dom, but by
+ // using the composedPath() you can actually find the true origin of the
+ // event.
+ const rootTarget = e.composedPath()[0];
+ if (!isElementTarget(rootTarget)) return false;
+ const tagName = rootTarget.tagName;
+ const type = rootTarget.getAttribute('type');
+
+ if (
+ // Suppress shortcuts on <input> and <textarea>, but not on
+ // checkboxes, because we want to enable workflows like 'click
+ // mark-reviewed and then press ] to go to the next file'.
+ (tagName === 'INPUT' && type !== 'checkbox') ||
+ tagName === 'TEXTAREA' ||
+ // Suppress shortcuts if the key is 'enter'
+ // and target is an anchor or button or paper-tab.
+ (e.keyCode === 13 &&
+ (tagName === 'A' || tagName === 'BUTTON' || tagName === 'PAPER-TAB'))
+ ) {
+ return true;
+ }
+ for (let i = 0; e.path && i < e.path.length; i++) {
+ // TODO(TS): narrow this down to Element from EventTarget first
+ if ((e.path[i] as Element).tagName === 'GR-OVERLAY') {
+ return true;
+ }
+ }
+ // eg: {key: "k:keydown", ..., from: "gr-diff-view"}
+ let key = `${(e as unknown as KeyboardEvent).key}:${e.type}`;
+ // TODO(brohlfs): Re-enable reporting of g- and v-keys.
+ // if (this._inGoKeyMode()) key = 'g+' + key;
+ // if (this.inVKeyMode()) key = 'v+' + key;
+ if (e.shiftKey) key = 'shift+' + key;
+ if (e.ctrlKey) key = 'ctrl+' + key;
+ if (e.metaKey) key = 'meta+' + key;
+ if (e.altKey) key = 'alt+' + key;
+ let from = 'unknown';
+ if (isElementTarget(e.currentTarget)) {
+ from = e.currentTarget.tagName;
+ }
+ this.reporting?.reportInteraction('shortcut-triggered', {key, from});
+ return false;
+ }
+
createTitle(shortcutName: Shortcut, section: ShortcutSection) {
const desc = this.getDescription(section, shortcutName);
const shortcut = this.getShortcut(shortcutName);
diff --git a/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts b/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts
index a48fa92..f795f78 100644
--- a/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts
+++ b/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts
@@ -17,18 +17,90 @@
import '../../test/common-test-setup-karma';
import {ShortcutsService} from '../../services/shortcuts/shortcuts-service';
import {Shortcut, ShortcutSection} from './shortcuts-config';
+import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
+import {CustomKeyboardEvent} from '../../types/events';
+
+async function keyEventOn(
+ el: Element,
+ callback: (e: CustomKeyboardEvent) => void,
+ keyCode = 75,
+ key = 'k'
+): Promise<CustomKeyboardEvent> {
+ let resolve: (e: CustomKeyboardEvent) => void;
+ const promise = new Promise<CustomKeyboardEvent>(r => (resolve = r));
+ el.addEventListener('keydown', e => {
+ const cke = e as CustomKeyboardEvent;
+ callback(cke);
+ resolve(cke);
+ });
+ MockInteractions.keyDownOn(el, keyCode, null, key);
+ return await promise;
+}
suite('shortcuts-service tests', () => {
+ let service: ShortcutsService;
+
+ setup(() => {
+ service = new ShortcutsService();
+ });
+
+ suite('shouldSuppress', () => {
+ test('do not suppress shortcut event from <div>', async () => {
+ await keyEventOn(document.createElement('div'), e => {
+ assert.isFalse(service.shouldSuppress(e));
+ });
+ });
+
+ test('suppress shortcut event from <input>', async () => {
+ await keyEventOn(document.createElement('input'), e => {
+ assert.isTrue(service.shouldSuppress(e));
+ });
+ });
+
+ test('suppress shortcut event from <textarea>', async () => {
+ await keyEventOn(document.createElement('textarea'), e => {
+ assert.isTrue(service.shouldSuppress(e));
+ });
+ });
+
+ test('do not suppress shortcut event from checkbox <input>', async () => {
+ const inputEl = document.createElement('input');
+ inputEl.setAttribute('type', 'checkbox');
+ await keyEventOn(inputEl, e => {
+ assert.isFalse(service.shouldSuppress(e));
+ });
+ });
+
+ test('suppress shortcut event from children of <gr-overlay>', async () => {
+ const overlay = document.createElement('gr-overlay');
+ const div = document.createElement('div');
+ overlay.appendChild(div);
+ await keyEventOn(div, e => {
+ assert.isTrue(service.shouldSuppress(e));
+ });
+ });
+
+ test('suppress "enter" shortcut event from <a>', async () => {
+ await keyEventOn(document.createElement('a'), e => {
+ assert.isFalse(service.shouldSuppress(e));
+ });
+ await keyEventOn(
+ document.createElement('a'),
+ e => assert.isTrue(service.shouldSuppress(e)),
+ 13,
+ 'enter'
+ );
+ });
+ });
+
test('getShortcut', () => {
- const mgr = new ShortcutsService();
const NEXT_FILE = Shortcut.NEXT_FILE;
- assert.equal(mgr.getShortcut(NEXT_FILE), ']');
+ assert.equal(service.getShortcut(NEXT_FILE), ']');
});
test('getShortcut with modifiers', () => {
- const mgr = new ShortcutsService();
const NEXT_FILE = Shortcut.TOGGLE_LEFT_PANE;
- assert.equal(mgr.getShortcut(NEXT_FILE), 'Shift+a');
+ assert.equal(service.getShortcut(NEXT_FILE), 'Shift+a');
});
suite('binding descriptions', () => {
@@ -39,11 +111,10 @@
}
test('single combo description', () => {
- const mgr = new ShortcutsService();
- assert.deepEqual(mgr.describeBinding('a'), ['a']);
- assert.deepEqual(mgr.describeBinding('a:keyup'), ['a']);
- assert.deepEqual(mgr.describeBinding('ctrl+a'), ['Ctrl', 'a']);
- assert.deepEqual(mgr.describeBinding('ctrl+shift+up:keyup'), [
+ assert.deepEqual(service.describeBinding('a'), ['a']);
+ assert.deepEqual(service.describeBinding('a:keyup'), ['a']);
+ assert.deepEqual(service.describeBinding('ctrl+a'), ['Ctrl', 'a']);
+ assert.deepEqual(service.describeBinding('ctrl+shift+up:keyup'), [
'Ctrl',
'Shift',
'↑',
@@ -51,45 +122,48 @@
});
test('combo set description', () => {
- const mgr = new ShortcutsService();
- assert.deepEqual(mgr.describeBindings(Shortcut.GO_TO_OPENED_CHANGES), [
- ['g', 'o'],
- ]);
- assert.deepEqual(mgr.describeBindings(Shortcut.SAVE_COMMENT), [
+ assert.deepEqual(
+ service.describeBindings(Shortcut.GO_TO_OPENED_CHANGES),
+ [['g', 'o']]
+ );
+ assert.deepEqual(service.describeBindings(Shortcut.SAVE_COMMENT), [
['Ctrl', 'Enter'],
['Meta', 'Enter'],
['Ctrl', 's'],
['Meta', 's'],
]);
- assert.deepEqual(mgr.describeBindings(Shortcut.PREV_FILE), [['[']]);
+ assert.deepEqual(service.describeBindings(Shortcut.PREV_FILE), [['[']]);
});
test('combo set description width', () => {
- const mgr = new ShortcutsService();
- assert.strictEqual(mgr.comboSetDisplayWidth([['u']]), 1);
- assert.strictEqual(mgr.comboSetDisplayWidth([['g', 'o']]), 2);
- assert.strictEqual(mgr.comboSetDisplayWidth([['Shift', 'r']]), 6);
- assert.strictEqual(mgr.comboSetDisplayWidth([['x'], ['y']]), 4);
+ assert.strictEqual(service.comboSetDisplayWidth([['u']]), 1);
+ assert.strictEqual(service.comboSetDisplayWidth([['g', 'o']]), 2);
+ assert.strictEqual(service.comboSetDisplayWidth([['Shift', 'r']]), 6);
+ assert.strictEqual(service.comboSetDisplayWidth([['x'], ['y']]), 4);
assert.strictEqual(
- mgr.comboSetDisplayWidth([['x'], ['y'], ['Shift', 'z']]),
+ service.comboSetDisplayWidth([['x'], ['y'], ['Shift', 'z']]),
12
);
});
test('distribute shortcut help', () => {
- const mgr = new ShortcutsService();
- assert.deepEqual(mgr.distributeBindingDesc([['o']]), [[['o']]]);
- assert.deepEqual(mgr.distributeBindingDesc([['g', 'o']]), [[['g', 'o']]]);
+ assert.deepEqual(service.distributeBindingDesc([['o']]), [[['o']]]);
+ assert.deepEqual(service.distributeBindingDesc([['g', 'o']]), [
+ [['g', 'o']],
+ ]);
assert.deepEqual(
- mgr.distributeBindingDesc([['ctrl', 'shift', 'meta', 'enter']]),
+ service.distributeBindingDesc([['ctrl', 'shift', 'meta', 'enter']]),
[[['ctrl', 'shift', 'meta', 'enter']]]
);
assert.deepEqual(
- mgr.distributeBindingDesc([['ctrl', 'shift', 'meta', 'enter'], ['o']]),
+ service.distributeBindingDesc([
+ ['ctrl', 'shift', 'meta', 'enter'],
+ ['o'],
+ ]),
[[['ctrl', 'shift', 'meta', 'enter']], [['o']]]
);
assert.deepEqual(
- mgr.distributeBindingDesc([
+ service.distributeBindingDesc([
['ctrl', 'enter'],
['meta', 'enter'],
['ctrl', 's'],
@@ -109,11 +183,10 @@
});
test('active shortcuts by section', () => {
- const mgr = new ShortcutsService();
- assert.deepEqual(mapToObject(mgr.activeShortcutsBySection()), {});
+ assert.deepEqual(mapToObject(service.activeShortcutsBySection()), {});
- mgr.attachHost({}, new Map([[Shortcut.NEXT_FILE, 'null']]));
- assert.deepEqual(mapToObject(mgr.activeShortcutsBySection()), {
+ service.attachHost({}, new Map([[Shortcut.NEXT_FILE, 'null']]));
+ assert.deepEqual(mapToObject(service.activeShortcutsBySection()), {
[ShortcutSection.NAVIGATION]: [
{
shortcut: Shortcut.NEXT_FILE,
@@ -123,8 +196,8 @@
],
});
- mgr.attachHost({}, new Map([[Shortcut.NEXT_LINE, 'null']]));
- assert.deepEqual(mapToObject(mgr.activeShortcutsBySection()), {
+ service.attachHost({}, new Map([[Shortcut.NEXT_LINE, 'null']]));
+ assert.deepEqual(mapToObject(service.activeShortcutsBySection()), {
[ShortcutSection.DIFFS]: [
{
shortcut: Shortcut.NEXT_LINE,
@@ -141,14 +214,14 @@
],
});
- mgr.attachHost(
+ service.attachHost(
{},
new Map([
[Shortcut.SEARCH, 'null'],
[Shortcut.GO_TO_OPENED_CHANGES, 'null'],
])
);
- assert.deepEqual(mapToObject(mgr.activeShortcutsBySection()), {
+ assert.deepEqual(mapToObject(service.activeShortcutsBySection()), {
[ShortcutSection.DIFFS]: [
{
shortcut: Shortcut.NEXT_LINE,
@@ -179,11 +252,9 @@
});
test('directory view', () => {
- const mgr = new ShortcutsService();
+ assert.deepEqual(mapToObject(service.directoryView()), {});
- assert.deepEqual(mapToObject(mgr.directoryView()), {});
-
- mgr.attachHost(
+ service.attachHost(
{},
new Map([
[Shortcut.GO_TO_OPENED_CHANGES, 'null'],
@@ -193,7 +264,7 @@
[Shortcut.SEARCH, 'null'],
])
);
- assert.deepEqual(mapToObject(mgr.directoryView()), {
+ assert.deepEqual(mapToObject(service.directoryView()), {
[ShortcutSection.DIFFS]: [
{binding: [['j'], ['↓']], text: 'Go to next line'},
{
diff --git a/polygerrit-ui/app/services/user/user-model.ts b/polygerrit-ui/app/services/user/user-model.ts
index 4115a71..72ce3e1 100644
--- a/polygerrit-ui/app/services/user/user-model.ts
+++ b/polygerrit-ui/app/services/user/user-model.ts
@@ -60,3 +60,8 @@
map(preferences => preferences?.my ?? []),
distinctUntilChanged()
);
+
+export const disableShortcuts$ = preferences$.pipe(
+ map(preferences => preferences?.disable_keyboard_shortcuts ?? false),
+ distinctUntilChanged()
+);
diff --git a/polygerrit-ui/app/utils/dom-util.ts b/polygerrit-ui/app/utils/dom-util.ts
index 7b1f3e3..71f913d 100644
--- a/polygerrit-ui/app/utils/dom-util.ts
+++ b/polygerrit-ui/app/utils/dom-util.ts
@@ -37,6 +37,17 @@
return 'shadowRoot' in el;
}
+export function isElement(node: Node): node is Element {
+ return node.nodeType === 1;
+}
+
+export function isElementTarget(
+ target: EventTarget | null | undefined
+): target is Element {
+ if (!target) return false;
+ return 'nodeType' in target && isElement(target as Node);
+}
+
// TODO: maybe should have a better name for this
function getPathFromNode(el: EventTarget) {
let tagName = '';