Merge "Reindex when edit reference is updated"
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
index b7a76a2..f54cfc9 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
@@ -19,6 +19,7 @@
<link rel="import" href="../../../behaviors/rest-client-behavior.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
+<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -38,7 +39,8 @@
section {
display: inline-block;
}
- gr-button {
+ gr-button,
+ gr-dropdown {
margin-left: .5em;
}
gr-button:before {
@@ -63,29 +65,29 @@
}
</style>
<div>
- <section hidden$="[[_shouldHideActions(actions.*, _additionalActions.*, _loading)]]">
- <template is="dom-repeat" items="[[_changeActionValues]]" as="action">
+ <gr-dropdown
+ id="moreActions"
+ down-arrow
+ vertical-offset="32"
+ horizontal-align="right"
+ on-tap-item-abandon="_handleAbandonTap"
+ on-tap-item-cherrypick="_handleCherrypickTap"
+ on-tap-item-delete="_handleDeleteTap"
+ hidden$="[[_shouldHideActions(_menuActions.*, _loading)]]"
+ items="[[_menuActions]]">More</gr-dropdown>
+ <section hidden$="[[_shouldHideActions(_topLevelActions.*, _loading)]]">
+ <template
+ is="dom-repeat"
+ items="[[_topLevelActions]]"
+ as="action">
<gr-button title$="[[action.title]]"
- hidden$="[[_computeActionHidden(action.__key, _hiddenChangeActions.*)]]"
+ hidden$="[[_computeActionHidden(action.__key, _hiddenActions.*)]]"
primary$="[[action.__primary]]"
- hidden$="[[!action.enabled]]"
data-action-key$="[[action.__key]]"
data-action-type$="[[action.__type]]"
data-label$="[[action.label]]"
data-loading-label$="[[_computeLoadingLabel(action.__key)]]"
- on-tap="_handleActionTap"></gr-button>
- </template>
- </section>
- <section hidden$="[[_shouldHideActions(revisionActions.*, _additionalActions.*, _loading)]]">
- <template is="dom-repeat" items="[[_revisionActionValues]]" as="action">
- <gr-button title$="[[action.title]]"
- hidden$="[[_computeActionHidden(action.__key, _hiddenRevisionActions.*)]]"
- primary$="[[action.__primary]]"
disabled$="[[!action.enabled]]"
- data-action-key$="[[action.__key]]"
- data-action-type$="[[action.__type]]"
- data-label$="[[action.label]]"
- data-loading-label$="[[_computeLoadingLabel(action.__key)]]"
on-tap="_handleActionTap"></gr-button>
</template>
</section>
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index 6fdc3ad..331c3c5 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -87,6 +87,16 @@
method: 'POST',
};
+ /**
+ * Keys for actions to appear in the overflow menu rather than the top-level
+ * set of action buttons.
+ */
+ var MENU_ACTION_KEYS = [
+ 'abandon',
+ 'cherrypick',
+ '/', // '/' is the key for the delete action.
+ ];
+
Polymer({
is: 'gr-change-actions',
@@ -96,6 +106,12 @@
* @event reload-change
*/
+ /**
+ * Fired when an action is tapped.
+ *
+ * @event <action key>-tap
+ */
+
properties: {
change: Object,
actions: {
@@ -129,25 +145,26 @@
type: Boolean,
value: true,
},
- _revisionActionValues: {
+
+ _allActionValues: {
type: Array,
- computed: '_computeRevisionActionValues(revisionActions.*, ' +
- 'primaryActionKeys.*, _additionalActions.*)',
- },
- _changeActionValues: {
- type: Array,
- computed: '_computeChangeActionValues(actions.*, ' +
+ computed: '_computeAllActions(actions.*, revisionActions.*,' +
'primaryActionKeys.*, _additionalActions.*, change)',
},
+ _topLevelActions: {
+ type: Array,
+ computed: '_computeTopLevelActions(_allActionValues.*)',
+ },
+ _menuActions: {
+ type: Array,
+ computed: '_computeMenuActions(_allActionValues.*, _hiddenActions.*)',
+ },
+
_additionalActions: {
type: Array,
value: function() { return []; },
},
- _hiddenChangeActions: {
- type: Array,
- value: function() { return []; },
- },
- _hiddenRevisionActions: {
+ _hiddenActions: {
type: Array,
value: function() { return []; },
},
@@ -220,20 +237,15 @@
},
setActionHidden: function(type, key, hidden) {
- var path;
- if (type === ActionType.CHANGE) {
- path = '_hiddenChangeActions';
- } else if (type === ActionType.REVISION) {
- path = '_hiddenRevisionActions';
- } else {
+ if (type !== ActionType.CHANGE && type !== ActionType.REVISION) {
throw Error('Invalid action type given: ' + type);
}
- var idx = this.get(path).indexOf(key);
+ var idx = this._hiddenActions.indexOf(key);
if (hidden && idx === -1) {
- this.push(path, key);
+ this.push('_hiddenActions', key);
} else if (!hidden && idx !== -1) {
- this.splice(path, idx, 1);
+ this.splice('_hiddenActions', idx, 1);
}
},
@@ -251,16 +263,8 @@
this.patchNum);
},
- _shouldHideActions: function(actionsRecord, additionalActionsRecord,
- loading) {
- if (loading) { return true; }
- return !this._actionCount(actionsRecord, additionalActionsRecord);
- },
-
- _actionCount: function(actionsRecord, additionalActionsRecord) {
- var additionalActions = (additionalActionsRecord &&
- additionalActionsRecord.base) || [];
- return this._keyCount(actionsRecord) + additionalActions.length;
+ _shouldHideActions: function(actions, loading) {
+ return loading || !actions || !actions.base || !actions.base.length;
},
_keyCount: function(changeRecord) {
@@ -282,24 +286,6 @@
});
},
- _computeRevisionActionValues: function(actionsChangeRecord,
- primariesChangeRecord, additionalActionsChangeRecord) {
- return this._getActionValues(actionsChangeRecord, primariesChangeRecord,
- additionalActionsChangeRecord, ActionType.REVISION);
- },
-
- _computeChangeActionValues: function(actionsChangeRecord,
- primariesChangeRecord, additionalActionsChangeRecord, change) {
- var actions = this._getActionValues(
- actionsChangeRecord, primariesChangeRecord,
- additionalActionsChangeRecord, ActionType.CHANGE, change);
- var quickApprove = this._getQuickApproveAction();
- if (quickApprove) {
- actions.unshift(quickApprove);
- }
- return actions;
- },
-
_getLabelStatus: function(label) {
if (label.approved) {
return LabelStatus.OK;
@@ -466,14 +452,10 @@
var type = el.getAttribute('data-action-type');
if (type === ActionType.REVISION) {
this._handleRevisionAction(key);
- } else if (key === ChangeActions.DELETE) {
- this._showActionDialog(this.$.confirmDeleteDialog);
} else if (key === ChangeActions.REVERT) {
this.showRevertDialog();
- } else if (key === ChangeActions.ABANDON) {
- this._showActionDialog(this.$.confirmAbandonDialog);
} else if (key === QUICK_APPROVE_ACTION.key) {
- var action = this._changeActionValues.find(function(o) {
+ var action = this._allActionValues.find(function(o) {
return o.key === key;
});
this._fireAction(
@@ -488,10 +470,6 @@
case RevisionActions.REBASE:
this._showActionDialog(this.$.confirmRebase);
break;
- case RevisionActions.CHERRYPICK:
- this.$.confirmCherrypick.branch = '';
- this._showActionDialog(this.$.confirmCherrypick);
- break;
case RevisionActions.SUBMIT:
if (!this._canSubmitChange()) {
return;
@@ -664,5 +642,92 @@
return response;
}.bind(this)).then(this._handleResponseError.bind(this));
},
+
+ _handleAbandonTap: function() {
+ this._showActionDialog(this.$.confirmAbandonDialog);
+ },
+
+ _handleCherrypickTap: function() {
+ this.$.confirmCherrypick.branch = '';
+ this._showActionDialog(this.$.confirmCherrypick);
+ },
+
+ _handleDeleteTap: function() {
+ this._showActionDialog(this.$.confirmDeleteDialog);
+ },
+
+ /**
+ * Merge sources of change actions into a single ordered array of action
+ * values.
+ * @param {splices} changeActionsRecord
+ * @param {splices} revisionActionsRecord
+ * @param {splices} primariesRecord
+ * @param {splices} additionalActionsRecord
+ * @param {Object} change The change object.
+ * @return {Array}
+ */
+ _computeAllActions: function(changeActionsRecord, revisionActionsRecord,
+ primariesRecord, additionalActionsRecord, change) {
+ var revisionActionValues = this._getActionValues(revisionActionsRecord,
+ primariesRecord, additionalActionsRecord, ActionType.REVISION);
+ var changeActionValues = this._getActionValues(changeActionsRecord,
+ primariesRecord, additionalActionsRecord, ActionType.CHANGE, change);
+ var quickApprove = this._getQuickApproveAction();
+ if (quickApprove) {
+ changeActionValues.unshift(quickApprove);
+ }
+ return revisionActionValues
+ .concat(changeActionValues)
+ .sort(this._actionComparator);
+ },
+
+ /**
+ * Sort comparator to define the order of change actions.
+ */
+ _actionComparator: function(actionA, actionB) {
+ // The code review action always appears first.
+ if (actionA.__key === 'review') {
+ return -1;
+ } else if (actionB.__key === 'review') {
+ return 1;
+ }
+
+ // Primary actions always appear last.
+ if (actionA.__primary) {
+ return 1;
+ } else if (actionB.__primary) {
+ return -1;
+ }
+
+ // Change actions appear before revision actions.
+ if (actionA.__type === 'change' && actionB.__type === 'revision') {
+ return -1;
+ } else if (actionA.__type === 'revision' && actionB.__type === 'change') {
+ return 1;
+ }
+
+ // Otherwise, sort by the button label.
+ return actionA.label > actionB.label ? 1 : -1;
+ },
+
+ _computeTopLevelActions: function(actionRecord) {
+ return actionRecord.base.filter(function(a) {
+ return MENU_ACTION_KEYS.indexOf(a.__key) === -1;
+ });
+ },
+
+ _computeMenuActions: function(actionRecord, hiddenActionsRecord) {
+ var hiddenActions = hiddenActionsRecord.base || [];
+ return actionRecord.base
+ .filter(function(a) {
+ return MENU_ACTION_KEYS.indexOf(a.__key) !== -1 &&
+ hiddenActions.indexOf(a.__key) === -1;
+ })
+ .map(function(action) {
+ var key = action.__key;
+ if (key === '/') { key = 'delete'; }
+ return {name: action.label, id: key, };
+ });
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index 7dbd903..dbdf00f 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -99,11 +99,9 @@
});
test('_shouldHideActions', function() {
- assert.isTrue(element._shouldHideActions(undefined, undefined, true));
- assert.isTrue(element._shouldHideActions({base: {}},
- {base: []}, false));
- assert.isFalse(element._shouldHideActions({base: { test: 'test' }},
- {base: ['test']}, false));
+ assert.isTrue(element._shouldHideActions(undefined, true));
+ assert.isTrue(element._shouldHideActions({base: {}}, false));
+ assert.isFalse(element._shouldHideActions({base: ['test']}, false));
});
test('hide revision action', function(done) {
@@ -114,10 +112,10 @@
assert.throws(element.setActionHidden.bind(element, 'invalid type'));
element.setActionHidden(element.ActionType.REVISION,
element.RevisionActions.SUBMIT, true);
- assert.lengthOf(element._hiddenRevisionActions, 1);
+ assert.lengthOf(element._hiddenActions, 1);
element.setActionHidden(element.ActionType.REVISION,
element.RevisionActions.SUBMIT, true);
- assert.lengthOf(element._hiddenRevisionActions, 1);
+ assert.lengthOf(element._hiddenActions, 1);
flush(function() {
var buttonEl = element.$$('[data-action-key="submit"]');
assert.isOk(buttonEl);
@@ -135,29 +133,26 @@
});
});
- test('hide change action', function(done) {
+ test('hide menu action', function(done) {
flush(function() {
- var buttonEl = element.$$('[data-action-key="/"]');
+ var buttonEl = element.$.moreActions.$$('span[data-id="delete"]');
assert.isOk(buttonEl);
- assert.isFalse(buttonEl.hasAttribute('hidden'));
assert.throws(element.setActionHidden.bind(element, 'invalid type'));
element.setActionHidden(element.ActionType.CHANGE,
element.ChangeActions.DELETE, true);
- assert.lengthOf(element._hiddenChangeActions, 1);
+ assert.lengthOf(element._hiddenActions, 1);
element.setActionHidden(element.ActionType.CHANGE,
element.ChangeActions.DELETE, true);
- assert.lengthOf(element._hiddenChangeActions, 1);
+ assert.lengthOf(element._hiddenActions, 1);
flush(function() {
- var buttonEl = element.$$('[data-action-key="/"]');
- assert.isOk(buttonEl);
- assert.isTrue(buttonEl.hasAttribute('hidden'));
+ var buttonEl = element.$.moreActions.$$('span[data-id="delete"]');
+ assert.isNotOk(buttonEl);
element.setActionHidden(element.ActionType.CHANGE,
element.RevisionActions.DELETE, false);
flush(function() {
- var buttonEl = element.$$('[data-action-key="/"]');
+ var buttonEl = element.$.moreActions.$$('span[data-id="delete"]');
assert.isOk(buttonEl);
- assert.isFalse(buttonEl.hasAttribute('hidden'));
done();
});
});
@@ -169,7 +164,8 @@
flush(function() {
var buttonEls = Polymer.dom(element.root)
.querySelectorAll('gr-button');
- assert.equal(buttonEls.length, 6);
+ var menuItems = element.$.moreActions.items;
+ assert.equal(buttonEls.length + menuItems.length, 6);
assert.isFalse(element.hidden);
done();
});
@@ -177,18 +173,18 @@
test('delete buttons have explicit labels', function(done) {
flush(function() {
- var buttonEls =
- Polymer.dom(element.root).querySelectorAll('[data-action-key="/"]');
- assert.equal(buttonEls.length, 2);
- assert.notEqual(buttonEls[0].getAttribute('data-label'),
- buttonEls[1].getAttribute['data-label']);
+ var deleteItems = element.$.moreActions.items.filter(function(item) {
+ return item.id === 'delete';
+ });
+ assert.equal(deleteItems.length, 2);
+ assert.notEqual(deleteItems[0].name, deleteItems[1].name)
assert.isTrue(
- buttonEls[0].getAttribute('data-label') === 'Delete Revision' ||
- buttonEls[0].getAttribute('data-label') === 'Delete Change'
+ deleteItems[0].name === 'Delete Revision' ||
+ deleteItems[0].name === 'Delete Change'
);
assert.isTrue(
- buttonEls[1].getAttribute('data-label') === 'Delete Revision' ||
- buttonEls[1].getAttribute('data-label') === 'Delete Change'
+ deleteItems[1].name === 'Delete Revision' ||
+ deleteItems[1].name === 'Delete Change'
);
done();
});
@@ -205,6 +201,18 @@
assert.deepEqual(element._getRevision(change, '2'), revObj);
});
+ test('_actionComparator sort order', function() {
+ var actions = [
+ {label: '123', type: 'change', __key: 'review'},
+ {label: 'abc',type: 'revision'},
+ {label: 'abc',type: 'change'},
+ {label: 'def', type: 'change'},
+ {label: 'def', type: 'change',__primary: true},
+ ];
+ var result = actions.slice().sort(element._actionComparator);
+ assert.deepEqual(result, actions);
+ });
+
test('submit change', function(done) {
element.change = {
revisions: {
@@ -285,10 +293,7 @@
flushAsynchronousOperations();
assert.isFalse(element.$.confirmRebase.hidden);
- var cherryPickButton =
- element.$$('gr-button[data-action-key="cherrypick"]');
- assert.ok(cherryPickButton);
- MockInteractions.tap(cherryPickButton);
+ element._handleCherrypickTap();
flushAsynchronousOperations();
assert.isTrue(element.$.confirmRebase.hidden);
assert.isFalse(element.$.confirmCherrypick.hidden);
@@ -311,9 +316,7 @@
});
test('works', function() {
- var cherryPickButton =
- element.$$('gr-button[data-action-key="cherrypick"]');
- MockInteractions.tap(cherryPickButton);
+ element._handleCherrypickTap();
var action = {
__key: 'cherrypick',
__type: 'revision',
@@ -350,12 +353,10 @@
});
test('branch name cleared when re-open cherrypick', function() {
- var cherryPickButton =
- element.$$('gr-button[data-action-key="cherrypick"]');
var emptyBranchName = '';
element.$.confirmCherrypick.branch = 'master';
- MockInteractions.tap(cherryPickButton);
+ element._handleCherrypickTap();
assert.equal(element.$.confirmCherrypick.branch, emptyBranchName);
});
});
@@ -457,12 +458,6 @@
var fireActionStub;
var deleteAction;
- var tapDeleteAction = function() {
- var deleteButton = element.$$('gr-button[data-action-key=\'/\']');
- MockInteractions.tap(deleteButton);
- flushAsynchronousOperations();
- };
-
setup(function() {
fireActionStub = sinon.stub(element, '_fireAction');
element.change = {
@@ -484,12 +479,12 @@
});
test('does not delete on action', function() {
- tapDeleteAction();
+ element._handleDeleteTap();
assert.isFalse(fireActionStub.called);
});
test('shows confirm dialog', function() {
- tapDeleteAction();
+ element._handleDeleteTap();
assert.isFalse(element.$$('#confirmDeleteDialog').hidden);
MockInteractions.tap(
element.$$('#confirmDeleteDialog').$$('gr-button[primary]'));
@@ -498,7 +493,7 @@
});
test('hides delete confirm on cancel', function() {
- tapDeleteAction();
+ element._handleDeleteTap();
MockInteractions.tap(
element.$$('#confirmDeleteDialog').$$('gr-button:not([primary])'));
flushAsynchronousOperations();
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
index 1882500..f640dd6 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
@@ -49,11 +49,6 @@
gr-button[link] {
padding: 1em 0;
}
- gr-button:focus:not([primary]):not([secondary]),
- gr-button:hover:not([primary]):not([secondary]) {
- background-color: transparent;
- border-color: transparent;
- }
ul {
list-style: none;
}
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
index 93e676c..c02583a 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
@@ -49,6 +49,7 @@
setup(function() {
element = fixture('basic');
+ element.change = {};
var plugin;
Gerrit.install(function(p) { plugin = p; }, '0.1',
'http://test.com/plugins/testplugin/static/test.js');