Merge "Submit requirements - trigger vote hovercard"
diff --git a/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts b/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts
index d90d173..5feb1ae 100644
--- a/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts
+++ b/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts
@@ -16,7 +16,6 @@
*/
import '../../shared/gr-button/gr-button';
import '../../shared/gr-label-info/gr-label-info';
-import '../../shared/gr-limited-text/gr-limited-text';
import {customElement, property} from 'lit/decorators';
import {
AccountInfo,
@@ -37,7 +36,7 @@
const base = HovercardMixin(LitElement);
@customElement('gr-submit-requirement-hovercard')
-export class GrHovercardRun extends base {
+export class GrSubmitRequirementHovercard extends base {
@property({type: Object})
requirement?: SubmitRequirementResultInfo;
@@ -261,6 +260,6 @@
declare global {
interface HTMLElementTagNameMap {
- 'gr-submit-requirement-hovercard': GrHovercardRun;
+ 'gr-submit-requirement-hovercard': GrSubmitRequirementHovercard;
}
}
diff --git a/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts b/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
index 004d594..40d80bd 100644
--- a/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
+++ b/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
@@ -14,7 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import '../../shared/gr-label-info/gr-label-info';
import '../gr-submit-requirement-hovercard/gr-submit-requirement-hovercard';
+import '../gr-trigger-vote-hovercard/gr-trigger-vote-hovercard';
import {LitElement, css, html} from 'lit';
import {customElement, property, state} from 'lit/decorators';
import {ParsedChangeInfo} from '../../../types/types';
@@ -271,6 +273,9 @@
html`<gr-trigger-vote
.label="${label}"
.labelInfo="${labels[label]}"
+ .change="${this.change}"
+ .account="${this.account}"
+ .mutable="${this.mutable ?? false}"
></gr-trigger-vote>`
)}
</section>`;
@@ -285,6 +290,15 @@
@property({type: Object})
labelInfo?: LabelInfo;
+ @property({type: Object})
+ change?: ParsedChangeInfo;
+
+ @property({type: Object})
+ account?: AccountInfo;
+
+ @property({type: Boolean})
+ mutable?: boolean;
+
static override get styles() {
return css`
:host {
@@ -309,6 +323,10 @@
--gr-vote-chip-width: 14px;
--gr-vote-chip-height: 14px;
margin-right: 0px;
+ margin-left: var(--spacing-xs);
+ }
+ gr-vote-chip:first-of-type {
+ margin-left: 0px;
}
`;
}
@@ -317,6 +335,17 @@
if (!this.labelInfo) return;
return html`
<div class="container">
+ <gr-trigger-vote-hovercard .labelName=${this.label}>
+ <gr-label-info
+ slot="label-info"
+ .change=${this.change}
+ .account=${this.account}
+ .mutable=${this.mutable}
+ .label=${this.label}
+ .labelInfo=${this.labelInfo}
+ .showAllReviewers=${false}
+ ></gr-label-info>
+ </gr-trigger-vote-hovercard>
<span class="label">${this.label}</span>
${this.renderVotes()}
</div>
diff --git a/polygerrit-ui/app/elements/change/gr-trigger-vote-hovercard/gr-trigger-vote-hovercard.ts b/polygerrit-ui/app/elements/change/gr-trigger-vote-hovercard/gr-trigger-vote-hovercard.ts
new file mode 100644
index 0000000..552cc69
--- /dev/null
+++ b/polygerrit-ui/app/elements/change/gr-trigger-vote-hovercard/gr-trigger-vote-hovercard.ts
@@ -0,0 +1,94 @@
+/**
+ * @license
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {customElement, property} from 'lit/decorators';
+import {css, html, LitElement} from 'lit';
+import {HovercardMixin} from '../../../mixins/hovercard-mixin/hovercard-mixin';
+import {fontStyles} from '../../../styles/gr-font-styles';
+
+// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
+const base = HovercardMixin(LitElement);
+
+@customElement('gr-trigger-vote-hovercard')
+export class GrTriggerVoteHovercard extends base {
+ @property()
+ labelName?: string;
+
+ static override get styles() {
+ return [
+ fontStyles,
+ base.styles || [],
+ css`
+ #container {
+ min-width: 300px;
+ max-width: 300px;
+ padding: var(--spacing-xl) 0 var(--spacing-m) 0;
+ }
+ .row {
+ display: flex;
+ }
+ .title {
+ color: var(--deemphasized-text-color);
+ margin-right: var(--spacing-m);
+ }
+ div.section {
+ margin: 0 var(--spacing-xl) var(--spacing-m) var(--spacing-xl);
+ display: flex;
+ align-items: flex-start;
+ }
+ div.sectionIcon {
+ flex: 0 0 30px;
+ }
+ div.sectionIcon iron-icon {
+ position: relative;
+ width: 20px;
+ height: 20px;
+ }
+ `,
+ ];
+ }
+
+ override render() {
+ return html` <div id="container" role="tooltip" tabindex="-1">
+ <div class="section">
+ <div class="sectionContent">
+ <h3 class="name heading-3">
+ <span>${this.labelName}</span>
+ </h3>
+ </div>
+ </div>
+ <div class="section">
+ <div class="sectionIcon">
+ <iron-icon class="small" icon="gr-icons:info-outline"></iron-icon>
+ </div>
+ <div class="sectionContent">
+ <div class="row">
+ <div class="title">Status</div>
+ <div>
+ <slot name="label-info"></slot>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>`;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'gr-trigger-vote-hovercard': GrTriggerVoteHovercard;
+ }
+}
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
index f60b9b6..947ef3e 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
@@ -43,6 +43,7 @@
getApprovalInfo,
getVotingRangeOrDefault,
hasNeutralStatus,
+ hasVoted,
} from '../../../utils/label-util';
import {appContext} from '../../../services/app-context';
import {ParsedChangeInfo} from '../../../types/types';
@@ -86,9 +87,21 @@
@property({type: Object})
account?: AccountInfo;
+ /**
+ * A user is able to delete a vote iff the mutable property is true and the
+ * reviewer that left the vote exists in the list of removable_reviewers
+ * received from the backend.
+ */
@property({type: Boolean})
mutable = false;
+ /**
+ * if true - show all reviewers that can vote on label
+ * if false - show only reviewers that voted on label
+ */
+ @property({type: Boolean})
+ showAllReviewers = true;
+
private readonly restApiService = appContext.restApiService;
private readonly reporting = appContext.reportingService;
@@ -201,7 +214,9 @@
const labelInfo = this.labelInfo;
if (!labelInfo) return;
const reviewers = (this.change?.reviewers['REVIEWER'] ?? []).filter(
- reviewer => canVote(labelInfo, reviewer)
+ reviewer =>
+ (this.showAllReviewers && canVote(labelInfo, reviewer)) ||
+ (!this.showAllReviewers && hasVoted(labelInfo, reviewer))
);
return html`<div>
${reviewers.map(reviewer => this.renderReviewerVote(reviewer))}
diff --git a/polygerrit-ui/app/utils/label-util.ts b/polygerrit-ui/app/utils/label-util.ts
index 960b02f..a8a2719 100644
--- a/polygerrit-ui/app/utils/label-util.ts
+++ b/polygerrit-ui/app/utils/label-util.ts
@@ -92,8 +92,9 @@
export function hasNeutralStatus(
label: DetailedLabelInfo,
- approvalInfo: ApprovalInfo
+ approvalInfo?: ApprovalInfo
) {
+ if (!approvalInfo) return true;
return getLabelStatus(label, approvalInfo.value) === LabelStatus.NEUTRAL;
}
@@ -134,6 +135,15 @@
return label.all?.filter(x => x._account_id === account._account_id)[0];
}
+export function hasVoted(label: LabelInfo, account: AccountInfo) {
+ if (isDetailedLabelInfo(label)) {
+ return !hasNeutralStatus(label, getApprovalInfo(label, account));
+ } else if (isQuickLabelInfo(label)) {
+ return label.approved === account || label.rejected === account;
+ }
+ return false;
+}
+
export function canVote(label: DetailedLabelInfo, account: AccountInfo) {
const approvalInfo = getApprovalInfo(label, account);
if (!approvalInfo) return false;