Keyboard shortcuts to expand and collapse all comments
This change adds keyboard shortcuts to the "gr-diff-comment-thread" expand all comments when 'e' is pressed and collapse all comments when 'shift + e' is pressed. Note that the keyboard event is detected on the thread instead of the comment to minimize the number of events getting triggered. Feature: Issue 4738 Change-Id: Iab77349bd1527d7af5e05a827919a78a86909835
This commit is contained in:
@@ -29,6 +29,10 @@
|
|||||||
type: Array,
|
type: Array,
|
||||||
value: function() { return []; },
|
value: function() { return []; },
|
||||||
},
|
},
|
||||||
|
keyEventTarget: {
|
||||||
|
type: Object,
|
||||||
|
value: function() { return document.body; },
|
||||||
|
},
|
||||||
patchNum: String,
|
patchNum: String,
|
||||||
path: String,
|
path: String,
|
||||||
projectConfig: Object,
|
projectConfig: Object,
|
||||||
@@ -41,6 +45,10 @@
|
|||||||
_orderedComments: Array,
|
_orderedComments: Array,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
behaviors: [
|
||||||
|
Gerrit.KeyboardShortcutBehavior,
|
||||||
|
],
|
||||||
|
|
||||||
listeners: {
|
listeners: {
|
||||||
'comment-update': '_handleCommentUpdate',
|
'comment-update': '_handleCommentUpdate',
|
||||||
},
|
},
|
||||||
@@ -80,6 +88,22 @@
|
|||||||
this._orderedComments = this._sortedComments(this.comments);
|
this._orderedComments = this._sortedComments(this.comments);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_handleKey: function(e) {
|
||||||
|
if (this.shouldSupressKeyboardShortcut(e)) { return; }
|
||||||
|
if (e.keyCode === 69) { // 'e'
|
||||||
|
e.preventDefault();
|
||||||
|
this._expandCollapseComments(e.shiftKey);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_expandCollapseComments: function(actionIsCollapse) {
|
||||||
|
var comments =
|
||||||
|
Polymer.dom(this.root).querySelectorAll('gr-diff-comment');
|
||||||
|
comments.forEach(function(comment) {
|
||||||
|
comment.collapsed = actionIsCollapse;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
_sortedComments: function(comments) {
|
_sortedComments: function(comments) {
|
||||||
comments.sort(function(c1, c2) {
|
comments.sort(function(c1, c2) {
|
||||||
var c1Date = c1.__date || util.parseDate(c1.updated);
|
var c1Date = c1.__date || util.parseDate(c1.updated);
|
||||||
|
@@ -54,30 +54,25 @@ limitations under the License.
|
|||||||
message: 'i like you, too',
|
message: 'i like you, too',
|
||||||
in_reply_to: 'sallys_confession',
|
in_reply_to: 'sallys_confession',
|
||||||
updated: '2015-12-25 15:00:20.396000000',
|
updated: '2015-12-25 15:00:20.396000000',
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'sallys_confession',
|
id: 'sallys_confession',
|
||||||
message: 'i like you, jack',
|
message: 'i like you, jack',
|
||||||
updated: '2015-12-24 15:00:20.396000000',
|
updated: '2015-12-24 15:00:20.396000000',
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'sally_to_dr_finklestein',
|
id: 'sally_to_dr_finklestein',
|
||||||
message: 'i’m running away',
|
message: 'i’m running away',
|
||||||
updated: '2015-10-31 09:00:20.396000000',
|
updated: '2015-10-31 09:00:20.396000000',
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'sallys_defiance',
|
id: 'sallys_defiance',
|
||||||
in_reply_to: 'sally_to_dr_finklestein',
|
in_reply_to: 'sally_to_dr_finklestein',
|
||||||
message: 'i will poison you so i can get away',
|
message: 'i will poison you so i can get away',
|
||||||
updated: '2015-10-31 15:00:20.396000000',
|
updated: '2015-10-31 15:00:20.396000000',
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'dr_finklesteins_response',
|
id: 'dr_finklesteins_response',
|
||||||
in_reply_to: 'sally_to_dr_finklestein',
|
in_reply_to: 'sally_to_dr_finklestein',
|
||||||
message: 'no i will pull a thread and your arm will fall off',
|
message: 'no i will pull a thread and your arm will fall off',
|
||||||
updated: '2015-10-31 11:00:20.396000000'
|
updated: '2015-10-31 11:00:20.396000000'
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'sallys_mission',
|
id: 'sallys_mission',
|
||||||
message: 'i have to find santa',
|
message: 'i have to find santa',
|
||||||
updated: '2015-12-24 21:00:20.396000000'
|
updated: '2015-12-24 21:00:20.396000000'
|
||||||
@@ -89,31 +84,26 @@ limitations under the License.
|
|||||||
id: 'sally_to_dr_finklestein',
|
id: 'sally_to_dr_finklestein',
|
||||||
message: 'i’m running away',
|
message: 'i’m running away',
|
||||||
updated: '2015-10-31 09:00:20.396000000',
|
updated: '2015-10-31 09:00:20.396000000',
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'dr_finklesteins_response',
|
id: 'dr_finklesteins_response',
|
||||||
in_reply_to: 'sally_to_dr_finklestein',
|
in_reply_to: 'sally_to_dr_finklestein',
|
||||||
message: 'no i will pull a thread and your arm will fall off',
|
message: 'no i will pull a thread and your arm will fall off',
|
||||||
updated: '2015-10-31 11:00:20.396000000'
|
updated: '2015-10-31 11:00:20.396000000'
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'sallys_defiance',
|
id: 'sallys_defiance',
|
||||||
in_reply_to: 'sally_to_dr_finklestein',
|
in_reply_to: 'sally_to_dr_finklestein',
|
||||||
message: 'i will poison you so i can get away',
|
message: 'i will poison you so i can get away',
|
||||||
updated: '2015-10-31 15:00:20.396000000',
|
updated: '2015-10-31 15:00:20.396000000',
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'sallys_confession',
|
id: 'sallys_confession',
|
||||||
message: 'i like you, jack',
|
message: 'i like you, jack',
|
||||||
updated: '2015-12-24 15:00:20.396000000',
|
updated: '2015-12-24 15:00:20.396000000',
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'jacks_reply',
|
id: 'jacks_reply',
|
||||||
message: 'i like you, too',
|
message: 'i like you, too',
|
||||||
in_reply_to: 'sallys_confession',
|
in_reply_to: 'sallys_confession',
|
||||||
updated: '2015-12-25 15:00:20.396000000',
|
updated: '2015-12-25 15:00:20.396000000',
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'sallys_mission',
|
id: 'sallys_mission',
|
||||||
message: 'i have to find santa',
|
message: 'i have to find santa',
|
||||||
updated: '2015-12-24 21:00:20.396000000'
|
updated: '2015-12-24 21:00:20.396000000'
|
||||||
@@ -247,20 +237,17 @@ limitations under the License.
|
|||||||
message: 'i like you, too',
|
message: 'i like you, too',
|
||||||
in_reply_to: 'sallys_confession',
|
in_reply_to: 'sallys_confession',
|
||||||
updated: '2015-12-25 15:00:20.396000000',
|
updated: '2015-12-25 15:00:20.396000000',
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'sallys_confession',
|
id: 'sallys_confession',
|
||||||
in_reply_to: 'nonexistent_comment',
|
in_reply_to: 'nonexistent_comment',
|
||||||
message: 'i like you, jack',
|
message: 'i like you, jack',
|
||||||
updated: '2015-12-24 15:00:20.396000000',
|
updated: '2015-12-24 15:00:20.396000000',
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'sally_to_dr_finklestein',
|
id: 'sally_to_dr_finklestein',
|
||||||
in_reply_to: 'nonexistent_comment',
|
in_reply_to: 'nonexistent_comment',
|
||||||
message: 'i’m running away',
|
message: 'i’m running away',
|
||||||
updated: '2015-10-31 09:00:20.396000000',
|
updated: '2015-10-31 09:00:20.396000000',
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
id: 'sallys_defiance',
|
id: 'sallys_defiance',
|
||||||
message: 'i will poison you so i can get away',
|
message: 'i will poison you so i can get away',
|
||||||
updated: '2015-10-31 15:00:20.396000000',
|
updated: '2015-10-31 15:00:20.396000000',
|
||||||
@@ -268,5 +255,37 @@ limitations under the License.
|
|||||||
element.comments = comments;
|
element.comments = comments;
|
||||||
assert.equal(4, element._orderedComments.length);
|
assert.equal(4, element._orderedComments.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('keyboard shortcuts', function() {
|
||||||
|
var comments = [
|
||||||
|
{
|
||||||
|
id: 'jacks_reply',
|
||||||
|
message: 'i like you, too',
|
||||||
|
in_reply_to: 'sallys_confession',
|
||||||
|
updated: '2015-12-25 15:00:20.396000000',
|
||||||
|
}, {
|
||||||
|
id: 'sallys_confession',
|
||||||
|
in_reply_to: 'nonexistent_comment',
|
||||||
|
message: 'i like you, jack',
|
||||||
|
updated: '2015-12-24 15:00:20.396000000',
|
||||||
|
}, {
|
||||||
|
id: 'sally_to_dr_finklestein',
|
||||||
|
in_reply_to: 'nonexistent_comment',
|
||||||
|
message: 'i’m running away',
|
||||||
|
updated: '2015-10-31 09:00:20.396000000',
|
||||||
|
}, {
|
||||||
|
id: 'sallys_defiance',
|
||||||
|
message: 'i will poison you so i can get away',
|
||||||
|
updated: '2015-10-31 15:00:20.396000000',
|
||||||
|
}];
|
||||||
|
element.comments = comments;
|
||||||
|
var expandCollapseStub = sinon.stub(element, '_expandCollapseComments');
|
||||||
|
MockInteractions.pressAndReleaseKeyOn(element, 69); // 'e'
|
||||||
|
assert.isTrue(expandCollapseStub.lastCall.calledWith(false));
|
||||||
|
|
||||||
|
MockInteractions.pressAndReleaseKeyOn(element, 69, 'shift'); // 'e'
|
||||||
|
assert.isTrue(expandCollapseStub.lastCall.calledWith(true));
|
||||||
|
expandCollapseStub.restore();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -170,9 +170,9 @@ limitations under the License.
|
|||||||
<div class="show-hide">
|
<div class="show-hide">
|
||||||
<label class="show-hide">
|
<label class="show-hide">
|
||||||
<input type="checkbox" class="show-hide"
|
<input type="checkbox" class="show-hide"
|
||||||
checked$="[[_commentCollapsed]]"
|
checked$="[[collapsed]]"
|
||||||
on-change="_handleToggleCollapsed">
|
on-change="_handleToggleCollapsed">
|
||||||
[[_computeShowHideText(_commentCollapsed)]]
|
[[_computeShowHideText(collapsed)]]
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -186,7 +186,7 @@ limitations under the License.
|
|||||||
<gr-linked-text class="message"
|
<gr-linked-text class="message"
|
||||||
pre
|
pre
|
||||||
content="[[comment.message]]"
|
content="[[comment.message]]"
|
||||||
collapsed="[[_commentCollapsed]]"
|
collapsed="[[collapsed]]"
|
||||||
config="[[projectConfig.commentlinks]]"></gr-linked-text>
|
config="[[projectConfig.commentlinks]]"></gr-linked-text>
|
||||||
<div class="actions" hidden$="[[!showActions]]">
|
<div class="actions" hidden$="[[!showActions]]">
|
||||||
<gr-button class="action reply" on-tap="_handleReply">Reply</gr-button>
|
<gr-button class="action reply" on-tap="_handleReply">Reply</gr-button>
|
||||||
|
@@ -81,7 +81,7 @@
|
|||||||
},
|
},
|
||||||
patchNum: String,
|
patchNum: String,
|
||||||
showActions: Boolean,
|
showActions: Boolean,
|
||||||
_commentCollapsed: {
|
collapsed: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: true,
|
value: true,
|
||||||
observer: '_toggleCollapseClass',
|
observer: '_toggleCollapseClass',
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
|
|
||||||
attached: function() {
|
attached: function() {
|
||||||
if (this.editing) {
|
if (this.editing) {
|
||||||
this._commentCollapsed = false;
|
this.collapsed = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -226,11 +226,11 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
_handleToggleCollapsed: function() {
|
_handleToggleCollapsed: function() {
|
||||||
this._commentCollapsed = !this._commentCollapsed;
|
this.collapsed = !this.collapsed;
|
||||||
},
|
},
|
||||||
|
|
||||||
_toggleCollapseClass: function(_commentCollapsed) {
|
_toggleCollapseClass: function(collapsed) {
|
||||||
if (_commentCollapsed) {
|
if (collapsed) {
|
||||||
this.$.container.classList.add('collapsed');
|
this.$.container.classList.add('collapsed');
|
||||||
} else {
|
} else {
|
||||||
this.$.container.classList.remove('collapsed');
|
this.$.container.classList.remove('collapsed');
|
||||||
|
@@ -66,6 +66,7 @@ limitations under the License.
|
|||||||
|
|
||||||
test('collapsible comments', function() {
|
test('collapsible comments', function() {
|
||||||
// When a comment (not draft) is loaded, it should be collapsed
|
// When a comment (not draft) is loaded, it should be collapsed
|
||||||
|
assert.isTrue(element.collapsed);
|
||||||
assert.isFalse(isVisible(element.$$('gr-linked-text')),
|
assert.isFalse(isVisible(element.$$('gr-linked-text')),
|
||||||
'gr-linked-text is not visible');
|
'gr-linked-text is not visible');
|
||||||
assert.isFalse(isVisible(element.$$('.actions')),
|
assert.isFalse(isVisible(element.$$('.actions')),
|
||||||
@@ -80,6 +81,7 @@ limitations under the License.
|
|||||||
|
|
||||||
// When the header row is clicked, the comment should expand
|
// When the header row is clicked, the comment should expand
|
||||||
MockInteractions.tap(element.$.header);
|
MockInteractions.tap(element.$.header);
|
||||||
|
assert.isFalse(element.collapsed);
|
||||||
assert.isTrue(isVisible(element.$$('gr-linked-text')),
|
assert.isTrue(isVisible(element.$$('gr-linked-text')),
|
||||||
'gr-linked-text is visible');
|
'gr-linked-text is visible');
|
||||||
assert.isTrue(isVisible(element.$$('.actions')),
|
assert.isTrue(isVisible(element.$$('.actions')),
|
||||||
@@ -124,6 +126,29 @@ limitations under the License.
|
|||||||
'Should navigate to ' + dest + ' without triggering nav');
|
'Should navigate to ' + dest + ' without triggering nav');
|
||||||
showStub.restore();
|
showStub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('comment expand and collapse', function() {
|
||||||
|
element.collapsed = true;
|
||||||
|
assert.isFalse(isVisible(element.$$('gr-linked-text')),
|
||||||
|
'gr-linked-text is not visible');
|
||||||
|
assert.isFalse(isVisible(element.$$('.actions')),
|
||||||
|
'actions are not visible');
|
||||||
|
assert.isFalse(isVisible(element.$$('iron-autogrow-textarea')),
|
||||||
|
'textarea is not visible');
|
||||||
|
assert.isTrue(isVisible(element.$$('.collapsedContent')),
|
||||||
|
'header middle content is visible');
|
||||||
|
|
||||||
|
element.collapsed = false;
|
||||||
|
assert.isFalse(element.collapsed);
|
||||||
|
assert.isTrue(isVisible(element.$$('gr-linked-text')),
|
||||||
|
'gr-linked-text is visible');
|
||||||
|
assert.isTrue(isVisible(element.$$('.actions')),
|
||||||
|
'actions are visible');
|
||||||
|
assert.isFalse(isVisible(element.$$('iron-autogrow-textarea')),
|
||||||
|
'textarea is not visible');
|
||||||
|
assert.isFalse(isVisible(element.$$('.collapsedContent')),
|
||||||
|
'header middle content is is not visible');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
suite('gr-diff-comment draft tests', function() {
|
suite('gr-diff-comment draft tests', function() {
|
||||||
@@ -213,6 +238,7 @@ limitations under the License.
|
|||||||
assert.ok(e.detail.comment);
|
assert.ok(e.detail.comment);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
assert.isTrue(element.collapsed);
|
||||||
assert.isFalse(isVisible(element.$$('gr-linked-text')),
|
assert.isFalse(isVisible(element.$$('gr-linked-text')),
|
||||||
'gr-linked-text is not visible');
|
'gr-linked-text is not visible');
|
||||||
assert.isFalse(isVisible(element.$$('.actions')),
|
assert.isFalse(isVisible(element.$$('.actions')),
|
||||||
@@ -223,6 +249,7 @@ limitations under the License.
|
|||||||
'header middle content is visible');
|
'header middle content is visible');
|
||||||
|
|
||||||
MockInteractions.tap(element.$.header);
|
MockInteractions.tap(element.$.header);
|
||||||
|
assert.isFalse(element.collapsed);
|
||||||
assert.isTrue(isVisible(element.$$('gr-linked-text')),
|
assert.isTrue(isVisible(element.$$('gr-linked-text')),
|
||||||
'gr-linked-text is visible');
|
'gr-linked-text is visible');
|
||||||
assert.isTrue(isVisible(element.$$('.actions')),
|
assert.isTrue(isVisible(element.$$('.actions')),
|
||||||
@@ -235,6 +262,7 @@ limitations under the License.
|
|||||||
// When the edit button is pressed, should still see the actions
|
// When the edit button is pressed, should still see the actions
|
||||||
// and also textarea
|
// and also textarea
|
||||||
MockInteractions.tap(element.$$('.edit'));
|
MockInteractions.tap(element.$$('.edit'));
|
||||||
|
assert.isFalse(element.collapsed);
|
||||||
assert.isFalse(isVisible(element.$$('gr-linked-text')),
|
assert.isFalse(isVisible(element.$$('gr-linked-text')),
|
||||||
'gr-linked-text is not visible');
|
'gr-linked-text is not visible');
|
||||||
assert.isTrue(isVisible(element.$$('.actions')),
|
assert.isTrue(isVisible(element.$$('.actions')),
|
||||||
@@ -247,6 +275,7 @@ limitations under the License.
|
|||||||
// When toggle again, everything should be hidden except for textarea
|
// When toggle again, everything should be hidden except for textarea
|
||||||
// and header middle content should be visible
|
// and header middle content should be visible
|
||||||
MockInteractions.tap(element.$.header);
|
MockInteractions.tap(element.$.header);
|
||||||
|
assert.isTrue(element.collapsed);
|
||||||
assert.isFalse(isVisible(element.$$('gr-linked-text')),
|
assert.isFalse(isVisible(element.$$('gr-linked-text')),
|
||||||
'gr-linked-text is not visible');
|
'gr-linked-text is not visible');
|
||||||
assert.isFalse(isVisible(element.$$('.actions')),
|
assert.isFalse(isVisible(element.$$('.actions')),
|
||||||
|
Reference in New Issue
Block a user